├── .asdf-plugins ├── .circleci └── config.yml ├── .dockerignore ├── .eslintrc.json ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc.toml ├── .tool-versions ├── README.md ├── contracts ├── .gitignore ├── .prettierignore ├── Dockerfile ├── README.md ├── deploy │ ├── 090_fund.js │ ├── 100_keypers.js │ ├── 130_keyperconfig.js │ ├── 200_collator.js │ ├── 210_collatorconfig.js │ ├── 300_eonkeystorage.js │ ├── 400_batchcounter.js │ ├── 500_configure_keypers.js │ └── 515_configure_collator.js ├── hardhat.config.js ├── lib │ ├── configure-keypers.js │ └── fund.js ├── package-lock.json ├── package.json ├── scripts │ ├── abigen.js │ ├── add-spare-keyper-set.js │ ├── change-keypers.js │ └── configure-keypers.js ├── src │ ├── AddrsSeq.sol │ ├── BatchCounter.sol │ ├── CollatorConfigsList.sol │ ├── EonKeyBroadcast.sol │ ├── KeypersConfigsList.sol │ └── binding.sol └── test │ ├── addrsseq-test.js │ ├── eonkeystorage-test.js │ └── keypersconfigslist-test.js ├── docker ├── .env ├── 00-build.sh ├── 01-init-db.sh ├── 02-init-chain.sh ├── 03-run.sh ├── 04-bootstrap.sh ├── 05-test.sh ├── 10-change-keyperset.sh ├── 90-stop.sh ├── build-src │ ├── deploy_contracts │ │ └── Dockerfile │ ├── dummyserver │ │ └── Dockerfile │ ├── geth │ │ └── Dockerfile │ ├── metrics │ │ ├── Dockerfile │ │ └── targets.yaml │ ├── optimism │ │ └── Dockerfile │ └── rolling-shutter │ │ └── Dockerfile ├── common.sh ├── config.example │ ├── bootnode-0.toml │ ├── bootnode-1.toml │ ├── collator.toml │ ├── grafana │ │ └── provisioning │ │ │ └── datasources │ │ │ └── vm.yaml │ ├── keyper-0.toml │ ├── keyper-1.toml │ ├── keyper-2.toml │ ├── keyper-3.toml │ ├── node-deploy.json │ └── snapshot.toml ├── config │ ├── .gitignore │ └── grafana │ │ └── provisioning │ │ └── datasources │ │ └── vm.yaml └── docker-compose.yml ├── docs └── spec.md ├── js └── shutter-crypto │ ├── .eslintrc.js │ ├── Makefile │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── patches │ └── wasm_exec.patch │ ├── shutter_testdata.json │ ├── src │ ├── _node18_crypto_fallback.js │ └── index.js │ ├── test │ └── test.js │ └── webpack.config.js ├── play ├── .gitignore ├── Dockerfile ├── README.md ├── bb.edn ├── build.clj ├── deps.edn ├── dev │ └── user.clj ├── manual │ ├── README.md │ ├── ethereum.sh │ ├── initdb.sh │ ├── k.sh │ ├── keyper-0.toml │ ├── keyper-1.toml │ ├── keyper-2.toml │ ├── op-bootstrap-config.toml │ ├── p2p.sh │ ├── p2p_bootstrap.toml │ ├── rolling-shutter │ └── shuttermint.sh ├── process-compose.yaml ├── rsbb └── src │ └── sht │ ├── base64.clj │ ├── build.clj │ ├── collator_test.clj │ ├── core.clj │ ├── dkg_test.clj │ ├── play.clj │ ├── runner.clj │ └── toml_writer.clj ├── rolling-shutter ├── .gitignore ├── .golangci.yml ├── Makefile ├── README-dev.md ├── app │ ├── app.go │ ├── app_test.go │ ├── checktx.go │ ├── checktx_test.go │ ├── configvoting_test.go │ ├── dkg.go │ ├── dkg_test.go │ ├── messages.go │ ├── messages_test.go │ ├── noncetracker.go │ ├── noncetracker_test.go │ ├── powermap.go │ ├── powermap_test.go │ ├── types.go │ └── voting.go ├── bin │ ├── .gitignore │ └── rs ├── chainobserver │ ├── addrseq.go │ ├── db │ │ ├── collator │ │ │ ├── collator.sqlc.gen.go │ │ │ ├── db.sqlc.gen.go │ │ │ ├── definition.go │ │ │ ├── handler.go │ │ │ ├── models.sqlc.gen.go │ │ │ └── sql │ │ │ │ ├── queries │ │ │ │ └── collator.sql │ │ │ │ ├── schemas │ │ │ │ └── collator.sql │ │ │ │ └── sqlc.yaml │ │ ├── database.go │ │ ├── keyper │ │ │ ├── db.sqlc.gen.go │ │ │ ├── definition.go │ │ │ ├── extend.go │ │ │ ├── extend_test.go │ │ │ ├── handler.go │ │ │ ├── keyper.sqlc.gen.go │ │ │ ├── models.sqlc.gen.go │ │ │ └── sql │ │ │ │ ├── queries │ │ │ │ └── keyper.sql │ │ │ │ ├── schemas │ │ │ │ └── keyper.sql │ │ │ │ └── sqlc.yaml │ │ └── sync │ │ │ ├── db.sqlc.gen.go │ │ │ ├── definition.go │ │ │ ├── models.sqlc.gen.go │ │ │ ├── sql │ │ │ ├── queries │ │ │ │ └── sync.sql │ │ │ ├── schemas │ │ │ │ └── sync.sql │ │ │ └── sqlc.yaml │ │ │ └── sync.sqlc.gen.go │ ├── eventsync.go │ ├── handler.go │ └── observer.go ├── chaintesthelpers │ ├── chaintesthelpers.go │ └── chaintesthelpers_test.go ├── cmd │ ├── bootstrap │ │ └── bootstrap.go │ ├── chain │ │ ├── chain.go │ │ ├── init.go │ │ └── log.go │ ├── command.go │ ├── cryptocmd │ │ ├── cryptocmd.go │ │ └── jsontests.go │ ├── gnosisaccessnode │ │ └── gnosisaccessnode.go │ ├── gnosiskeyper │ │ └── gnosiskeyper.go │ ├── optimism │ │ ├── bootstrap.go │ │ ├── keyper.go │ │ └── keyper_test.go │ ├── p2pnode │ │ ├── p2pnode.go │ │ └── p2pnode_test.go │ ├── shutterservicekeyper │ │ └── shutterservicekeyper.go │ ├── shversion │ │ ├── race.go │ │ ├── race_enabled.go │ │ └── shversion.go │ ├── snapshot │ │ ├── snapshot.go │ │ └── snapshot_test.go │ └── snapshotkeyper │ │ └── snapshotkeyper.go ├── contract │ ├── binding.abigen.gen.go │ ├── deployment │ │ ├── deployment.go │ │ └── metrics.go │ ├── doc.go │ └── extend.go ├── docs │ ├── Makefile │ ├── docs.go │ ├── gendocs.go │ ├── rolling-shutter.md │ ├── rolling-shutter_bootstrap.md │ ├── rolling-shutter_chain.md │ ├── rolling-shutter_chain_init.md │ ├── rolling-shutter_crypto.md │ ├── rolling-shutter_crypto_aggregate.md │ ├── rolling-shutter_crypto_decrypt.md │ ├── rolling-shutter_crypto_encrypt.md │ ├── rolling-shutter_crypto_jsontests.md │ ├── rolling-shutter_crypto_testdata.md │ ├── rolling-shutter_crypto_verify-key.md │ ├── rolling-shutter_gnosis-access-node.md │ ├── rolling-shutter_gnosis-access-node_dump-config.md │ ├── rolling-shutter_gnosis-access-node_generate-config.md │ ├── rolling-shutter_gnosiskeyper.md │ ├── rolling-shutter_gnosiskeyper_dump-config.md │ ├── rolling-shutter_gnosiskeyper_generate-config.md │ ├── rolling-shutter_gnosiskeyper_initdb.md │ ├── rolling-shutter_gnosiskeyper_watch.md │ ├── rolling-shutter_op-bootstrap.md │ ├── rolling-shutter_op-bootstrap_fetch-keyperset.md │ ├── rolling-shutter_op-bootstrap_generate-config.md │ ├── rolling-shutter_op-keyper.md │ ├── rolling-shutter_op-keyper_generate-config.md │ ├── rolling-shutter_op-keyper_initdb.md │ ├── rolling-shutter_p2pnode.md │ ├── rolling-shutter_p2pnode_dump-config.md │ ├── rolling-shutter_p2pnode_generate-config.md │ ├── rolling-shutter_shutterservicekeyper.md │ ├── rolling-shutter_shutterservicekeyper_dump-config.md │ ├── rolling-shutter_shutterservicekeyper_generate-config.md │ ├── rolling-shutter_shutterservicekeyper_initdb.md │ ├── rolling-shutter_snapshot.md │ ├── rolling-shutter_snapshot_dump-config.md │ ├── rolling-shutter_snapshot_generate-config.md │ ├── rolling-shutter_snapshot_initdb.md │ ├── rolling-shutter_snapshotkeyper.md │ ├── rolling-shutter_snapshotkeyper_dump-config.md │ ├── rolling-shutter_snapshotkeyper_generate-config.md │ └── rolling-shutter_snapshotkeyper_initdb.md ├── eonkeypublisher │ └── eonkeypublisher.go ├── gnosisaccessnode │ ├── config.go │ ├── decryptionkeyshandler.go │ ├── node.go │ └── storage.go ├── gnosiskeyperwatcher │ ├── blocks.go │ ├── keys.go │ └── watcher.go ├── go.mod ├── go.sum ├── keyper │ ├── database │ │ ├── db.sqlc.gen.go │ │ ├── definition.go │ │ ├── extend.go │ │ ├── keyper.sqlc.gen.go │ │ ├── models.sqlc.gen.go │ │ └── sql │ │ │ ├── queries │ │ │ └── keyper.sql │ │ │ ├── schemas │ │ │ └── keyper.sql │ │ │ └── sqlc.yaml │ ├── dkgphase │ │ └── phase.go │ ├── eonpkhandler.go │ ├── epochkg │ │ ├── epochkg.go │ │ └── epochkg_test.go │ ├── epochkghandler │ │ ├── benchmark_test.go │ │ ├── eonpublickey.go │ │ ├── key.go │ │ ├── key_test.go │ │ ├── keyshare.go │ │ ├── keyshare_test.go │ │ ├── metrics.go │ │ ├── sendkeyshare.go │ │ ├── service.go │ │ ├── setup_test.go │ │ └── trigger_test.go │ ├── fx │ │ ├── messagesender.go │ │ └── send.go │ ├── keyper.go │ ├── keypermetrics │ │ └── metrics.go │ ├── kprapi │ │ ├── http.go │ │ └── kprapi.go │ ├── kprconfig │ │ └── config.go │ ├── kproapi │ │ ├── doc.go │ │ ├── middleware.go │ │ ├── middleware_test.go │ │ ├── oapi.gen.go │ │ └── oapi.yaml │ ├── kprtopics │ │ └── kprtopics.go │ ├── options.go │ ├── shutterevents │ │ ├── batchconfig.go │ │ ├── events.go │ │ ├── events_test.go │ │ ├── evtype │ │ │ └── evtype.go │ │ ├── helpers.go │ │ ├── marshal.go │ │ └── shtxresp │ │ │ └── code.go │ └── smobserver │ │ ├── smdriver.go │ │ └── smstate.go ├── keyperimpl │ ├── gnosis │ │ ├── config.go │ │ ├── database │ │ │ ├── db.sqlc.gen.go │ │ │ ├── definition.go │ │ │ ├── gnosiskeyper.sqlc.gen.go │ │ │ ├── models.sqlc.gen.go │ │ │ └── sql │ │ │ │ ├── queries │ │ │ │ └── gnosiskeyper.sql │ │ │ │ ├── schemas │ │ │ │ └── gnosiskeyper.sql │ │ │ │ └── sqlc.yaml │ │ ├── gnosisssztypes │ │ │ ├── slotdecryptionsignatures.go │ │ │ └── slotdecryptionsignatures_encoding.go │ │ ├── handlers.go │ │ ├── identitieshash.go │ │ ├── keyper.go │ │ ├── messagingmiddleware.go │ │ ├── metrics.go │ │ ├── newblock.go │ │ ├── neweonpublickey.go │ │ ├── newkeyperset.go │ │ ├── newslot.go │ │ ├── newslot_test.go │ │ ├── sequencersyncer.go │ │ ├── syncmonitor.go │ │ ├── syncmonitor_test.go │ │ ├── validatorsyncer.go │ │ ├── validatorsyncer_integration_test.go │ │ └── validatorsyncer_test.go │ ├── optimism │ │ ├── bootstrap │ │ │ ├── bootstrap.go │ │ │ ├── config.go │ │ │ └── keyperset.go │ │ ├── config │ │ │ ├── config.go │ │ │ └── ethereum.go │ │ ├── database │ │ │ └── definition.go │ │ ├── keyper.go │ │ ├── logger.go │ │ └── setup_test.go │ ├── shutterservice │ │ ├── config.go │ │ ├── database │ │ │ ├── db.sqlc.gen.go │ │ │ ├── definition.go │ │ │ ├── models.sqlc.gen.go │ │ │ ├── shutterservice.sqlc.gen.go │ │ │ └── sql │ │ │ │ ├── queries │ │ │ │ └── shutterservice.sql │ │ │ │ ├── schemas │ │ │ │ └── shutterservice.sql │ │ │ │ └── sqlc.yaml │ │ ├── handlers.go │ │ ├── handlers_test.go │ │ ├── identitieshash.go │ │ ├── keyper.go │ │ ├── messagingmiddleware.go │ │ ├── metrics.go │ │ ├── newblock.go │ │ ├── newblock_test.go │ │ ├── neweonpublickey.go │ │ ├── newkeyperset.go │ │ ├── registrysyncer.go │ │ ├── registrysyncer_test.go │ │ ├── serviceztypes │ │ │ ├── decryptionsignatures.go │ │ │ └── decryptionsignatures_encoding.go │ │ ├── setup_test.go │ │ ├── syncmonitor.go │ │ └── syncmonitor_test.go │ └── snapshot │ │ ├── config.go │ │ ├── database │ │ └── definition.go │ │ ├── keyper.go │ │ └── trigger.go ├── main.go ├── medley │ ├── beaconapiclient │ │ ├── beaconapiclient.go │ │ ├── getproposerduties.go │ │ └── getvalidator.go │ ├── broker.go │ ├── broker │ │ └── event.go │ ├── chainsync │ │ ├── client.go │ │ ├── client │ │ │ └── client.go │ │ ├── event │ │ │ ├── events.go │ │ │ └── handler.go │ │ ├── options.go │ │ └── syncer │ │ │ ├── eonpubkey.go │ │ │ ├── keyperset.go │ │ │ ├── shutterstate.go │ │ │ ├── unsafehead.go │ │ │ └── util.go │ ├── channel │ │ ├── channel.go │ │ ├── fanin.go │ │ ├── fanin_test.go │ │ └── fanout.go │ ├── comparer │ │ └── comparer.go │ ├── configuration │ │ ├── command │ │ │ ├── command.go │ │ │ ├── options.go │ │ │ └── parse.go │ │ ├── config.go │ │ ├── ethereum.go │ │ ├── test │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ └── helper.go │ │ └── traverse.go │ ├── db │ │ ├── connect.go │ │ ├── db.sqlc.gen.go │ │ ├── definition.go │ │ ├── definitions.go │ │ ├── meta.sqlc.gen.go │ │ ├── metadb.go │ │ ├── models.sqlc.gen.go │ │ ├── sql │ │ │ ├── queries │ │ │ │ └── meta.sql │ │ │ ├── schemas │ │ │ │ └── meta.sql │ │ │ └── sqlc.yaml │ │ ├── sqlc.go │ │ └── transaction.go │ ├── decodehooks.go │ ├── encodeable │ │ ├── address │ │ │ └── libp2p.go │ │ ├── encodeable.go │ │ ├── env │ │ │ ├── environment.go │ │ │ └── environment_enum.go │ │ ├── hex │ │ │ └── bytes.go │ │ ├── keys │ │ │ ├── ecdsa.go │ │ │ ├── ed25519.go │ │ │ ├── keys.go │ │ │ └── libp2p.go │ │ ├── number │ │ │ └── block.go │ │ ├── time │ │ │ └── duration.go │ │ └── url │ │ │ └── url.go │ ├── eventsyncer │ │ └── eventsyncer.go │ ├── identitypreimage │ │ └── identitypreimage.go │ ├── introspection │ │ └── introspection.go │ ├── logger │ │ └── noop.go │ ├── medley.go │ ├── metricsserver │ │ ├── config.go │ │ └── metricsserver.go │ ├── retry │ │ ├── options.go │ │ ├── retry.go │ │ └── retry_test.go │ ├── rootcmd │ │ └── root.go │ ├── service │ │ └── service.go │ ├── slots.go │ ├── slotticker │ │ ├── slotticker.go │ │ └── slotticker_test.go │ ├── spit.go │ ├── syncranges.go │ ├── syncranges_test.go │ ├── testkeygen │ │ ├── eonkeys.go │ │ └── keygen_test.go │ ├── testlog │ │ └── testlog.go │ ├── testsetup │ │ ├── database.go │ │ └── eon.go │ └── validatorregistry │ │ ├── signature.go │ │ ├── signature_test.go │ │ ├── validatorregistry.go │ │ └── validatorregistry_test.go ├── p2p │ ├── bootstrap.go │ ├── config.go │ ├── dht.go │ ├── dht_test.go │ ├── floodsubpeerdiscovery │ │ ├── gossippeerdiscovery.go │ │ ├── peer.pb.go │ │ └── peer.proto │ ├── message.go │ ├── messaging.go │ ├── metrics.go │ ├── multiaddr.go │ ├── p2p.go │ ├── p2p_test.go │ ├── p2ptest │ │ ├── p2ptest.go │ │ └── sender.go │ ├── params.go │ ├── topic.go │ ├── trace.go │ └── trace_test.go ├── p2pmsg │ ├── decryptiontrigger.go │ ├── doc.go │ ├── eonpublickey.go │ ├── gossip.pb.go │ ├── gossip.proto │ ├── messages.go │ ├── messages_test.go │ └── signing.go ├── p2pnode │ ├── config.go │ └── host.go ├── sandbox │ ├── ganache.go │ ├── keygen │ │ └── keygen.go │ ├── signing_test.go │ └── testclient │ │ └── testclient.go ├── shcryptowasm │ └── shutter_crypto_wasm.go ├── shdb │ ├── db.go │ └── notify.go ├── shmsg │ ├── doc.go │ ├── encode.go │ ├── encode_test.go │ ├── messages.go │ ├── messages_test.go │ ├── shmsg.pb.go │ └── shmsg.proto ├── snapshot │ ├── config.go │ ├── database │ │ ├── db.sqlc.gen.go │ │ ├── definition.go │ │ ├── models.sqlc.gen.go │ │ ├── snapshot.sqlc.gen.go │ │ └── sql │ │ │ ├── queries │ │ │ └── snapshot.sql │ │ │ ├── schemas │ │ │ └── snapshot.sql │ │ │ └── sqlc.yaml │ ├── handler.go │ ├── hubapi │ │ └── hubapi.go │ ├── metrics.go │ ├── snapshot.go │ └── snpjrpc │ │ └── server.go └── trace │ ├── noop.go │ ├── test.go │ ├── trace.go │ └── trace_test.go ├── testdata ├── shlib_v0.1.19_testdata.json ├── signedRegistrations_0x01.json └── validatorInfo_0x01.json └── tools ├── asdf-install-plugins.sh ├── shutter-gob-printer └── main.go └── snapshot └── dummyserver ├── README.md ├── dummyserver ├── __init__.py └── main.py ├── poetry.lock └── pyproject.toml /.asdf-plugins: -------------------------------------------------------------------------------- 1 | # ASDF plugins used in the project 2 | # 3 | # Tools without urls are available in the default asdf plugin repo 4 | # To automatically install these run `make install-asdf-plugins` inside 5 | # the 'rolling-shutter' directory. 6 | 7 | circleci https://github.com/ulope/asdf-circleci.git 8 | direnv 9 | golang 10 | golangci-lint 11 | nodejs 12 | postgres 13 | pre-commit 14 | protoc 15 | python 16 | shfmt 17 | solidity 18 | sqlc https://github.com/ulope/asdf-sqlc.git 19 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | contracts/combined.json 2 | contracts/node_modules 3 | contracts/deployments 4 | docker/data* 5 | play/work 6 | rolling-shutter/bin/rolling-shutter 7 | rolling-shutter/bin/shutter-crypto.wasm 8 | tools/snapshot/dummyserver/dist 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es2021": true, 6 | "mocha": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": 13 11 | }, 12 | "rules": {} 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo 2 | .devenv 3 | .direnv 4 | .lsp 5 | .vscode 6 | docker/data* 7 | log 8 | node_modules 9 | result 10 | temp 11 | work 12 | .env 13 | .idea 14 | .DS_Store -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /contracts/artifacts/ 3 | /contracts/combined.json 4 | /contracts/cache/ 5 | /rolling-shutter/docs/ 6 | -------------------------------------------------------------------------------- /.prettierrc.toml: -------------------------------------------------------------------------------- 1 | proseWrap = "preserve" 2 | 3 | [[overrides]] 4 | files = ["**/*.txt", "**/*.md"] 5 | 6 | [overrides.options] 7 | proseWrap = "always" 8 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | circleci 0.1.31425 2 | golang 1.23.6 3 | golangci-lint 1.64.5 4 | nodejs 18.17.0 5 | postgres 14.2 6 | pre-commit 4.1.0 7 | protoc 29.3 8 | shfmt 3.10.0 9 | solidity 0.8.9 10 | sqlc 1.28.0 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rolling Shutter 2 | 3 | Rolling Shutter is an MEV protection system for rollups. It prevents 4 | frontrunning and improves censorship resistance using a threshold encryption 5 | mechanism. The system can be integrated into any rollup with only slight 6 | modifications to its protocol. 7 | 8 | ## Further Reading 9 | 10 | For a high-level introduction, check out the 11 | [announcement blog post](https://blog.shutter.network/announcing-rolling-shutter/). 12 | Also, have a look at the 13 | [Rolling Shutter FAQ](https://blog.shutter.network/rolling-shutter-faq/). 14 | -------------------------------------------------------------------------------- /contracts/.gitignore: -------------------------------------------------------------------------------- 1 | combined.json 2 | node_modules 3 | 4 | #Hardhat files 5 | cache 6 | artifacts 7 | cache-ovm 8 | artifacts-ovm 9 | deployments 10 | -------------------------------------------------------------------------------- /contracts/.prettierignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /artifacts/ 3 | /combined.json 4 | /cache/ 5 | -------------------------------------------------------------------------------- /contracts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.17.1 as builder 2 | WORKDIR /contracts 3 | COPY /contracts/package.json /contracts/package-lock.json /contracts/ 4 | RUN npm install 5 | RUN mkdir -p /contracts/deployments 6 | 7 | FROM gcr.io/distroless/nodejs18-debian11 8 | VOLUME /contracts/deployments 9 | COPY /contracts /contracts 10 | COPY --from=builder /contracts /contracts 11 | WORKDIR /contracts 12 | RUN ["/nodejs/bin/node", "node_modules/.bin/hardhat", "compile"] 13 | ENTRYPOINT ["/nodejs/bin/node", "node_modules/.bin/hardhat"] 14 | -------------------------------------------------------------------------------- /contracts/README.md: -------------------------------------------------------------------------------- 1 | This directory contains rolling shutter's smart contracts. It uses hardhat for 2 | testing and deploying contracts. 3 | 4 | Run 5 | 6 | ``` 7 | npx hardhat test 8 | ``` 9 | 10 | to run the tests on normal Ethereum. 11 | 12 | Run 13 | 14 | ``` 15 | npx hardhat --network optimistic test 16 | ``` 17 | 18 | to test on Optimistic Ethereum. This assumes you are running a local Optimistic 19 | Ethereum network. 20 | -------------------------------------------------------------------------------- /contracts/deploy/090_fund.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | const { fund } = require("../lib/fund.js"); 3 | 4 | module.exports = async function (hre) { 5 | const fundValue = hre.deployConf.fundValue; 6 | const { bank, deployer } = await hre.getNamedAccounts(); 7 | const bankSigner = await ethers.getSigner(bank); 8 | 9 | let addresses = []; 10 | if (deployer !== bank) { 11 | addresses.push(deployer); 12 | } 13 | addresses.push(...(await hre.getKeyperAddresses(0))); 14 | addresses.push(await hre.getCollatorAddress()); 15 | await fund(addresses, bankSigner, fundValue); 16 | }; 17 | -------------------------------------------------------------------------------- /contracts/deploy/100_keypers.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | module.exports = async function (hre) { 4 | const { deployments, getNamedAccounts } = hre; 5 | const { deployer } = await getNamedAccounts(); 6 | const deployResult = await deployments.deploy("Keypers", { 7 | contract: "AddrsSeq", 8 | from: deployer, 9 | args: [], 10 | log: true, 11 | }); 12 | if (deployResult.newlyDeployed) { 13 | const c = await ethers.getContract("Keypers"); 14 | const tx = await c.append(); 15 | await tx.wait(); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /contracts/deploy/130_keyperconfig.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | module.exports = async function (hre) { 4 | const { deployments, getNamedAccounts } = hre; 5 | const { deployer } = await getNamedAccounts(); 6 | var keypers = await ethers.getContract("Keypers"); 7 | await deployments.deploy("KeyperConfig", { 8 | contract: "KeypersConfigsList", 9 | from: deployer, 10 | args: [keypers.address], 11 | log: true, 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /contracts/deploy/200_collator.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | module.exports = async function (hre) { 4 | const { deployments, getNamedAccounts } = hre; 5 | const { deployer } = await getNamedAccounts(); 6 | const deployResult = await deployments.deploy("Collator", { 7 | contract: "AddrsSeq", 8 | from: deployer, 9 | args: [], 10 | log: true, 11 | }); 12 | if (deployResult.newlyDeployed) { 13 | const c = await ethers.getContract("Collator"); 14 | const tx = await c.append(); 15 | await tx.wait(); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /contracts/deploy/210_collatorconfig.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | module.exports = async function (hre) { 4 | const { deployments, getNamedAccounts } = hre; 5 | const { deployer } = await getNamedAccounts(); 6 | var collator = await ethers.getContract("Collator"); 7 | await deployments.deploy("CollatorConfig", { 8 | contract: "CollatorConfigsList", 9 | from: deployer, 10 | args: [collator.address], 11 | log: true, 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /contracts/deploy/300_eonkeystorage.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (hre) { 2 | const { deployments, getNamedAccounts } = hre; 3 | const { deployer } = await getNamedAccounts(); 4 | await deployments.deploy("EonKeyStorage", { 5 | contract: "EonKeyStorage", 6 | from: deployer, 7 | args: [], 8 | log: true, 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /contracts/deploy/400_batchcounter.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (hre) { 2 | const { deployments, getNamedAccounts } = hre; 3 | const { deployer } = await getNamedAccounts(); 4 | await deployments.deploy("BatchCounter", { 5 | contract: "BatchCounter", 6 | from: deployer, 7 | args: [], 8 | log: true, 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /contracts/deploy/500_configure_keypers.js: -------------------------------------------------------------------------------- 1 | const { configure_keypers } = require("../lib/configure-keypers.js"); 2 | 3 | module.exports = async function (hre) { 4 | var keyperAddrs = await hre.getKeyperAddresses(); 5 | await configure_keypers( 6 | keyperAddrs, 7 | hre.deployConf.activationBlockOffset, 8 | hre.deployConf.thresholdRatio 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /contracts/lib/fund.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | fund: fund, 3 | }; 4 | 5 | const { ethers } = require("hardhat"); 6 | const { BigNumber } = require("ethers"); 7 | 8 | async function fund(addresses, bankSigner, fundValue = "1000") { 9 | if (fundValue == "") { 10 | // TODO return error? 11 | console.log("fund: not doing any funding"); 12 | return; 13 | } 14 | 15 | // TODO errorhandling? 16 | const value = ethers.utils.parseEther(fundValue); 17 | console.log( 18 | "fund: funding %s adresses with %s eth", 19 | addresses.length, 20 | fundValue 21 | ); 22 | 23 | const txs = []; 24 | for (const a of addresses) { 25 | const balance = await ethers.provider.getBalance(a); 26 | const weiFund = ethers.utils.parseEther(fundValue); 27 | if (balance.gt(BigNumber.from(weiFund))) { 28 | console.log(a + " already funded"); 29 | continue; 30 | } 31 | const tx = await bankSigner.sendTransaction({ 32 | to: a, 33 | value: value, 34 | }); 35 | txs.push(tx); 36 | } 37 | const txPromises = []; 38 | for (const tx of txs) { 39 | txPromises.push(tx.wait()); 40 | } 41 | return Promise.all(txPromises); 42 | } 43 | -------------------------------------------------------------------------------- /contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "license": "MIT", 4 | "engines": { 5 | "node": ">=16.0.0", 6 | "npm": "^9.6.0" 7 | }, 8 | "scripts": { 9 | "eslint": "eslint .", 10 | "prettier": "prettier -w .", 11 | "test": "hardhat test" 12 | }, 13 | "devDependencies": { 14 | "@nomicfoundation/hardhat-verify": "^1.1.1", 15 | "@nomiclabs/hardhat-ethers": "^2.0.2", 16 | "@nomiclabs/hardhat-ganache": "^2.0.1", 17 | "@nomiclabs/hardhat-waffle": "^2.0.1", 18 | "chai": "^4.3.4", 19 | "eslint": "^8.4.1", 20 | "ethereum-waffle": "^3.4.0", 21 | "ethers": "^5.5.1", 22 | "hardhat": "^2.14.0", 23 | "hardhat-deploy": "^0.9.5", 24 | "hardhat-deploy-ethers": "^0.3.0-beta.11", 25 | "prettier": "^2.8.8", 26 | "prettier-plugin-solidity": "^1.1.3" 27 | }, 28 | "dependencies": { 29 | "@eth-optimism/hardhat-ovm": "^0.2.2", 30 | "@openzeppelin/contracts": "^4.7.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/scripts/add-spare-keyper-set.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | This script adds a spare unused keyper set to the AddrSeq contract holding the keyper sets. 5 | see https://github.com/shutter-network/rolling-shutter/issues/240 6 | */ 7 | const process = require("process"); 8 | const path = require("path"); 9 | const { ethers } = require("hardhat"); 10 | 11 | // const { inspect } = require("util"); 12 | 13 | /* global __dirname */ 14 | process.chdir(path.dirname(__dirname)); // allow calling the script from anywhere 15 | 16 | async function main() { 17 | const keyperAddrs = [ 18 | "0xb7f85e85201fd635f139bc0f1d1133c1bb0a1800", 19 | "0xb7f85e85201fd635f139bc0f1d1133c1bb0a1801", 20 | "0xb7f85e85201fd635f139bc0f1d1133c1bb0a1802", 21 | "0xb7f85e85201fd635f139bc0f1d1133c1bb0a1803", 22 | ]; 23 | const keypers = await ethers.getContract("Keypers"); 24 | await keypers.add(keyperAddrs); 25 | await keypers.append(); 26 | } 27 | 28 | main() 29 | .then(() => process.exit(0)) 30 | .catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /contracts/scripts/configure-keypers.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | This script configures a new set of keypers 4 | */ 5 | const fs = require("fs"); 6 | const process = require("process"); 7 | const path = require("path"); 8 | const { configure_keypers } = require("../lib/configure-keypers.js"); 9 | 10 | // const { inspect } = require("util"); 11 | 12 | /* global __dirname */ 13 | process.chdir(path.dirname(__dirname)); // allow calling the script from anywhere 14 | 15 | async function main() { 16 | const deployConf = JSON.parse(fs.readFileSync(process.env.DEPLOY_CONF)); 17 | await configure_keypers(deployConf.keypers); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch((error) => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /contracts/src/BatchCounter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity =0.8.9; 4 | 5 | contract BatchCounter { 6 | error CallerNotZeroAddress(); 7 | 8 | event NewBatchIndex(uint64 oldIndex, uint64 newIndex); 9 | uint64 public batchIndex; 10 | 11 | function increment() external { 12 | if (msg.sender != address(0)) { 13 | revert CallerNotZeroAddress(); 14 | } 15 | emit NewBatchIndex(batchIndex, batchIndex + 1); 16 | batchIndex += 1; 17 | } 18 | 19 | /// @notice intended for initialization 20 | function set(uint64 newBatchIndex) external { 21 | if (msg.sender != address(0)) { 22 | revert CallerNotZeroAddress(); 23 | } 24 | emit NewBatchIndex(batchIndex, newBatchIndex); 25 | batchIndex = newBatchIndex; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/src/binding.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // This file is the sole input file we use when running abigen to generate the bindings 4 | pragma solidity =0.8.9; 5 | 6 | import "./AddrsSeq.sol"; 7 | import "./KeypersConfigsList.sol"; 8 | import "./CollatorConfigsList.sol"; 9 | import "./BatchCounter.sol"; 10 | import "./EonKeyBroadcast.sol"; 11 | -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=snapshutter 2 | -------------------------------------------------------------------------------- /docker/00-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./common.sh 4 | 5 | $DC --profile dev build 6 | -------------------------------------------------------------------------------- /docker/01-init-db.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./common.sh 4 | 5 | mkdb() { 6 | $DC exec -T db createdb -U postgres $1 7 | $DC run -T --rm --no-deps $1 initdb --config /config/${1}.toml 8 | } 9 | 10 | $DC stop db 11 | $DC rm -f db 12 | 13 | ${BB} rm -rf data/db 14 | 15 | $DC up -d db 16 | $DC run --rm --no-deps dockerize -wait tcp://db:5432 -timeout 40s 17 | 18 | for cmd in snapshot keyper-0 keyper-1 keyper-2 keyper-3; do 19 | mkdb $cmd & 20 | done 21 | 22 | wait 23 | 24 | $DC stop db 25 | -------------------------------------------------------------------------------- /docker/03-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./common.sh 4 | 5 | echo "Starting entire system" 6 | $DC --profile dev up -d 7 | -------------------------------------------------------------------------------- /docker/04-bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./common.sh 4 | 5 | echo "Submitting bootstrap transaction" 6 | 7 | $DC run --rm --no-deps --entrypoint /rolling-shutter chain-0-validator bootstrap \ 8 | --deployment-dir /deployments/dockerGeth \ 9 | --ethereum-url http://geth:8545 \ 10 | --shuttermint-url http://chain-0-sentry:${TM_RPC_PORT} \ 11 | --signing-key 479968ffa5ee4c84514a477a8f15f3db0413964fd4c20b08a55fed9fed790fad 12 | -------------------------------------------------------------------------------- /docker/05-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./common.sh 4 | set +ex 5 | 6 | echo "Testing decryption key generation" 7 | EPOCH_ID=$(LC_ALL=C tr -dc 'a-f0-9' /dev/null 2>&1; then 6 | DC="docker compose" 7 | else 8 | DC=docker-compose 9 | fi 10 | 11 | set -xe 12 | -------------------------------------------------------------------------------- /docker/config.example/bootnode-0.toml: -------------------------------------------------------------------------------- 1 | # Peer identity: /p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ 2 | # Peer role: bootstrap 3 | 4 | # whether to register handlers on the messages and log them 5 | InstanceID = 0 6 | ListenMessages = true 7 | 8 | [P2P] 9 | P2PKey = "CAESQKFtiMAqd2c8bQ/mfPStxViY970MNtWUVWdn44rUoQXAfv7ztSQ9nLeqliXrkuqKi3XUObyAfH+Py3eMbHFvIpM=" 10 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 11 | # Overwrite p2p boostrap nodes 12 | CustomBootstrapAddresses = [ 13 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 14 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 15 | ] 16 | -------------------------------------------------------------------------------- /docker/config.example/bootnode-1.toml: -------------------------------------------------------------------------------- 1 | # Peer identity: /p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo 2 | # Peer role: bootstrap 3 | 4 | # whether to register handlers on the messages and log them 5 | InstanceID = 0 6 | ListenMessages = true 7 | 8 | [P2P] 9 | P2PKey = "CAESQCywBzANmzk2gFd63qkQ8+PlZzy/IqK9Wuv5d6FB1cYh+SggYvQ9pBD5FPLqTbc7garDqgQUJz/6Gpi6Ssbcx44=" 10 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 11 | # Overwrite p2p boostrap nodes 12 | CustomBootstrapAddresses = [ 13 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 14 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 15 | ] 16 | -------------------------------------------------------------------------------- /docker/config.example/collator.toml: -------------------------------------------------------------------------------- 1 | # Note: for snapshot shutter, we don't need to run a collator! 2 | 3 | # Peer identity: /p2p/12D3KooWFsVXBunDMNQZ8LLvS7BpY85Bop8isZaFAq6NVL7nLzUj 4 | # Ethereum address: 0x2E135FE171fB6351026B75aF688a7F9689B66B87 5 | 6 | 7 | InstanceID = 0 8 | DatabaseURL = "postgres://postgres@db:5432/collator" 9 | HTTPListenAddress = ':3000' 10 | SequencerURL = "http://geth:8545/" 11 | EpochDuration = '1s' 12 | ExecutionBlockDelay = 5 13 | BatchIndexAcceptenceInterval = 5 14 | 15 | [P2P] 16 | P2PKey = "CAESQDTb6IrZcY37WUI2Ld42H4ixyA7S/7ttXBUt0J9EwuPGWfMtbuNarlScRbqIz6Dm8jZqZWkpECDx8r8PwXB97ZQ=" 17 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 18 | # Overwrite p2p boostrap nodes 19 | CustomBootstrapAddresses = [ 20 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 21 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 22 | ] 23 | 24 | [Ethereum] 25 | 26 | PrivateKey = "215ddd19b91c54252bb956c7f88e42122a61bd45a562d5bae38bc2328a9b52d0" 27 | # The JSON RPC endpoint where the contracts are accessible 28 | ContractsURL = "http://geth:8545/" 29 | # Contract source directory 30 | DeploymentDir = "./deployments/dockerGeth/" 31 | # The layer 1 JSON RPC endpoint 32 | EthereumURL = "http://geth:8545/" 33 | -------------------------------------------------------------------------------- /docker/config.example/grafana/provisioning/datasources/vm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: VictoriaMetrics 5 | type: victoriametrics-datasource 6 | access: proxy 7 | url: http://metrics:8428 8 | isDefault: true 9 | -------------------------------------------------------------------------------- /docker/config.example/keyper-0.toml: -------------------------------------------------------------------------------- 1 | 2 | # Peer identity: /p2p/12D3KooWQ8iKQe4iEfkTh3gdBtpxWsKwx3BwrA18km5rq3Zwt2QF 3 | # Ethereum address: 0xCDD50A6F9B1439dc14c4f2A7eaF14dA1EF5A476c 4 | 5 | 6 | InstanceID = 0 7 | # If it's empty, we use the standard PG_ environment variables 8 | DatabaseURL = "postgres://postgres@db:5432/keyper-0" 9 | HTTPEnabled = false 10 | HTTPListenAddress = ':3000' 11 | 12 | [P2P] 13 | P2PKey = 'CAESQJ3NdZ6mRrDAW/Z59OKwcKdOCbZQ45z5o8K+tLHOL8Xw1LbawPZLk3mXNyiDyADcLk1bqYMe3uQ6T8xi65zkM0A=' 14 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 15 | # Overwrite p2p boostrap nodes 16 | CustomBootstrapAddresses = [ 17 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 18 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 19 | ] 20 | 21 | [Ethereum] 22 | PrivateKey = '82904d1c48d3a27d218408fc2db3e743f554a69b05b91d28c2897a9026ea47df' 23 | # The JSON RPC endpoint where the contracts are accessible 24 | ContractsURL = 'http://127.0.0.1:8555/' 25 | # Contract source directory 26 | DeploymentDir = "./deployments/dockerGeth/" 27 | # The layer 1 JSON RPC endpoint 28 | EthereumURL = "http://geth:8545/" 29 | 30 | [Shuttermint] 31 | ShuttermintURL = "http://chain-0-sentry:26657" 32 | ValidatorPublicKey = '720accc33c2bce1319d12b5f23f81b39a33f487e21c4a4d7ca1e1f7954a24172' 33 | EncryptionKey = 'e5c124eb4d3c13fa46532107b69b5abfa11e8aaf3a7a86d11ef370470834e076' 34 | DKGPhaseLength = 8 35 | DKGStartBlockDelta = 200 36 | 37 | [Metrics] 38 | Enabled = true 39 | Host = "[::]" 40 | Port = 9100 41 | -------------------------------------------------------------------------------- /docker/config.example/keyper-1.toml: -------------------------------------------------------------------------------- 1 | 2 | # Peer identity: /p2p/12D3KooWGksg5G2oau6EgdJFLiQoKaMvzPJnTCuoPScZcmZRdkny 3 | # Ethereum address: 0x539cF80D345d26525A47dB80De0fAb147D588fDa 4 | 5 | 6 | InstanceID = 0 7 | # If it's empty, we use the standard PG_ environment variables 8 | DatabaseURL = "postgres://postgres@db:5432/keyper-1" 9 | HTTPEnabled = false 10 | HTTPListenAddress = ':3000' 11 | 12 | [P2P] 13 | P2PKey = 'CAESQO+t2CR93jdMq/FDqZf2+KIY9kLhKA1rZY9WFherSqvZZxzY8W4y5hSBrW5u79SDCvbLcmo7kEwu6VsK0NjZnxY=' 14 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 15 | # Overwrite p2p boostrap nodes 16 | CustomBootstrapAddresses = [ 17 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 18 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 19 | ] 20 | 21 | [Ethereum] 22 | PrivateKey = '939babbad75cbcc42eef92496ce86ede989ba96918bbc6cc0efcc498f9cc0887' 23 | # The JSON RPC endpoint where the contracts are accessible 24 | ContractsURL = 'http://127.0.0.1:8555/' 25 | # Contract source directory 26 | DeploymentDir = "./deployments/dockerGeth/" 27 | # The layer 1 JSON RPC endpoint 28 | EthereumURL = "http://geth:8545/" 29 | 30 | [Shuttermint] 31 | ShuttermintURL = "http://chain-1-sentry:26657" 32 | ValidatorPublicKey = '720accc33c2bce1319d12b5f23f81b39a33f487e21c4a4d7ca1e1f7954a24172' 33 | EncryptionKey = '8972bbbcf5b4a9eaef0030b1eff64332e7188ff4dd523314176956b5c624ca8b' 34 | DKGPhaseLength = 8 35 | DKGStartBlockDelta = 200 36 | 37 | [Metrics] 38 | Enabled = true 39 | Host = "[::]" 40 | Port = 9100 41 | -------------------------------------------------------------------------------- /docker/config.example/keyper-2.toml: -------------------------------------------------------------------------------- 1 | # Peer identity: /p2p/12D3KooWLwtKMLJqRGWB3AGi87u8Sc7hwp6a6PDMAyWEAsHnjGJG 2 | # Ethereum address: 0x4F01A5A4Ef09c08Df83A85885516424A4a53be68 3 | 4 | 5 | InstanceID = 0 6 | # If it's empty, we use the standard PG_ environment variables 7 | DatabaseURL = "postgres://postgres@db:5432/keyper-2" 8 | HTTPEnabled = false 9 | HTTPListenAddress = ':3000' 10 | 11 | [P2P] 12 | P2PKey = 'CAESQJrsyuTYZZpjqfbXTtMbrQOuS1XtzD+M6ssAZLo5R26fpV218+TTRXw4mkAdaxWpIDX0ZEQH5NC8A8+1nWvbF2U=' 13 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 14 | # Overwrite p2p boostrap nodes 15 | CustomBootstrapAddresses = [ 16 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 17 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 18 | ] 19 | 20 | [Ethereum] 21 | PrivateKey = 'e9383a24352f05bc11895c0da19efb6b83c726f05643c38f64b4146f19215125' 22 | # The JSON RPC endpoint where the contracts are accessible 23 | ContractsURL = 'http://127.0.0.1:8555/' 24 | # Contract source directory 25 | DeploymentDir = "./deployments/dockerGeth/" 26 | # The layer 1 JSON RPC endpoint 27 | EthereumURL = "http://geth:8545/" 28 | 29 | [Shuttermint] 30 | ShuttermintURL = "http://chain-2-sentry:26657" 31 | ValidatorPublicKey = '720accc33c2bce1319d12b5f23f81b39a33f487e21c4a4d7ca1e1f7954a24172' 32 | EncryptionKey = 'ee014227e5b6209682f31c967b4a21c6617a02bc5f11f644f5f54f6d613906b9' 33 | DKGPhaseLength = 8 34 | DKGStartBlockDelta = 200 35 | 36 | [Metrics] 37 | Enabled = true 38 | Host = "[::]" 39 | Port = 9100 40 | -------------------------------------------------------------------------------- /docker/config.example/keyper-3.toml: -------------------------------------------------------------------------------- 1 | # Peer identity: /p2p/12D3KooWQHp6UiLdi46qyettnq664LkjqwFv8yKhxA1tLHi1C73c 2 | # Ethereum address: 0x03C82916552440f803b6284B40a0d64C5eb4E779 3 | 4 | InstanceID = 0 5 | # If it's empty, we use the standard PG_ environment variables 6 | DatabaseURL = "postgres://postgres@db:5432/keyper-3" 7 | HTTPEnabled = false 8 | HTTPListenAddress = ':3000' 9 | 10 | [P2P] 11 | P2PKey = 'CAESQH9VL1y+iPWZeVK+ga3pIy33JNpkX0bdKyON/45ut+gG1wudK44qlCp/pcE40rEk4NljSzkfHs7Ez86wIfN8YEc=' 12 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 13 | # Overwrite p2p boostrap nodes 14 | CustomBootstrapAddresses = [ 15 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 16 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 17 | ] 18 | 19 | [Ethereum] 20 | PrivateKey = 'e728f5f1254b4c645ff2f2181fc114f4bb6ab2a995df2f6d97f1f220f5936898' 21 | # The JSON RPC endpoint where the contracts are accessible 22 | ContractsURL = 'http://127.0.0.1:8555/' 23 | # Contract source directory 24 | DeploymentDir = "./deployments/dockerGeth/" 25 | # The layer 1 JSON RPC endpoint 26 | EthereumURL = "http://geth:8545/" 27 | 28 | [Shuttermint] 29 | ShuttermintURL = "http://chain-3-sentry:26657" 30 | ValidatorPublicKey = '8ad31f4e77c5092977d57e578b47dc23a86762933a4c09bcb055456805c84096' 31 | EncryptionKey = '13c24d4a50e53bdb4cf8ae328491fd10fbf1bc0118ff6adad1340fa77eaf9c3c' 32 | DKGPhaseLength = 8 33 | DKGStartBlockDelta = 200 34 | 35 | [Metrics] 36 | Enabled = true 37 | Host = "[::]" 38 | Port = 9100 39 | -------------------------------------------------------------------------------- /docker/config.example/node-deploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "keypers": [ 3 | [ 4 | "0xCDD50A6F9B1439dc14c4f2A7eaF14dA1EF5A476c", 5 | "0x539cF80D345d26525A47dB80De0fAb147D588fDa", 6 | "0x4F01A5A4Ef09c08Df83A85885516424A4a53be68" 7 | ], 8 | [ 9 | "0xCDD50A6F9B1439dc14c4f2A7eaF14dA1EF5A476c", 10 | "0x539cF80D345d26525A47dB80De0fAb147D588fDa", 11 | "0x03C82916552440f803b6284B40a0d64C5eb4E779" 12 | ] 13 | ], 14 | "decryptors": ["0x0000000000000000000000000000000000000000"], 15 | "collator": "0x2E135FE171fB6351026B75aF688a7F9689B66B87", 16 | "fundValue": "1000", 17 | "activationBlockOffset": 30 18 | } 19 | -------------------------------------------------------------------------------- /docker/config.example/snapshot.toml: -------------------------------------------------------------------------------- 1 | 2 | # Peer identity: /p2p/12D3KooWGFa3hxTrzU27tgCNE94NP2Wb5my6iS2ty19M3yW24Jeq 3 | # Ethereum address: 0x2E135FE171fB6351026B75aF688a7F9689B66B87 4 | 5 | 6 | InstanceID = 0 7 | DatabaseURL = "postgres://postgres@db:5432/snapshot" 8 | SnapshotHubURL = "http://dummyserver:5000" 9 | JSONRPCHost = '' 10 | JSONRPCPort = 8754 11 | 12 | [P2P] 13 | P2PKey = 'CAESQIL98WGKWUdyzp8mznCV8jJv5Lqsbz2jDNqaY7BXrjCJX5sSskzcPC6U25ZRn98qyvXrunN9DT8AkS1T5PcD/Z4=' 14 | ListenAddresses = ["/ip4/0.0.0.0/tcp/23000"] 15 | # Overwrite p2p boostrap nodes 16 | CustomBootstrapAddresses = [ 17 | "/dns4/bootnode-0/tcp/23000/p2p/12D3KooWJN7262vmnEQHkYG7VrZDwz9fMyJtHyvGp4XSenuUYfeJ", 18 | "/dns4/bootnode-1/tcp/23000/p2p/12D3KooWSayB2PEYpXtdk2dEqFaagoy8kDzmWpoD9DieuuzYdcBo" 19 | ] 20 | 21 | [Ethereum] 22 | # Ethereum Private keyh (we reuse the collator EthereumKey, so snapshot can be authorized against collator contract) 23 | PrivateKey = "215ddd19b91c54252bb956c7f88e42122a61bd45a562d5bae38bc2328a9b52d0" 24 | # The JSON RPC endpoint where the contracts are accessible 25 | ContractsURL = 'http://127.0.0.1:8555/' 26 | # Contract source directory 27 | DeploymentDir = './deployments/localhost/' 28 | # The layer 1 JSON RPC endpoint 29 | EthereumURL = "http://geth:8545/" 30 | 31 | [Metrics] 32 | Enabled = true 33 | Host = "[::]" 34 | Port = 9100 35 | -------------------------------------------------------------------------------- /docker/config/.gitignore: -------------------------------------------------------------------------------- 1 | *.toml 2 | *.json 3 | -------------------------------------------------------------------------------- /docker/config/grafana/provisioning/datasources/vm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: VictoriaMetrics 5 | type: victoriametrics-datasource 6 | access: proxy 7 | url: http://metrics:8428 8 | isDefault: true 9 | -------------------------------------------------------------------------------- /js/shutter-crypto/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: "eslint:recommended", 3 | ignorePatterns: ["derived/*.js"], 4 | parserOptions: { 5 | sourceType: "module", 6 | }, 7 | env: { 8 | browser: true, 9 | jest: true, 10 | node: true, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /js/shutter-crypto/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | NPM ?= npm 4 | NPX ?= npx 5 | TINYGO ?= tinygo 6 | 7 | clean: 8 | rm -r dist 9 | mkdir -p dist 10 | 11 | build: clean derive 12 | ${NPX} webpack build 13 | rsync derived/shutter-crypto.wasm dist/shutter-crypto.wasm 14 | 15 | publish: build 16 | ${NPM} publish --access=public 17 | 18 | pack: build 19 | ${NPM} pack 20 | 21 | derive: derived/shutter-crypto.wasm derived/wasm_exec.js 22 | 23 | derived/shutter-crypto.wasm: ../../rolling-shutter/bin/shutter-crypto.wasm 24 | ln -sf ../../../rolling-shutter/bin/shutter-crypto.wasm derived/ 25 | derived/wasm_exec.js: 26 | cp $(shell ${TINYGO} env TINYGOROOT)/targets/wasm_exec.js derived/ 27 | patch --quiet --directory derived --strip 1 --input ../patches/wasm_exec.patch 28 | 29 | test: 30 | ${NPM} test 31 | 32 | .PHONY: build derive publish test 33 | -------------------------------------------------------------------------------- /js/shutter-crypto/README.md: -------------------------------------------------------------------------------- 1 | # shutter-crypto 2 | 3 | This is a NPM package which provides the core crypto primitives necessary to 4 | interact with the Shutter Network. 5 | 6 | ## Installation 7 | 8 | ``` 9 | npm install @shutter-network/shutter-crypto 10 | ``` 11 | 12 | ## Usage 13 | 14 | The module provides the following public functions: 15 | 16 | ### `async shutterCrypto.init(wasmUrlOrPath)` 17 | 18 | Load and initialize the Go wasm library. This Promise needs to be consumed 19 | before any other function in the library is called. 20 | 21 | On Node the `wasmUrlOrPath` parameter is optional. If not given it will be 22 | determined automatically. 23 | 24 | In a Web context the path to the `shutter-crypto.wasm` file needs to be given 25 | (since it appears no standard cross framework way of automatically determining a 26 | path is available). 27 | 28 | ### `async shutterCrypto.encrypt(message, eonPublicKey, epochId, sigma)` 29 | 30 | ... 31 | 32 | ### `async shutterCrypto.decrypt(encryptedMessage, decryptionKey)` 33 | 34 | ... 35 | 36 | ### `async shutterCrypto.verifyDecryptionKey(decryptionKey, eonPublicKey, epochId)` 37 | 38 | ... 39 | 40 | ## Releases 41 | 42 | ### `1.0.1` - 2023-08-02 43 | 44 | Only contains a fix in the README. 45 | 46 | ### `1.0.0` - 2023-08-02 47 | 48 | Stable release. 49 | 50 | Changes: 51 | 52 | - Fix node v18 incompatibility 53 | - Add README 54 | 55 | ### `0.1.0-beta.3` - 2022-07-18 56 | 57 | First usable version 58 | 59 | This includes significant size reduction of the generated wasm file. 60 | -------------------------------------------------------------------------------- /js/shutter-crypto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shutter-network/shutter-crypto", 3 | "version": "2.0.0", 4 | "description": "Core crypto primitives to interact with Shutter Network", 5 | "author": "Shutter Network ", 6 | "license": "MIT", 7 | "homepage": "https://github.com/shutter-network/rolling-shutter/tree/main/js/shutter-crypto#readme", 8 | "repository": "github:shutter-network/rolling-shutter", 9 | "devDependencies": { 10 | "eslint": "^8.56.0", 11 | "ethers": "^6.11.1", 12 | "jest": "^29.7.0", 13 | "webpack": "^5.72.1", 14 | "webpack-cli": "^4.9.2" 15 | }, 16 | "main": "./dist/shutter-crypto.js", 17 | "files": [ 18 | "dist" 19 | ], 20 | "dependencies": { 21 | "browser-or-node": "^2.0.0" 22 | }, 23 | "scripts": { 24 | "prepare": "cp -L derived/shutter-crypto.wasm dist", 25 | "test": "jest ./test/" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /js/shutter-crypto/patches/wasm_exec.patch: -------------------------------------------------------------------------------- 1 | diff --git a/wasm_exec.js b/wasm_exec.js 2 | index 8021b44..13d0075 100644 3 | --- a/wasm_exec.js 4 | +++ b/wasm_exec.js 5 | @@ -101,12 +101,7 @@ 6 | } 7 | 8 | if (!global.crypto) { 9 | - const nodeCrypto = require("crypto"); 10 | - global.crypto = { 11 | - getRandomValues(b) { 12 | - nodeCrypto.randomFillSync(b); 13 | - }, 14 | - }; 15 | + global.crypto = require("crypto"); 16 | } 17 | 18 | if (!global.performance) { 19 | -------------------------------------------------------------------------------- /js/shutter-crypto/src/_node18_crypto_fallback.js: -------------------------------------------------------------------------------- 1 | const { isNode } = require("browser-or-node"); 2 | const crypto = isNode ? __non_webpack_require__("crypto") : window.crypto; // eslint-disable-line no-undef 3 | module.exports = crypto; 4 | -------------------------------------------------------------------------------- /js/shutter-crypto/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | mode: "production", 5 | output: { 6 | path: path.resolve(__dirname, "dist"), 7 | filename: "shutter-crypto.js", 8 | globalObject: "this", 9 | library: { 10 | name: "shutterCrypto", 11 | type: "umd", 12 | }, 13 | }, 14 | node: { 15 | // Disable mangling node's `__dirname` property since we need it to load the WASM file 16 | __dirname: false, 17 | }, 18 | resolve: { 19 | fallback: { 20 | fs: false, 21 | crypto: path.resolve(__dirname, "src", "_node18_crypto_fallback.js"), 22 | util: false, 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /play/.gitignore: -------------------------------------------------------------------------------- 1 | /chain/ 2 | /k*.toml 3 | /p2p*.toml 4 | /mock.toml 5 | /collator.toml 6 | /mocksequencer.toml 7 | /play.json 8 | /testchain/ 9 | /deployments 10 | /.nrepl* 11 | /.cpcache 12 | /work/ 13 | /ci-bb.edn 14 | *.jar 15 | /target/ 16 | .clj-kondo 17 | -------------------------------------------------------------------------------- /play/build.clj: -------------------------------------------------------------------------------- 1 | (ns build 2 | (:require [clojure.tools.build.api :as b])) 3 | 4 | (def class-dir "target/classes") 5 | (def basis (b/create-basis {:project "deps.edn"})) 6 | (def uber-file "sht-standalone.jar") 7 | 8 | (defn clean [_] 9 | (b/delete {:path "target"})) 10 | 11 | (defn uber [_] 12 | (clean nil) 13 | (b/copy-dir {:src-dirs ["src" "resources"] 14 | :target-dir class-dir}) 15 | (b/compile-clj {:basis basis 16 | :src-dirs ["src"] 17 | :class-dir class-dir}) 18 | (b/uber {:class-dir class-dir 19 | :uber-file uber-file 20 | :basis basis 21 | :main 'sht.core})) 22 | -------------------------------------------------------------------------------- /play/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :resource-paths [] 3 | :deps {org.clojure/clojure {:mvn/version "1.11.1"} 4 | cheshire/cheshire {:mvn/version "5.11.0"} 5 | com.github.seancorfield/next.jdbc {:mvn/version "1.3.883"} 6 | org.postgresql/postgresql {:mvn/version "42.6.0"} 7 | com.taoensso/timbre {:mvn/version "6.2.2"} 8 | toml/toml {:mvn/version "0.1.4"} 9 | mvxcvi/puget {:mvn/version "1.3.4"} 10 | org.babashka/http-client {:mvn/version "0.4.13"} 11 | babashka/fs {:mvn/version "0.4.19"} 12 | babashka/process {:mvn/version "0.5.21"} 13 | org.clojars.lispyclouds/contajners {:mvn/version "0.0.6"}} 14 | 15 | :aliases {;; (deprecated) Run tests with clojure -X:run-tests 16 | :run-tests {:exec-fn sht.core/run-tests} 17 | ;; Run with clojure -M:test 18 | :test {:main-opts ["-m" "sht.core"]} 19 | ;; Build uberjar with clojure -T:build 20 | :build {:deps {io.github.clojure/tools.build {:mvn/version "0.9.5"}} 21 | :ns-default build 22 | :exec-fn uber} 23 | :outdated 24 | {:extra-deps {com.github.liquidz/antq {:mvn/version "RELEASE"} 25 | org.slf4j/slf4j-nop {:mvn/version "2.0.9"}} 26 | :main-opts ["-m" "antq.core"]} 27 | 28 | :dev {:extra-paths ["dev"]}}} 29 | -------------------------------------------------------------------------------- /play/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [next.jdbc :as jdbc] 3 | [taoensso.timbre 4 | :refer [log trace debug info warn error fatal report 5 | logf tracef debugf infof warnf errorf fatalf reportf 6 | spy get-env]] 7 | [sht.runner :as runner] 8 | [sht.build :as build] 9 | [sht.dkg-test :as dkg-test] 10 | [sht.collator-test :as collator-test] 11 | [sht.play :as play] 12 | [sht.core :as core])) 13 | 14 | (defn doit [] 15 | (core/report-result [(runner/run-test (first @dkg-test/tests))])) 16 | 17 | (defn doit2 [] 18 | (core/report-result [(runner/run-test (collator-test/test-collator-basic {:num-keypers 3, :threshold 2}))])) 19 | 20 | (comment 21 | (do 22 | (def db {:dbtype "postgresql" 23 | :dbname (play/keyper-db 0) 24 | :password (System/getenv "PLAY_DB_PASSWORD")}) 25 | 26 | (def ds (jdbc/get-datasource db))) 27 | (jdbc/execute! ds ["select * from keyper_set"]) 28 | 29 | (jdbc/execute! ds ["select * from tendermint_batch_config"]) 30 | (jdbc/execute-one! ds ["select * from meta_inf"]) 31 | (jdbc/execute! ds ["select * from eons"]) 32 | ) 33 | -------------------------------------------------------------------------------- /play/manual/initdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dropdb --if-exists keyper-db-0 4 | dropdb --if-exists keyper-db-1 5 | dropdb --if-exists keyper-db-2 6 | createdb keyper-db-0 7 | createdb keyper-db-1 8 | createdb keyper-db-2 9 | ./rolling-shutter gnosiskeyper initdb --config keyper-0.toml 10 | ./rolling-shutter gnosiskeyper initdb --config keyper-1.toml 11 | ./rolling-shutter gnosiskeyper initdb --config keyper-2.toml 12 | -------------------------------------------------------------------------------- /play/manual/k.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./rolling-shutter gnosiskeyper --config keyper-"$1".toml 4 | -------------------------------------------------------------------------------- /play/manual/op-bootstrap-config.toml: -------------------------------------------------------------------------------- 1 | InstanceID = 42 2 | JSONRPCURL = 'https://rpc.chiadochain.net/' 3 | KeyperSetManager = '0x058062B7d74ba6B4Af1eE22833579C67adC17175' 4 | ByIndex = 1 5 | KeyperSetFilePath = 'keyperset.json' 6 | ShuttermintURL = 'http://127.0.0.1:26657' 7 | SigningKey = '479968ffa5ee4c84514a477a8f15f3db0413964fd4c20b08a55fed9fed790fad' 8 | -------------------------------------------------------------------------------- /play/manual/p2p.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./rolling-shutter p2pnode --config p2p_bootstrap.toml 4 | -------------------------------------------------------------------------------- /play/manual/p2p_bootstrap.toml: -------------------------------------------------------------------------------- 1 | # Peer identity: /p2p/12D3KooWMyutShWdqYj7fre4Vjuq2QnCTb26Ki1KpyDyVsrmKeki 2 | # Peer role: bootstrap 3 | 4 | 5 | # whether to register handlers on the messages and log them 6 | ListenMessages = true 7 | SigningKey = "479968ffa5ee4c84514a477a8f15f3db0413964fd4c20b08a55fed9fed790fad" 8 | 9 | [P2P] 10 | P2PKey = 'CAESQP97SvcBcpQ+FlDQ2+unlt2/sQWnY4MLaG8IFBCkCwR7tL5Vy3RFatDpX4Sy9fMvjLl7vM/hzqQxi9C6liPRL8s=' 11 | ListenAddresses = ['/ip4/127.0.0.1/tcp/2001'] 12 | # Overwrite p2p boostrap nodes 13 | CustomBootstrapAddresses = ['/ip4/127.0.0.1/tcp/2001/p2p/12D3KooWMyutShWdqYj7fre4Vjuq2QnCTb26Ki1KpyDyVsrmKeki'] 14 | Environment = 'local' 15 | DiscoveryNamespace = "shutter-42" 16 | -------------------------------------------------------------------------------- /play/manual/rolling-shutter: -------------------------------------------------------------------------------- 1 | ../../rolling-shutter/bin/rolling-shutter -------------------------------------------------------------------------------- /play/manual/shuttermint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export ROLLING_SHUTTER_BOOTSTRAP_SIGNING_KEY=479968ffa5ee4c84514a477a8f15f3db0413964fd4c20b08a55fed9fed790fad 4 | export ROLLING_SHUTTER_CHAIN_GENESIS_KEYPER=0x440Dc6F164e9241F04d282215ceF2780cd0B755e 5 | 6 | (cd .. && bb init:testchain) 7 | (cd .. && bb chain) & 8 | 9 | sleep 2 10 | rm -f keyperset.json 11 | ./rolling-shutter op-bootstrap fetch-keyperset --config op-bootstrap-config.toml 12 | ./rolling-shutter op-bootstrap --config op-bootstrap-config.toml 13 | 14 | wait 15 | -------------------------------------------------------------------------------- /play/process-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "0.5" 2 | 3 | environment: 4 | - "PG_USER=postgres" 5 | - "NOCOLOR=1" 6 | log_location: work/run/output.log 7 | log_level: debug 8 | log_configuration: 9 | no_color: true 10 | 11 | processes: 12 | Postgres: 13 | command: "pg_ctl -D work/db/ -l work/db/logfile start" 14 | is_daemon: true 15 | shutdown: 16 | command: "pg_ctl -D work/db/ stop" 17 | timeout_seconds: 10 # default 10 18 | signal: 15 # default 15, but only if command is not defined or empty 19 | readiness_probe: 20 | exec: 21 | command: "pg_isready" 22 | initial_delay_seconds: 5 23 | period_seconds: 30 24 | timeout_seconds: 60 25 | success_threshold: 1 26 | failure_threshold: 2 27 | -------------------------------------------------------------------------------- /play/rsbb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # Run babashka tasks from anywhere. This script is meant to be symlinked into 4 | # your PATH with something like 5 | # 6 | # ln -s $(pwd)/rsbb ~/bin 7 | # 8 | # It will chdir to the play directory and run babashka's task runner with 9 | # additional arguments passed through, e.g. the following will start the first 10 | # keyper: 11 | # 12 | # rsbb k 0 13 | # 14 | 15 | set -euo pipefail 16 | 17 | DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" 18 | cd -- "${DIR}" 19 | 20 | if [[ -z $* ]]; then 21 | exec bb run build 22 | elif [[ "$1" = "tasks" ]]; then 23 | exec bb tasks 24 | else 25 | exec bb run "$@" 26 | fi 27 | -------------------------------------------------------------------------------- /play/src/sht/base64.clj: -------------------------------------------------------------------------------- 1 | (ns sht.base64) 2 | 3 | (defn encode 4 | "Encode an array of bytes into a base64 encoded string." 5 | [^bytes unencoded] 6 | (String. (.encode (java.util.Base64/getEncoder) unencoded))) 7 | 8 | (defn decode 9 | "Decode a base64 encoded string into an array of bytes." 10 | [^String encoded] 11 | (.decode (java.util.Base64/getDecoder) encoded)) 12 | -------------------------------------------------------------------------------- /play/src/sht/toml_writer.clj: -------------------------------------------------------------------------------- 1 | (ns sht.toml-writer 2 | "write enough toml to be able to dump the chain subcommand's config" 3 | (:require [clojure.string :as str])) 4 | 5 | (defn- print-name [name] 6 | (when (seq name) 7 | (printf "\n[%s]\n" name))) 8 | 9 | 10 | (defn- compose-deep-name [deep-name nested-name] 11 | (let [deep-name (name deep-name) 12 | nested-name (name nested-name)] 13 | (if (seq deep-name) 14 | (str deep-name "." nested-name) 15 | nested-name))) 16 | 17 | (defn- pr-str-value 18 | [v] 19 | (cond 20 | (or (number? v) (boolean? v) (string? v)) 21 | (pr-str v) 22 | (or (seq? v) (vector? v)) 23 | (format "[%s]" (str/join "," (mapv pr-str v))) 24 | :else 25 | (throw (ex-info "not handled" {:v v})))) 26 | 27 | 28 | (defn- write 29 | ([data] (write data "")) 30 | ([data deep-name] 31 | (let [simple-vals (filter (fn [[k v]] (not (map? v))) data) 32 | complex-vals (filter (fn [[k v]] (map? v)) data)] 33 | (when (seq simple-vals) 34 | (print-name deep-name) 35 | (doseq [[k v] simple-vals] 36 | (printf "%s = " (name k)) 37 | (println (pr-str-value v)))) 38 | (doseq [[dp v] complex-vals] 39 | (write v (compose-deep-name deep-name dp)))))) 40 | 41 | (defn dump 42 | [m] 43 | (with-out-str 44 | (write m))) 45 | -------------------------------------------------------------------------------- /rolling-shutter/.gitignore: -------------------------------------------------------------------------------- 1 | /shuttermint 2 | /tmhome/ 3 | /data/ 4 | /testchain/ 5 | /testrun/ 6 | /t/ 7 | *.json 8 | *.toml 9 | /godoc.idx 10 | /go.work 11 | /go.work.sum 12 | -------------------------------------------------------------------------------- /rolling-shutter/app/noncetracker.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "github.com/ethereum/go-ethereum/common" 4 | 5 | // NewNonceTracker creates a new NonceTracker. 6 | func NewNonceTracker() *NonceTracker { 7 | return &NonceTracker{ 8 | RandomNonces: make(map[common.Address]map[uint64]bool), 9 | } 10 | } 11 | 12 | // Check returns true if the given nonce is free and false if it has been added already. 13 | func (t *NonceTracker) Check(sender common.Address, randomNonce uint64) bool { 14 | m, ok := t.RandomNonces[sender] 15 | if !ok { 16 | return true 17 | } 18 | return !m[randomNonce] 19 | } 20 | 21 | // Add adds the given nonce if it hasn't already. 22 | func (t *NonceTracker) Add(sender common.Address, randomNonce uint64) { 23 | m, ok := t.RandomNonces[sender] 24 | if !ok { 25 | m = make(map[uint64]bool) 26 | t.RandomNonces[sender] = m 27 | } 28 | m[randomNonce] = true 29 | } 30 | -------------------------------------------------------------------------------- /rolling-shutter/app/noncetracker_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "gotest.tools/v3/assert" 9 | ) 10 | 11 | func TestNonceTracker(t *testing.T) { 12 | tracker := NewNonceTracker() 13 | a1 := common.BigToAddress(big.NewInt(0)) 14 | a2 := common.BigToAddress(big.NewInt(1)) 15 | r1 := uint64(10) 16 | r2 := uint64(20) 17 | 18 | assert.Assert(t, tracker.Check(a1, r1)) 19 | tracker.Add(a1, r1) 20 | assert.Assert(t, !tracker.Check(a1, r1)) 21 | assert.Assert(t, tracker.Check(a1, r2)) 22 | assert.Assert(t, tracker.Check(a2, r1)) 23 | } 24 | -------------------------------------------------------------------------------- /rolling-shutter/bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /rolling-shutter/bin/rs: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # Run rolling-shutter and make sure it's up to date. 4 | # 5 | 6 | set -euo pipefail 7 | 8 | fail() { 9 | echo >&2 -e "$*" 10 | exit 1 11 | } 12 | 13 | DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" 14 | 15 | ( 16 | exec 1>&2 17 | cd -- "${DIR}"/../../play 18 | exec bb run build 19 | ) || ( 20 | fail "ERROR: build failed" 21 | ) 22 | 23 | exec "${DIR}/rolling-shutter" "$@" 24 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/addrseq.go: -------------------------------------------------------------------------------- 1 | package chainobserver 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/pkg/errors" 9 | 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/contract" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" 12 | ) 13 | 14 | func RetryGetAddrs(ctx context.Context, addrsSeq *contract.AddrsSeq, n uint64) ([]common.Address, error) { 15 | callOpts := &bind.CallOpts{ 16 | Pending: false, 17 | // We call for the current height instead of the height at which the event was emitted, 18 | // because the sets cannot change retroactively and we won't need an archive node. 19 | BlockNumber: nil, 20 | Context: ctx, 21 | } 22 | addrs, err := retry.FunctionCall(ctx, func(_ context.Context) ([]common.Address, error) { 23 | return addrsSeq.GetAddrs(callOpts, n) 24 | }) 25 | if err != nil { 26 | return []common.Address{}, errors.Wrapf(err, "failed to query address set from contract") 27 | } 28 | return addrs, nil 29 | } 30 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/collator.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | // source: collator.sql 5 | 6 | package database 7 | 8 | import ( 9 | "context" 10 | ) 11 | 12 | const getChainCollator = `-- name: GetChainCollator :one 13 | SELECT activation_block_number, collator FROM chain_collator 14 | WHERE activation_block_number <= $1 15 | ORDER BY activation_block_number DESC LIMIT 1 16 | ` 17 | 18 | func (q *Queries) GetChainCollator(ctx context.Context, activationBlockNumber int64) (ChainCollator, error) { 19 | row := q.db.QueryRow(ctx, getChainCollator, activationBlockNumber) 20 | var i ChainCollator 21 | err := row.Scan(&i.ActivationBlockNumber, &i.Collator) 22 | return i, err 23 | } 24 | 25 | const insertChainCollator = `-- name: InsertChainCollator :exec 26 | INSERT INTO chain_collator (activation_block_number, collator) 27 | VALUES ($1, $2) 28 | ` 29 | 30 | type InsertChainCollatorParams struct { 31 | ActivationBlockNumber int64 32 | Collator string 33 | } 34 | 35 | func (q *Queries) InsertChainCollator(ctx context.Context, arg InsertChainCollatorParams) error { 36 | _, err := q.db.Exec(ctx, insertChainCollator, arg.ActivationBlockNumber, arg.Collator) 37 | return err 38 | } 39 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | sync "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/sync" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 10 | ) 11 | 12 | //go:generate sqlc generate --file sql/sqlc.yaml 13 | 14 | //go:embed sql 15 | var files embed.FS 16 | var Definition db.Definition 17 | 18 | func init() { 19 | def, err := db.NewSQLCDefinition(files, "sql/", "chainobscollator", 1) 20 | if err != nil { 21 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 22 | } 23 | Definition = db.NewAggregateDefinition( 24 | "chainobscollator", 25 | def, 26 | sync.Definition, 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | type ChainCollator struct { 8 | ActivationBlockNumber int64 9 | Collator string 10 | } 11 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/sql/queries/collator.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertChainCollator :exec 2 | INSERT INTO chain_collator (activation_block_number, collator) 3 | VALUES ($1, $2); 4 | 5 | -- name: GetChainCollator :one 6 | SELECT * FROM chain_collator 7 | WHERE activation_block_number <= $1 8 | ORDER BY activation_block_number DESC LIMIT 1; 9 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/sql/schemas/collator.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE chain_collator( 2 | activation_block_number bigint PRIMARY KEY, 3 | collator text NOT NULL 4 | ); 5 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/collator/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | collatordb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/collator" 5 | keyperdb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 7 | ) 8 | 9 | var ( 10 | KeyperDefinition db.Definition = keyperdb.Definition 11 | CollatorDefinition db.Definition = collatordb.Definition 12 | ) 13 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | sync "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/sync" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 10 | ) 11 | 12 | //go:generate sqlc generate --file sql/sqlc.yaml 13 | 14 | //go:embed sql 15 | var files embed.FS 16 | var Definition db.Definition 17 | 18 | func init() { 19 | def, err := db.NewSQLCDefinition(files, "sql/", "chainobskeyper", 1) 20 | if err != nil { 21 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 22 | } 23 | Definition = db.NewAggregateDefinition( 24 | "chainobskeyper", 25 | def, 26 | sync.Definition, 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | type KeyperSet struct { 8 | KeyperConfigIndex int64 9 | ActivationBlockNumber int64 10 | Keypers []string 11 | Threshold int32 12 | } 13 | 14 | type RecentBlock struct { 15 | BlockHash []byte 16 | BlockNumber int64 17 | ParentHash []byte 18 | Timestamp int64 19 | Header []byte 20 | } 21 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/sql/queries/keyper.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertKeyperSet :exec 2 | INSERT INTO keyper_set ( 3 | keyper_config_index, 4 | activation_block_number, 5 | keypers, 6 | threshold 7 | ) VALUES ( 8 | $1, $2, $3, $4 9 | ) ON CONFLICT DO NOTHING; 10 | 11 | -- name: GetKeyperSetByKeyperConfigIndex :one 12 | SELECT * FROM keyper_set WHERE keyper_config_index=$1; 13 | 14 | -- name: GetKeyperSet :one 15 | SELECT * FROM keyper_set 16 | WHERE activation_block_number <= $1 17 | ORDER BY activation_block_number DESC LIMIT 1; 18 | 19 | -- name: GetKeyperSets :many 20 | SELECT * FROM keyper_set 21 | ORDER BY activation_block_number ASC; -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/sql/schemas/keyper.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE keyper_set( 2 | keyper_config_index bigint NOT NULL, 3 | activation_block_number bigint NOT NULL, 4 | keypers text[] NOT NULL, 5 | threshold integer NOT NULL, 6 | PRIMARY KEY (keyper_config_index) 7 | ); 8 | 9 | CREATE TABLE recent_block ( 10 | block_hash bytea NOT NULL, 11 | block_number bigint NOT NULL, 12 | parent_hash bytea NOT NULL, 13 | timestamp bigint NOT NULL, 14 | header bytea NOT NULL, 15 | PRIMARY KEY (block_number) 16 | ); -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/keyper/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 9 | ) 10 | 11 | //go:generate sqlc generate --file sql/sqlc.yaml 12 | 13 | //go:embed sql 14 | var files embed.FS 15 | var Definition db.Definition 16 | 17 | func init() { 18 | var err error 19 | Definition, err = db.NewSQLCDefinition(files, "sql/", "chainobssync", 1) 20 | if err != nil { 21 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | type EventSyncProgress struct { 8 | ID bool 9 | NextBlockNumber int32 10 | NextLogIndex int32 11 | } 12 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/sql/queries/sync.sql: -------------------------------------------------------------------------------- 1 | -- name: UpdateEventSyncProgress :exec 2 | INSERT INTO event_sync_progress (next_block_number, next_log_index) 3 | VALUES ($1, $2) 4 | ON CONFLICT (id) DO UPDATE 5 | SET next_block_number = $1, 6 | next_log_index = $2; 7 | 8 | -- name: GetEventSyncProgress :one 9 | SELECT next_block_number, next_log_index FROM event_sync_progress LIMIT 1; 10 | 11 | -- name: GetNextBlockNumber :one 12 | SELECT next_block_number from event_sync_progress LIMIT 1; 13 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/sql/schemas/sync.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE event_sync_progress ( 2 | id bool UNIQUE NOT NULL DEFAULT true, 3 | next_block_number integer NOT NULL, 4 | next_log_index integer NOT NULL 5 | ); 6 | INSERT INTO event_sync_progress (next_block_number, next_log_index) VALUES (0,0); 7 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/db/sync/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/eventsync.go: -------------------------------------------------------------------------------- 1 | package chainobserver 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v4" 7 | "github.com/pkg/errors" 8 | 9 | syncdb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/sync" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/eventsyncer" 11 | ) 12 | 13 | var ErrDBUpdateFail = errors.New("failed to update last synced event") 14 | 15 | // handleEventSyncUpdate handles events and advances the sync state, but rolls back any db updates 16 | // on failure. 17 | func (c *ChainObserver) handleEventSyncUpdate( 18 | ctx context.Context, eventSyncUpdate eventsyncer.EventSyncUpdate, 19 | ) error { 20 | return c.dbpool.BeginFunc(ctx, func(tx pgx.Tx) error { 21 | if eventSyncUpdate.Event != nil { 22 | if err := c.handleEvent(ctx, tx, eventSyncUpdate.Event); err != nil { 23 | return err 24 | } 25 | } 26 | 27 | var nextBlockNumber uint64 28 | var nextLogIndex uint64 29 | if eventSyncUpdate.Event == nil { 30 | nextBlockNumber = eventSyncUpdate.BlockNumber + 1 31 | nextLogIndex = 0 32 | } else { 33 | nextBlockNumber = eventSyncUpdate.BlockNumber 34 | nextLogIndex = eventSyncUpdate.LogIndex + 1 35 | } 36 | db := syncdb.New(tx) 37 | if err := db.UpdateEventSyncProgress(ctx, syncdb.UpdateEventSyncProgressParams{ 38 | NextBlockNumber: int32(nextBlockNumber), 39 | NextLogIndex: int32(nextLogIndex), 40 | }); err != nil { 41 | return errors.Wrap(err, ErrDBUpdateFail.Error()) 42 | } 43 | return nil 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /rolling-shutter/chainobserver/handler.go: -------------------------------------------------------------------------------- 1 | package chainobserver 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | 7 | "github.com/jackc/pgx/v4" 8 | "github.com/pkg/errors" 9 | "github.com/rs/zerolog/log" 10 | ) 11 | 12 | var ErrNoHandler = errors.New("EventType has no Handler") 13 | 14 | func (c *ChainObserver) handleEvent( 15 | ctx context.Context, tx pgx.Tx, event any, 16 | ) error { 17 | eventType := reflect.TypeOf(event) 18 | ev, ok := c.eventTypes[eventType] 19 | if !ok { 20 | log.Info().Str("event-type", reflect.TypeOf(event).String()).Interface("event", event). 21 | Msg("ignoring unknown event") 22 | return nil 23 | } 24 | if ev.Handler == nil { 25 | return errors.Wrapf(ErrNoHandler, "`%s`: ", ev.Name) 26 | } 27 | 28 | err := ev.Handler(ctx, tx, event) 29 | if err != nil { 30 | return errors.Wrapf(err, "error during `%s` handler invocation", ev.Name) 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/chain/log.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/rs/zerolog" 7 | "github.com/rs/zerolog/log" 8 | tenderlog "github.com/tendermint/tendermint/libs/log" 9 | ) 10 | 11 | type tendermintLogger struct { 12 | zerolog.Logger 13 | } 14 | 15 | func (l tendermintLogger) Info(msg string, keyVals ...interface{}) { 16 | l.Logger.Info().Fields(keyVals).Msg(msg) 17 | } 18 | 19 | func (l tendermintLogger) Error(msg string, keyVals ...interface{}) { 20 | e := l.Logger.Error() 21 | e.Fields(keyVals).Msg(msg) 22 | } 23 | 24 | func (l tendermintLogger) Debug(msg string, keyVals ...interface{}) { 25 | l.Logger.Debug().Fields(keyVals).Msg(msg) 26 | } 27 | 28 | func (l tendermintLogger) With(keyVals ...interface{}) tenderlog.Logger { 29 | return tendermintLogger{ 30 | Logger: l.Logger.With().Fields(keyVals).Logger(), 31 | } 32 | } 33 | 34 | // newLogger sets up a new tendermint logger adapted from the global logger. This will be 35 | // compatible with the logger we configure in cmd/root.go. We use this as a replacement for 36 | // tenderlog.NewDefaultLogger. 37 | func newLogger(level string) (tenderlog.Logger, error) { 38 | logLevel, err := zerolog.ParseLevel(level) 39 | if err != nil { 40 | return nil, fmt.Errorf("failed to parse log level (%s): %w", level, err) 41 | } 42 | logger := log.Logger.Level(logLevel) 43 | logger = logger.With().CallerWithSkipFrameCount(zerolog.CallerSkipFrameCount + 1).Logger() 44 | return tendermintLogger{Logger: logger}, nil 45 | } 46 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/command.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/bootstrap" 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/chain" 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/cryptocmd" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosisaccessnode" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosiskeyper" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/optimism" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/p2pnode" 13 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shutterservicekeyper" 14 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/snapshot" 15 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/snapshotkeyper" 16 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/rootcmd" 17 | ) 18 | 19 | func Subcommands() []*cobra.Command { 20 | return []*cobra.Command{ 21 | bootstrap.Cmd(), 22 | chain.Cmd(), 23 | optimism.Cmd(), 24 | optimism.OPBootstrapCmd(), 25 | snapshot.Cmd(), 26 | snapshotkeyper.Cmd(), 27 | gnosiskeyper.Cmd(), 28 | gnosisaccessnode.Cmd(), 29 | cryptocmd.Cmd(), 30 | p2pnode.Cmd(), 31 | shutterservicekeyper.Cmd(), 32 | } 33 | } 34 | 35 | func Command() *cobra.Command { 36 | cmd := rootcmd.Cmd() 37 | cmd.Use = "rolling-shutter" 38 | cmd.Short = "A collection of commands to run and interact with Rolling Shutter nodes" 39 | cmd.AddCommand(Subcommands()...) 40 | return cmd 41 | } 42 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/gnosisaccessnode/gnosisaccessnode.go: -------------------------------------------------------------------------------- 1 | package gnosisaccessnode 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/rs/zerolog/log" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/gnosisaccessnode" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 13 | ) 14 | 15 | func Cmd() *cobra.Command { 16 | builder := command.Build( 17 | main, 18 | command.Usage( 19 | "Run an access node for the keyper network of Shutterized Gnosis Chain", 20 | `This command runs a node that only relays messages, but doesn't create any on 21 | its own. It is intended to be a stable node to connect to to receive messages.`, 22 | ), 23 | command.WithGenerateConfigSubcommand(), 24 | command.WithDumpConfigSubcommand(), 25 | ) 26 | return builder.Command() 27 | } 28 | 29 | func main(config *gnosisaccessnode.Config) error { 30 | log.Info(). 31 | Str("version", shversion.Version()). 32 | Msg("starting access node") 33 | 34 | accessNode := gnosisaccessnode.New(config) 35 | return service.RunWithSighandler(context.Background(), accessNode) 36 | } 37 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/optimism/bootstrap.go: -------------------------------------------------------------------------------- 1 | package optimism 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | boot "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/bootstrap" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" 10 | ) 11 | 12 | // TODO: use this to replace the old bootstrap command. 13 | // First writing the keyperset and then bootstrapping allows 14 | // to support different contracts etc. 15 | func OPBootstrapCmd() *cobra.Command { 16 | builder := command.Build( 17 | bootstrap, 18 | command.Usage( 19 | "Bootstrap validator utility functions for a shuttermint chain", 20 | ``, 21 | ), 22 | command.WithGenerateConfigSubcommand(), 23 | ) 24 | 25 | bootstrapCmd := &cobra.Command{ 26 | Use: "fetch-keyperset", 27 | Short: "fetch-keyperset", 28 | Args: cobra.NoArgs, 29 | RunE: builder.WrapFuncParseConfig(keyperSet), 30 | } 31 | builder.Command().AddCommand(bootstrapCmd) 32 | return builder.Command() 33 | } 34 | 35 | func keyperSet(cfg *boot.Config) error { 36 | ctx := context.Background() 37 | return boot.GetKeyperSet(ctx, cfg) 38 | } 39 | 40 | func bootstrap(cfg *boot.Config) error { 41 | return boot.BootstrapValidators(cfg) 42 | } 43 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/optimism/keyper_test.go: -------------------------------------------------------------------------------- 1 | package optimism_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "gotest.tools/assert" 7 | 8 | config "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/optimism/config" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/test" 11 | ) 12 | 13 | func TestSmokeGenerateConfig(t *testing.T) { 14 | c := config.NewConfig() 15 | test.SmokeGenerateConfig(t, c) 16 | } 17 | 18 | func TestParsedConfig(t *testing.T) { 19 | c := config.NewConfig() 20 | 21 | err := configuration.SetExampleValuesRecursive(c) 22 | assert.NilError(t, err) 23 | parsedConfig := test.RoundtripParseConfig(t, c) 24 | assert.DeepEqual(t, c, parsedConfig) 25 | } 26 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/p2pnode/p2pnode.go: -------------------------------------------------------------------------------- 1 | package p2pnode 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/rs/zerolog/log" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pnode" 13 | ) 14 | 15 | func Cmd() *cobra.Command { 16 | builder := command.Build( 17 | main, 18 | // TODO long usage 19 | command.Usage( 20 | "Run a Shutter p2p bootstrap node", 21 | "", 22 | ), 23 | command.WithGenerateConfigSubcommand(), 24 | command.WithDumpConfigSubcommand(), 25 | ) 26 | return builder.Command() 27 | } 28 | 29 | func main(config *p2pnode.Config) error { 30 | log.Info(). 31 | Str("version", shversion.Version()). 32 | Msg("starting p2pnode") 33 | p2pNode := p2pnode.New(config) 34 | return service.RunWithSighandler(context.Background(), p2pNode) 35 | } 36 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/p2pnode/p2pnode_test.go: -------------------------------------------------------------------------------- 1 | package p2pnode_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "gotest.tools/assert" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/test" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pnode" 11 | ) 12 | 13 | func TestSmokeGenerateConfig(t *testing.T) { 14 | config := p2pnode.NewConfig() 15 | test.SmokeGenerateConfig(t, config) 16 | } 17 | 18 | func TestParsedConfig(t *testing.T) { 19 | config := p2pnode.NewConfig() 20 | 21 | err := configuration.SetExampleValuesRecursive(config) 22 | assert.NilError(t, err) 23 | parsedConfig := test.RoundtripParseConfig(t, config) 24 | assert.DeepEqual(t, config, parsedConfig) 25 | } 26 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/shversion/race.go: -------------------------------------------------------------------------------- 1 | //go:build !race 2 | // +build !race 3 | 4 | package shversion 5 | 6 | var raceDetectorEnabled = false 7 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/shversion/race_enabled.go: -------------------------------------------------------------------------------- 1 | //go:build race 2 | // +build race 3 | 4 | package shversion 5 | 6 | var raceDetectorEnabled = true 7 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/shversion/shversion.go: -------------------------------------------------------------------------------- 1 | // Package shversion contains version information being set via linker flags when building via the 2 | // Makefile 3 | package shversion 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "runtime/debug" 9 | ) 10 | 11 | // This gets set via ldflags when building via the Makefile. 12 | var version string 13 | 14 | // Version returns shuttermint's version string. 15 | func Version() string { 16 | var raceinfo string 17 | if raceDetectorEnabled { 18 | raceinfo = ", race detector enabled" 19 | } 20 | return fmt.Sprintf("%s (%s, %s-%s%s)", VersionShort(), runtime.Version(), runtime.GOOS, runtime.GOARCH, raceinfo) 21 | } 22 | 23 | func VersionShort() string { 24 | if version == "" { 25 | info, ok := debug.ReadBuildInfo() 26 | if ok { 27 | versionShort := info.Main.Version 28 | if versionShort == "(devel)" { 29 | for _, s := range info.Settings { 30 | if s.Key == "vcs.revision" { 31 | return fmt.Sprintf("(devel-%s)", s.Value) 32 | } 33 | } 34 | } 35 | return versionShort 36 | } 37 | } 38 | return version 39 | } 40 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/snapshot/snapshot.go: -------------------------------------------------------------------------------- 1 | package snapshot 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v4/pgxpool" 7 | "github.com/pkg/errors" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 13 | "github.com/shutter-network/rolling-shutter/rolling-shutter/snapshot" 14 | "github.com/shutter-network/rolling-shutter/rolling-shutter/snapshot/database" 15 | ) 16 | 17 | var ( 18 | cfgFile string 19 | outputFile string 20 | ) 21 | 22 | func Cmd() *cobra.Command { 23 | builder := command.Build( 24 | main, 25 | command.Usage( 26 | "Run the Snapshot Hub communication module", 27 | // TODO long usage description 28 | "", 29 | ), 30 | command.WithGenerateConfigSubcommand(), 31 | command.WithDumpConfigSubcommand(), 32 | ) 33 | builder.AddInitDBCommand(initDB) 34 | return builder.Command() 35 | } 36 | 37 | func initDB(cfg *snapshot.Config) error { 38 | ctx := context.Background() 39 | dbpool, err := pgxpool.Connect(ctx, cfg.DatabaseURL) 40 | if err != nil { 41 | return errors.Wrap(err, "failed to connect to database") 42 | } 43 | defer dbpool.Close() 44 | return db.InitDB(ctx, dbpool, database.Definition.Name(), database.Definition) 45 | } 46 | 47 | func main(config *snapshot.Config) error { 48 | snp, err := snapshot.New(config) 49 | if err != nil { 50 | return err 51 | } 52 | return service.RunWithSighandler(context.Background(), snp) 53 | } 54 | -------------------------------------------------------------------------------- /rolling-shutter/cmd/snapshot/snapshot_test.go: -------------------------------------------------------------------------------- 1 | package snapshot_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "gotest.tools/assert" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/test" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/snapshot" 11 | ) 12 | 13 | func TestSmokeGenerateConfig(t *testing.T) { 14 | config := snapshot.NewConfig() 15 | test.SmokeGenerateConfig(t, config) 16 | } 17 | 18 | func TestParsedConfig(t *testing.T) { 19 | config := snapshot.NewConfig() 20 | 21 | err := configuration.SetExampleValuesRecursive(config) 22 | assert.NilError(t, err) 23 | parsedConfig := test.RoundtripParseConfig(t, config) 24 | assert.DeepEqual(t, config, parsedConfig) 25 | } 26 | -------------------------------------------------------------------------------- /rolling-shutter/contract/deployment/metrics.go: -------------------------------------------------------------------------------- 1 | package deployment 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | var metricsContractDeploymentInfo = prometheus.NewGaugeVec( 6 | prometheus.GaugeOpts{ 7 | Namespace: "shutter", 8 | Subsystem: "contract", 9 | Name: "deployment_info", 10 | Help: "Information about the contract deployments in use", 11 | }, 12 | []string{"name", "address", "chainId", "blockNumber"}) 13 | 14 | func InitMetrics() { 15 | prometheus.MustRegister(metricsContractDeploymentInfo) 16 | } 17 | -------------------------------------------------------------------------------- /rolling-shutter/contract/doc.go: -------------------------------------------------------------------------------- 1 | // package contract contains the abigen generated bindings for rolling shutter's smart contracts 2 | package contract 3 | 4 | //go:generate node ../../contracts/scripts/abigen.js 5 | -------------------------------------------------------------------------------- /rolling-shutter/contract/extend.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 5 | "github.com/ethereum/go-ethereum/common" 6 | ) 7 | 8 | func (_AddrsSeq *AddrsSeqCaller) GetAddrs(opts *bind.CallOpts, n uint64) ([]common.Address, error) { 9 | numAddresses, err := _AddrsSeq.CountNth(opts, n) 10 | if err != nil { 11 | return nil, err 12 | } 13 | addresses := []common.Address{} 14 | for i := uint64(0); i < numAddresses; i++ { 15 | address, err := _AddrsSeq.At(opts, n, i) 16 | if err != nil { 17 | return nil, err 18 | } 19 | addresses = append(addresses, address) 20 | } 21 | return addresses, nil 22 | } 23 | -------------------------------------------------------------------------------- /rolling-shutter/docs/Makefile: -------------------------------------------------------------------------------- 1 | docs: rolling-shutter.md 2 | 3 | rolling-shutter.md: ../medley/rootcmd/root.go $(shell find ../cmd -iname '*.go' ! -iname '*_test.go') 4 | go run gendocs.go 5 | -------------------------------------------------------------------------------- /rolling-shutter/docs/docs.go: -------------------------------------------------------------------------------- 1 | package docs 2 | 3 | //go:generate make docs 4 | -------------------------------------------------------------------------------- /rolling-shutter/docs/gendocs.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/spf13/cobra/doc" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd" 9 | ) 10 | 11 | func main() { 12 | err := doc.GenMarkdownTree(cmd.Command(), "./") 13 | if err != nil { 14 | panic(err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_chain.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter chain 2 | 3 | Run a node for Shutter's Tendermint chain 4 | 5 | ### Synopsis 6 | 7 | This command runs a node that will connect to Shutter's Tendermint chain. 8 | 9 | ``` 10 | rolling-shutter chain [flags] 11 | ``` 12 | 13 | ### Options 14 | 15 | ``` 16 | --config string config file (required) 17 | -h, --help help for chain 18 | ``` 19 | 20 | ### Options inherited from parent commands 21 | 22 | ``` 23 | --logformat string set log format, possible values: min, short, long, max (default "long") 24 | --loglevel string set log level, possible values: warn, info, debug (default "info") 25 | --no-color do not write colored logs 26 | ``` 27 | 28 | ### SEE ALSO 29 | 30 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 31 | * [rolling-shutter chain init](rolling-shutter_chain_init.md) - Create a config file for a Shuttermint node 32 | 33 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_chain_init.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter chain init 2 | 3 | Create a config file for a Shuttermint node 4 | 5 | ``` 6 | rolling-shutter chain init [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --blocktime float block time in seconds (default 1) 13 | --dev turn on devmode (disables validator set changes) 14 | --genesis-keyper strings genesis keyper address 15 | -h, --help help for init 16 | --index int keyper index 17 | --initial-eon uint initial eon 18 | --listen-address string tendermint RPC listen address (default "tcp://127.0.0.1:26657") 19 | --role string tendermint node role (validator, isolated-validator, sentry, seed) (default "validator") 20 | --root string root directory 21 | ``` 22 | 23 | ### Options inherited from parent commands 24 | 25 | ``` 26 | --logformat string set log format, possible values: min, short, long, max (default "long") 27 | --loglevel string set log level, possible values: warn, info, debug (default "info") 28 | --no-color do not write colored logs 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [rolling-shutter chain](rolling-shutter_chain.md) - Run a node for Shutter's Tendermint chain 34 | 35 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_aggregate.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto aggregate 2 | 3 | Aggregate key shares to construct a decryption key 4 | 5 | ### Synopsis 6 | 7 | Aggregate key shares to construct a decryption key. 8 | 9 | Pass the shares as the first and only positional argument in the form of hex 10 | values separated by commas. The shares must be ordered by keyper index and 11 | missing shares must be denoted by empty strings. Exactly "threshold" shares 12 | must be provided. 13 | 14 | Example: rolling-shutter crypto aggregate -t 2 0BC5CDC5778D473B881E73297AFB830 15 | 1D35830786C6A80CD289672536655470A0149BA7394DF240C96F7D60BAF94D0FD2A39B4314088E 16 | AF94E3D1EB52106E718,,03646AE08A8EF00D0AE04294529466C0F7AC65C4D9B0ADEAD1461964A 17 | 6F784202B61B2EBD5DE8B80E787E9FD4DE4899880C2263B67EC478D88D3558B0C22DA66 18 | 19 | ``` 20 | rolling-shutter crypto aggregate [flags] 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for aggregate 27 | -t, --threshold uint threshold parameter 28 | ``` 29 | 30 | ### Options inherited from parent commands 31 | 32 | ``` 33 | --logformat string set log format, possible values: min, short, long, max (default "long") 34 | --loglevel string set log level, possible values: warn, info, debug (default "info") 35 | --no-color do not write colored logs 36 | ``` 37 | 38 | ### SEE ALSO 39 | 40 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 41 | 42 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_decrypt.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto decrypt 2 | 3 | Decrypt the message given as positional argument 4 | 5 | ``` 6 | rolling-shutter crypto decrypt [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -k, --decryption-key string decryption key (hex encoded) 13 | -h, --help help for decrypt 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_encrypt.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto encrypt 2 | 3 | Encrypt the message given as positional argument 4 | 5 | ``` 6 | rolling-shutter crypto encrypt [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -k, --eon-key string eon public key (hex encoded) 13 | -e, --epoch-id string epoch id (hex encoded) 14 | -h, --help help for encrypt 15 | -s, --sigma string sigma (optional, hex encoded) 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_jsontests.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto jsontests 2 | 3 | Use testdata in json format to test crypto implementations 4 | 5 | ``` 6 | rolling-shutter crypto jsontests [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --filename string filename to write result 13 | -h, --help help for jsontests 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_testdata.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto testdata 2 | 3 | Generate testdata in json format to test crypto implementations 4 | 5 | ``` 6 | rolling-shutter crypto testdata [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --filename string filename to write result 13 | -h, --help help for testdata 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_crypto_verify-key.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter crypto verify-key 2 | 3 | Check that the decryption key given as positional argument is correct 4 | 5 | ``` 6 | rolling-shutter crypto verify-key [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -k, --eon-key string eon public key (hex encoded) 13 | -e, --epoch-id string epoch id (hex encoded) 14 | -h, --help help for verify-key 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --logformat string set log format, possible values: min, short, long, max (default "long") 21 | --loglevel string set log level, possible values: warn, info, debug (default "info") 22 | --no-color do not write colored logs 23 | ``` 24 | 25 | ### SEE ALSO 26 | 27 | * [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions 28 | 29 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosis-access-node.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosis-access-node 2 | 3 | Run an access node for the keyper network of Shutterized Gnosis Chain 4 | 5 | ### Synopsis 6 | 7 | This command runs a node that only relays messages, but doesn't create any on 8 | its own. It is intended to be a stable node to connect to to receive messages. 9 | 10 | ``` 11 | rolling-shutter gnosis-access-node [flags] 12 | ``` 13 | 14 | ### Options 15 | 16 | ``` 17 | --config string config file 18 | -h, --help help for gnosis-access-node 19 | ``` 20 | 21 | ### Options inherited from parent commands 22 | 23 | ``` 24 | --logformat string set log format, possible values: min, short, long, max (default "long") 25 | --loglevel string set log level, possible values: warn, info, debug (default "info") 26 | --no-color do not write colored logs 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 32 | * [rolling-shutter gnosis-access-node dump-config](rolling-shutter_gnosis-access-node_dump-config.md) - Dump a 'gnosis-access-node' configuration file, based on given config and env vars 33 | * [rolling-shutter gnosis-access-node generate-config](rolling-shutter_gnosis-access-node_generate-config.md) - Generate a 'gnosis-access-node' configuration file 34 | 35 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosis-access-node_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosis-access-node dump-config 2 | 3 | Dump a 'gnosis-access-node' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter gnosis-access-node dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter gnosis-access-node](rolling-shutter_gnosis-access-node.md) - Run an access node for the keyper network of Shutterized Gnosis Chain 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosis-access-node_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosis-access-node generate-config 2 | 3 | Generate a 'gnosis-access-node' configuration file 4 | 5 | ``` 6 | rolling-shutter gnosis-access-node generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter gnosis-access-node](rolling-shutter_gnosis-access-node.md) - Run an access node for the keyper network of Shutterized Gnosis Chain 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosiskeyper.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosiskeyper 2 | 3 | Run a Shutter keyper for Gnosis Chain 4 | 5 | ### Synopsis 6 | 7 | This command runs a keyper node. It will connect to both a Gnosis and a 8 | Shuttermint node which have to be started separately in advance. 9 | 10 | ``` 11 | rolling-shutter gnosiskeyper [flags] 12 | ``` 13 | 14 | ### Options 15 | 16 | ``` 17 | --config string config file 18 | -h, --help help for gnosiskeyper 19 | ``` 20 | 21 | ### Options inherited from parent commands 22 | 23 | ``` 24 | --logformat string set log format, possible values: min, short, long, max (default "long") 25 | --loglevel string set log level, possible values: warn, info, debug (default "info") 26 | --no-color do not write colored logs 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 32 | * [rolling-shutter gnosiskeyper dump-config](rolling-shutter_gnosiskeyper_dump-config.md) - Dump a 'gnosiskeyper' configuration file, based on given config and env vars 33 | * [rolling-shutter gnosiskeyper generate-config](rolling-shutter_gnosiskeyper_generate-config.md) - Generate a 'gnosiskeyper' configuration file 34 | * [rolling-shutter gnosiskeyper initdb](rolling-shutter_gnosiskeyper_initdb.md) - Initialize the database of the 'gnosiskeyper' 35 | * [rolling-shutter gnosiskeyper watch](rolling-shutter_gnosiskeyper_watch.md) - Watch the keypers doing their work and log the generated decryption keys. 36 | 37 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosiskeyper_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosiskeyper dump-config 2 | 3 | Dump a 'gnosiskeyper' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter gnosiskeyper dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosiskeyper_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosiskeyper generate-config 2 | 3 | Generate a 'gnosiskeyper' configuration file 4 | 5 | ``` 6 | rolling-shutter gnosiskeyper generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosiskeyper_initdb.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosiskeyper initdb 2 | 3 | Initialize the database of the 'gnosiskeyper' 4 | 5 | ``` 6 | rolling-shutter gnosiskeyper initdb [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for initdb 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_gnosiskeyper_watch.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter gnosiskeyper watch 2 | 3 | Watch the keypers doing their work and log the generated decryption keys. 4 | 5 | ``` 6 | rolling-shutter gnosiskeyper watch [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for watch 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-bootstrap.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-bootstrap 2 | 3 | Bootstrap validator utility functions for a shuttermint chain 4 | 5 | ``` 6 | rolling-shutter op-bootstrap [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -h, --help help for op-bootstrap 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 27 | * [rolling-shutter op-bootstrap fetch-keyperset](rolling-shutter_op-bootstrap_fetch-keyperset.md) - fetch-keyperset 28 | * [rolling-shutter op-bootstrap generate-config](rolling-shutter_op-bootstrap_generate-config.md) - Generate a 'op-bootstrap' configuration file 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-bootstrap_fetch-keyperset.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-bootstrap fetch-keyperset 2 | 3 | fetch-keyperset 4 | 5 | ``` 6 | rolling-shutter op-bootstrap fetch-keyperset [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for fetch-keyperset 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter op-bootstrap](rolling-shutter_op-bootstrap.md) - Bootstrap validator utility functions for a shuttermint chain 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-bootstrap_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-bootstrap generate-config 2 | 3 | Generate a 'op-bootstrap' configuration file 4 | 5 | ``` 6 | rolling-shutter op-bootstrap generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter op-bootstrap](rolling-shutter_op-bootstrap.md) - Bootstrap validator utility functions for a shuttermint chain 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-keyper.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-keyper 2 | 3 | Run a Shutter optimism keyper node 4 | 5 | ### Synopsis 6 | 7 | This command runs a keyper node. It will connect to both an Optimism and a 8 | Shuttermint node which have to be started separately in advance. 9 | 10 | ``` 11 | rolling-shutter op-keyper [flags] 12 | ``` 13 | 14 | ### Options 15 | 16 | ``` 17 | --config string config file 18 | -h, --help help for op-keyper 19 | ``` 20 | 21 | ### Options inherited from parent commands 22 | 23 | ``` 24 | --logformat string set log format, possible values: min, short, long, max (default "long") 25 | --loglevel string set log level, possible values: warn, info, debug (default "info") 26 | --no-color do not write colored logs 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 32 | * [rolling-shutter op-keyper generate-config](rolling-shutter_op-keyper_generate-config.md) - Generate a 'op-keyper' configuration file 33 | * [rolling-shutter op-keyper initdb](rolling-shutter_op-keyper_initdb.md) - Initialize the database of the 'op-keyper' 34 | 35 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-keyper_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-keyper generate-config 2 | 3 | Generate a 'op-keyper' configuration file 4 | 5 | ``` 6 | rolling-shutter op-keyper generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter op-keyper](rolling-shutter_op-keyper.md) - Run a Shutter optimism keyper node 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_op-keyper_initdb.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter op-keyper initdb 2 | 3 | Initialize the database of the 'op-keyper' 4 | 5 | ``` 6 | rolling-shutter op-keyper initdb [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for initdb 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter op-keyper](rolling-shutter_op-keyper.md) - Run a Shutter optimism keyper node 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_p2pnode.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter p2pnode 2 | 3 | Run a Shutter p2p bootstrap node 4 | 5 | ``` 6 | rolling-shutter p2pnode [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -h, --help help for p2pnode 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 27 | * [rolling-shutter p2pnode dump-config](rolling-shutter_p2pnode_dump-config.md) - Dump a 'p2pnode' configuration file, based on given config and env vars 28 | * [rolling-shutter p2pnode generate-config](rolling-shutter_p2pnode_generate-config.md) - Generate a 'p2pnode' configuration file 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_p2pnode_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter p2pnode dump-config 2 | 3 | Dump a 'p2pnode' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter p2pnode dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter p2pnode](rolling-shutter_p2pnode.md) - Run a Shutter p2p bootstrap node 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_p2pnode_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter p2pnode generate-config 2 | 3 | Generate a 'p2pnode' configuration file 4 | 5 | ``` 6 | rolling-shutter p2pnode generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter p2pnode](rolling-shutter_p2pnode.md) - Run a Shutter p2p bootstrap node 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_shutterservicekeyper.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter shutterservicekeyper 2 | 3 | Run a Shutter keyper for Shutter Service 4 | 5 | ### Synopsis 6 | 7 | This command runs a keyper node. It will connect to both a Shutter service and a 8 | Shuttermint node which have to be started separately in advance. 9 | 10 | ``` 11 | rolling-shutter shutterservicekeyper [flags] 12 | ``` 13 | 14 | ### Options 15 | 16 | ``` 17 | --config string config file 18 | -h, --help help for shutterservicekeyper 19 | ``` 20 | 21 | ### Options inherited from parent commands 22 | 23 | ``` 24 | --logformat string set log format, possible values: min, short, long, max (default "long") 25 | --loglevel string set log level, possible values: warn, info, debug (default "info") 26 | --no-color do not write colored logs 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 32 | * [rolling-shutter shutterservicekeyper dump-config](rolling-shutter_shutterservicekeyper_dump-config.md) - Dump a 'shutterservicekeyper' configuration file, based on given config and env vars 33 | * [rolling-shutter shutterservicekeyper generate-config](rolling-shutter_shutterservicekeyper_generate-config.md) - Generate a 'shutterservicekeyper' configuration file 34 | * [rolling-shutter shutterservicekeyper initdb](rolling-shutter_shutterservicekeyper_initdb.md) - Initialize the database of the 'shutterservicekeyper' 35 | 36 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_shutterservicekeyper_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter shutterservicekeyper dump-config 2 | 3 | Dump a 'shutterservicekeyper' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter shutterservicekeyper dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter shutterservicekeyper](rolling-shutter_shutterservicekeyper.md) - Run a Shutter keyper for Shutter Service 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_shutterservicekeyper_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter shutterservicekeyper generate-config 2 | 3 | Generate a 'shutterservicekeyper' configuration file 4 | 5 | ``` 6 | rolling-shutter shutterservicekeyper generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter shutterservicekeyper](rolling-shutter_shutterservicekeyper.md) - Run a Shutter keyper for Shutter Service 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_shutterservicekeyper_initdb.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter shutterservicekeyper initdb 2 | 3 | Initialize the database of the 'shutterservicekeyper' 4 | 5 | ``` 6 | rolling-shutter shutterservicekeyper initdb [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for initdb 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter shutterservicekeyper](rolling-shutter_shutterservicekeyper.md) - Run a Shutter keyper for Shutter Service 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshot.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshot 2 | 3 | Run the Snapshot Hub communication module 4 | 5 | ``` 6 | rolling-shutter snapshot [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -h, --help help for snapshot 14 | ``` 15 | 16 | ### Options inherited from parent commands 17 | 18 | ``` 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 27 | * [rolling-shutter snapshot dump-config](rolling-shutter_snapshot_dump-config.md) - Dump a 'snapshot' configuration file, based on given config and env vars 28 | * [rolling-shutter snapshot generate-config](rolling-shutter_snapshot_generate-config.md) - Generate a 'snapshot' configuration file 29 | * [rolling-shutter snapshot initdb](rolling-shutter_snapshot_initdb.md) - Initialize the database of the 'snapshot' 30 | 31 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshot_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshot dump-config 2 | 3 | Dump a 'snapshot' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter snapshot dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter snapshot](rolling-shutter_snapshot.md) - Run the Snapshot Hub communication module 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshot_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshot generate-config 2 | 3 | Generate a 'snapshot' configuration file 4 | 5 | ``` 6 | rolling-shutter snapshot generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter snapshot](rolling-shutter_snapshot.md) - Run the Snapshot Hub communication module 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshot_initdb.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshot initdb 2 | 3 | Initialize the database of the 'snapshot' 4 | 5 | ``` 6 | rolling-shutter snapshot initdb [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for initdb 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter snapshot](rolling-shutter_snapshot.md) - Run the Snapshot Hub communication module 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshotkeyper.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshotkeyper 2 | 3 | Run a Shutter snapshotkeyper node 4 | 5 | ### Synopsis 6 | 7 | This command runs a keyper node. It will connect to both an Ethereum and a 8 | Shuttermint node which have to be started separately in advance. 9 | 10 | ``` 11 | rolling-shutter snapshotkeyper [flags] 12 | ``` 13 | 14 | ### Options 15 | 16 | ``` 17 | --config string config file 18 | -h, --help help for snapshotkeyper 19 | ``` 20 | 21 | ### Options inherited from parent commands 22 | 23 | ``` 24 | --logformat string set log format, possible values: min, short, long, max (default "long") 25 | --loglevel string set log level, possible values: warn, info, debug (default "info") 26 | --no-color do not write colored logs 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes 32 | * [rolling-shutter snapshotkeyper dump-config](rolling-shutter_snapshotkeyper_dump-config.md) - Dump a 'snapshotkeyper' configuration file, based on given config and env vars 33 | * [rolling-shutter snapshotkeyper generate-config](rolling-shutter_snapshotkeyper_generate-config.md) - Generate a 'snapshotkeyper' configuration file 34 | * [rolling-shutter snapshotkeyper initdb](rolling-shutter_snapshotkeyper_initdb.md) - Initialize the database of the 'snapshotkeyper' 35 | 36 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshotkeyper_dump-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshotkeyper dump-config 2 | 3 | Dump a 'snapshotkeyper' configuration file, based on given config and env vars 4 | 5 | ``` 6 | rolling-shutter snapshotkeyper dump-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | --config string config file 13 | -f, --force overwrite existing file 14 | -h, --help help for dump-config 15 | --output string output file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter snapshotkeyper](rolling-shutter_snapshotkeyper.md) - Run a Shutter snapshotkeyper node 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshotkeyper_generate-config.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshotkeyper generate-config 2 | 3 | Generate a 'snapshotkeyper' configuration file 4 | 5 | ``` 6 | rolling-shutter snapshotkeyper generate-config [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -f, --force overwrite existing file 13 | -h, --help help for generate-config 14 | --output string output file 15 | ``` 16 | 17 | ### Options inherited from parent commands 18 | 19 | ``` 20 | --config string config file 21 | --logformat string set log format, possible values: min, short, long, max (default "long") 22 | --loglevel string set log level, possible values: warn, info, debug (default "info") 23 | --no-color do not write colored logs 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [rolling-shutter snapshotkeyper](rolling-shutter_snapshotkeyper.md) - Run a Shutter snapshotkeyper node 29 | 30 | -------------------------------------------------------------------------------- /rolling-shutter/docs/rolling-shutter_snapshotkeyper_initdb.md: -------------------------------------------------------------------------------- 1 | ## rolling-shutter snapshotkeyper initdb 2 | 3 | Initialize the database of the 'snapshotkeyper' 4 | 5 | ``` 6 | rolling-shutter snapshotkeyper initdb [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -h, --help help for initdb 13 | ``` 14 | 15 | ### Options inherited from parent commands 16 | 17 | ``` 18 | --config string config file 19 | --logformat string set log format, possible values: min, short, long, max (default "long") 20 | --loglevel string set log level, possible values: warn, info, debug (default "info") 21 | --no-color do not write colored logs 22 | ``` 23 | 24 | ### SEE ALSO 25 | 26 | * [rolling-shutter snapshotkeyper](rolling-shutter_snapshotkeyper.md) - Run a Shutter snapshotkeyper node 27 | 28 | -------------------------------------------------------------------------------- /rolling-shutter/gnosisaccessnode/storage.go: -------------------------------------------------------------------------------- 1 | package gnosisaccessnode 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/shutter-network/shutter/shlib/shcrypto" 7 | 8 | obskeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db/keyper" 9 | ) 10 | 11 | type Storage struct { 12 | mu sync.Mutex 13 | eonKeys map[uint64]*shcrypto.EonPublicKey 14 | keyperSets map[uint64]*obskeyperdatabase.KeyperSet 15 | } 16 | 17 | func NewStorage() *Storage { 18 | return &Storage{ 19 | mu: sync.Mutex{}, 20 | eonKeys: make(map[uint64]*shcrypto.EonPublicKey), 21 | keyperSets: make(map[uint64]*obskeyperdatabase.KeyperSet), 22 | } 23 | } 24 | 25 | func (s *Storage) AddEonKey(keyperConfigIndex uint64, key *shcrypto.EonPublicKey) { 26 | s.mu.Lock() 27 | defer s.mu.Unlock() 28 | 29 | s.eonKeys[keyperConfigIndex] = key 30 | } 31 | 32 | func (s *Storage) GetEonKey(keyperConfigIndex uint64) (*shcrypto.EonPublicKey, bool) { 33 | s.mu.Lock() 34 | defer s.mu.Unlock() 35 | 36 | v, ok := s.eonKeys[keyperConfigIndex] 37 | return v, ok 38 | } 39 | 40 | func (s *Storage) AddKeyperSet(keyperConfigIndex uint64, keyperSet *obskeyperdatabase.KeyperSet) { 41 | s.mu.Lock() 42 | defer s.mu.Unlock() 43 | 44 | s.keyperSets[keyperConfigIndex] = keyperSet 45 | } 46 | 47 | func (s *Storage) GetKeyperSet(keyperConfigIndex uint64) (*obskeyperdatabase.KeyperSet, bool) { 48 | s.mu.Lock() 49 | defer s.mu.Unlock() 50 | 51 | v, ok := s.keyperSets[keyperConfigIndex] 52 | return v, ok 53 | } 54 | -------------------------------------------------------------------------------- /rolling-shutter/gnosiskeyperwatcher/watcher.go: -------------------------------------------------------------------------------- 1 | package gnosiskeyperwatcher 2 | 3 | import ( 4 | "context" 5 | 6 | keyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis" 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 8 | ) 9 | 10 | type Watcher struct { 11 | config *keyper.Config 12 | } 13 | 14 | func New(config *keyper.Config) *Watcher { 15 | return &Watcher{ 16 | config: config, 17 | } 18 | } 19 | 20 | func (w *Watcher) Start(_ context.Context, runner service.Runner) error { 21 | blocksChannel := make(chan *BlockReceivedEvent) 22 | blocksWatcher := NewBlocksWatcher(w.config, blocksChannel) 23 | keysWatcher := NewKeysWatcher(w.config, blocksChannel) 24 | return runner.StartService(blocksWatcher, keysWatcher) 25 | } 26 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/database/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/database/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/epochkghandler/eonpublickey.go: -------------------------------------------------------------------------------- 1 | package epochkghandler 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v4/pgxpool" 7 | pubsub "github.com/libp2p/go-libp2p-pubsub" 8 | "github.com/pkg/errors" 9 | 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" 12 | ) 13 | 14 | func NewEonPublicKeyHandler(config Config, _ *pgxpool.Pool) p2p.MessageHandler { 15 | return &EonPublicKeyHandler{config: config} 16 | } 17 | 18 | type EonPublicKeyHandler struct { 19 | config Config 20 | } 21 | 22 | func (*EonPublicKeyHandler) MessagePrototypes() []p2pmsg.Message { 23 | return []p2pmsg.Message{&p2pmsg.EonPublicKey{}} 24 | } 25 | 26 | func (handler *EonPublicKeyHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) { 27 | key := msg.(*p2pmsg.EonPublicKey) 28 | if key.GetInstanceId() != handler.config.GetInstanceID() { 29 | return pubsub.ValidationReject, 30 | errors.Errorf("instance ID mismatch (want=%d, have=%d)", handler.config.GetInstanceID(), key.GetInstanceId()) 31 | } 32 | return pubsub.ValidationAccept, nil 33 | } 34 | 35 | func (handler *EonPublicKeyHandler) HandleMessage(context.Context, p2pmsg.Message) ([]p2pmsg.Message, error) { 36 | return nil, nil 37 | } 38 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/epochkghandler/setup_test.go: -------------------------------------------------------------------------------- 1 | package epochkghandler 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testsetup" 9 | ) 10 | 11 | type TestConfig struct{} 12 | 13 | var config = &TestConfig{} 14 | 15 | func (TestConfig) GetAddress() common.Address { 16 | return common.HexToAddress("0x2222222222222222222222222222222222222222") 17 | } 18 | 19 | func (TestConfig) GetInstanceID() uint64 { 20 | return 55 21 | } 22 | 23 | func (TestConfig) GetEon() uint64 { 24 | return 22 25 | } 26 | 27 | func (c *TestConfig) GetCollatorKey() *ecdsa.PrivateKey { 28 | return nil 29 | } 30 | 31 | func (c *TestConfig) GetMaxNumKeysPerMessage() uint64 { 32 | return 1024 33 | } 34 | 35 | var _ testsetup.TestConfig = &TestConfig{} 36 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/kproapi/doc.go: -------------------------------------------------------------------------------- 1 | // package kproapi implements the keypers's openapi interface 2 | package kproapi 3 | 4 | //go:generate oapi-codegen --generate types,chi-server,spec -o oapi.gen.go --package kproapi oapi.yaml 5 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/kprtopics/kprtopics.go: -------------------------------------------------------------------------------- 1 | package kprtopics 2 | 3 | const ( 4 | DecryptionTrigger = "decryptionTrigger" 5 | DecryptionKeys = "decryptionKeys" 6 | DecryptionKeyShares = "decryptionKeyShares" 7 | EonPublicKey = "EonPublicKey" 8 | ) 9 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/shutterevents/evtype/evtype.go: -------------------------------------------------------------------------------- 1 | // evtype declares the the different event types sent by shuttermint 2 | package evtype 3 | 4 | var ( 5 | Accusation = "shutter.accusation-registered" 6 | Apology = "shutter.apology-registered" 7 | BatchConfig = "shutter.batch-config" 8 | BatchConfigStarted = "shutter.batch-config-started" 9 | CheckIn = "shutter.check-in" 10 | EonStarted = "shutter.eon-started" 11 | PolyCommitment = "shutter.poly-commitment-registered" 12 | PolyEval = "shutter.poly-eval-registered" 13 | ) 14 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/shutterevents/helpers.go: -------------------------------------------------------------------------------- 1 | package shutterevents 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | abcitypes "github.com/tendermint/tendermint/abci/types" 6 | 7 | "github.com/shutter-network/shutter/shlib/shcrypto" 8 | ) 9 | 10 | // 11 | // Encoding/decoding helpers 12 | // 13 | 14 | func newAddressPair(key string, value common.Address) abcitypes.EventAttribute { //nolint:unparam 15 | return abcitypes.EventAttribute{ 16 | Key: key, 17 | Value: encodeAddress(value), 18 | Index: true, 19 | } 20 | } 21 | 22 | func newAddressesPair(key string, value []common.Address) abcitypes.EventAttribute { 23 | return abcitypes.EventAttribute{ 24 | Key: key, 25 | Value: encodeAddresses(value), 26 | } 27 | } 28 | 29 | func newByteSequencePair(key string, value [][]byte) abcitypes.EventAttribute { 30 | return abcitypes.EventAttribute{ 31 | Key: key, 32 | Value: encodeByteSequence(value), 33 | } 34 | } 35 | 36 | func newUintPair(key string, value uint64) abcitypes.EventAttribute { 37 | return abcitypes.EventAttribute{ 38 | Key: key, 39 | Value: encodeUint64(value), 40 | Index: true, 41 | } 42 | } 43 | 44 | func newGammas(key string, gammas *shcrypto.Gammas) abcitypes.EventAttribute { 45 | return abcitypes.EventAttribute{ 46 | Key: key, 47 | Value: encodeGammas(gammas), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rolling-shutter/keyper/shutterevents/shtxresp/code.go: -------------------------------------------------------------------------------- 1 | // Used for the return codes between shuttermint and tx senders (e.g. Keyper, etc.) 2 | package shtxresp 3 | 4 | const ( 5 | Ok uint32 = iota 6 | Error 7 | Seen 8 | ) 9 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/database/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/database/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 10 | ) 11 | 12 | //go:generate sqlc generate --file sql/sqlc.yaml 13 | 14 | //go:embed sql 15 | var files embed.FS 16 | 17 | var Definition db.Definition 18 | 19 | func init() { 20 | def, err := db.NewSQLCDefinition(files, "sql/", "gnosiskeyper", 1) 21 | if err != nil { 22 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 23 | } 24 | Definition = db.NewAggregateDefinition( 25 | "gnosiskeyper", 26 | def, 27 | database.Definition, 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/database/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/identitieshash.go: -------------------------------------------------------------------------------- 1 | package gnosis 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/crypto" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" 8 | ) 9 | 10 | func computeIdentitiesHash(identityPreimages []identitypreimage.IdentityPreimage) []byte { 11 | identityPreimagesAsBytes := [][]byte{} 12 | for _, preimage := range identityPreimages { 13 | identityPreimagesAsBytes = append(identityPreimagesAsBytes, preimage) 14 | } 15 | return crypto.Keccak256(identityPreimagesAsBytes...) 16 | } 17 | 18 | func computeIdentitiesHashFromShares(shares []*p2pmsg.KeyShare) []byte { 19 | identityPreimges := []identitypreimage.IdentityPreimage{} 20 | for _, share := range shares { 21 | identityPreimges = append(identityPreimges, identitypreimage.IdentityPreimage(share.IdentityPreimage)) 22 | } 23 | return computeIdentitiesHash(identityPreimges) 24 | } 25 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/newblock.go: -------------------------------------------------------------------------------- 1 | package gnosis 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" 7 | syncevent "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" 8 | ) 9 | 10 | func (kpr *Keyper) processNewBlock(ctx context.Context, ev *syncevent.LatestBlock) error { 11 | if kpr.sequencerSyncer != nil { 12 | if err := kpr.sequencerSyncer.Sync(ctx, ev.Header); err != nil { 13 | return err 14 | } 15 | } 16 | err := kpr.validatorSyncer.Sync(ctx, ev.Header) 17 | if err != nil { 18 | return err 19 | } 20 | slot := medley.BlockTimestampToSlot( 21 | ev.Header.Time, 22 | kpr.config.Gnosis.GenesisSlotTimestamp, 23 | kpr.config.Gnosis.SecondsPerSlot, 24 | ) 25 | return kpr.maybeTriggerDecryption(ctx, slot+1) 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/gnosis/neweonpublickey.go: -------------------------------------------------------------------------------- 1 | package gnosis 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" 7 | ) 8 | 9 | func (kpr *Keyper) processNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { //nolint: unparam 10 | kpr.eonKeyPublisher.Publish(key) 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/optimism/bootstrap/keyperset.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "os" 8 | 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync/event" 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" 12 | ) 13 | 14 | func GetKeyperSet(ctx context.Context, config *Config) error { 15 | sl2, err := chainsync.NewClient( 16 | ctx, 17 | chainsync.WithClientURL(config.JSONRPCURL), 18 | chainsync.WithKeyperSetManager(config.KeyperSetManager), 19 | ) 20 | if err != nil { 21 | return err 22 | } 23 | var keyperSet *event.KeyperSet 24 | switch { 25 | case config.ByIndex == nil && config.ByActivationBlockNumber == nil: 26 | keyperSet, err = sl2.GetKeyperSetForBlock(ctx, number.LatestBlock) 27 | case config.ByIndex == nil && config.ByActivationBlockNumber != nil: 28 | keyperSet, err = sl2.GetKeyperSetForBlock(ctx, config.ByActivationBlockNumber) 29 | case config.ByIndex != nil && config.ByActivationBlockNumber == nil: 30 | keyperSet, err = sl2.GetKeyperSetByIndex(ctx, *config.ByIndex) 31 | case config.ByIndex != nil && config.ByActivationBlockNumber != nil: 32 | return errors.New("can only retrieve keyper set by either index or activation-blocknumber") 33 | default: 34 | return nil 35 | } 36 | if err != nil { 37 | return err 38 | } 39 | file, _ := json.MarshalIndent(keyperSet, "", " ") 40 | return os.WriteFile(config.KeyperSetFilePath, file, 0o644) 41 | } 42 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/optimism/config/ethereum.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" 10 | ) 11 | 12 | var _ configuration.Config = &OptimismConfig{} 13 | 14 | func NewEthnodeConfig() *OptimismConfig { 15 | c := &OptimismConfig{} 16 | c.Init() 17 | return c 18 | } 19 | 20 | type OptimismConfig struct { 21 | PrivateKey *keys.ECDSAPrivate `shconfig:",required"` 22 | JSONRPCURL string ` comment:"The op-geth JSON RPC endpoint"` 23 | } 24 | 25 | func (c *OptimismConfig) Init() { 26 | c.PrivateKey = &keys.ECDSAPrivate{} 27 | } 28 | 29 | func (c *OptimismConfig) Name() string { 30 | return "ethnode" 31 | } 32 | 33 | func (c *OptimismConfig) Validate() error { 34 | return nil 35 | } 36 | 37 | func (c *OptimismConfig) SetDefaultValues() error { 38 | c.JSONRPCURL = "http://127.0.0.1:8545/" 39 | return nil 40 | } 41 | 42 | func (c *OptimismConfig) SetExampleValues() error { 43 | err := c.SetDefaultValues() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | c.PrivateKey, err = keys.GenerateECDSAKey(rand.Reader) 49 | if err != nil { 50 | return err 51 | } 52 | return nil 53 | } 54 | 55 | func (c OptimismConfig) TOMLWriteHeader(w io.Writer) (int, error) { 56 | return fmt.Fprintf(w, "# Ethereum address: %s\n", c.PrivateKey.EthereumAddress()) 57 | } 58 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/optimism/database/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" 5 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 6 | ) 7 | 8 | var Definition = db.NewAggregateDefinition( 9 | "opkeyper", 10 | database.Definition, 11 | ) 12 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/optimism/logger.go: -------------------------------------------------------------------------------- 1 | package optimism 2 | 3 | // TODO: wrap the zerolog logger. 4 | type LoggerAdapter struct{} 5 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/optimism/setup_test.go: -------------------------------------------------------------------------------- 1 | package optimism 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 8 | "github.com/pkg/errors" 9 | 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testsetup" 11 | ) 12 | 13 | func init() { 14 | var err error 15 | testConfig.collatorKey, err = ethcrypto.GenerateKey() 16 | if err != nil { 17 | panic(errors.Wrap(err, "ethcrypto.GenerateKey failed")) 18 | } 19 | } 20 | 21 | type TestConfig struct { 22 | collatorKey *ecdsa.PrivateKey 23 | } 24 | 25 | var testConfig = &TestConfig{} 26 | 27 | func (TestConfig) GetAddress() common.Address { 28 | return common.HexToAddress("0x2222222222222222222222222222222222222222") 29 | } 30 | 31 | func (TestConfig) GetInstanceID() uint64 { 32 | return 55 33 | } 34 | 35 | func (TestConfig) GetEon() uint64 { 36 | return 22 37 | } 38 | 39 | func (c *TestConfig) GetCollatorKey() *ecdsa.PrivateKey { 40 | return testConfig.collatorKey 41 | } 42 | 43 | var _ testsetup.TestConfig = &TestConfig{} 44 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/database/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/database/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 10 | ) 11 | 12 | //go:generate sqlc generate --file sql/sqlc.yaml 13 | 14 | //go:embed sql 15 | var files embed.FS 16 | 17 | var Definition db.Definition 18 | 19 | func init() { 20 | def, err := db.NewSQLCDefinition(files, "sql/", "shutterservicekeyper", 1) 21 | if err != nil { 22 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 23 | } 24 | Definition = db.NewAggregateDefinition( 25 | "shutterservicekeyper", 26 | def, 27 | database.Definition, 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/database/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | type CurrentDecryptionTrigger struct { 8 | Eon int64 9 | TriggeredBlockNumber int64 10 | IdentitiesHash []byte 11 | } 12 | 13 | type DecryptionSignature struct { 14 | Eon int64 15 | KeyperIndex int64 16 | IdentitiesHash []byte 17 | Signature []byte 18 | } 19 | 20 | type IdentityRegisteredEvent struct { 21 | BlockNumber int64 22 | BlockHash []byte 23 | TxIndex int64 24 | LogIndex int64 25 | Eon int64 26 | IdentityPrefix []byte 27 | Sender string 28 | Timestamp int64 29 | Decrypted bool 30 | Identity []byte 31 | } 32 | 33 | type IdentityRegisteredEventsSyncedUntil struct { 34 | EnforceOneRow bool 35 | BlockHash []byte 36 | BlockNumber int64 37 | } 38 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/database/sql/schemas/shutterservice.sql: -------------------------------------------------------------------------------- 1 | -- schema-version: shutterservice-1 -- 2 | -- Please change the version above if you make incompatible changes to 3 | -- the schema. We'll use this to check we're using the right schema. 4 | 5 | CREATE TABLE identity_registered_event ( 6 | block_number bigint NOT NULL CHECK (block_number >= 0), 7 | block_hash bytea NOT NULL, 8 | tx_index bigint NOT NULL CHECK (tx_index >= 0), 9 | log_index bigint NOT NULL CHECK (log_index >= 0), 10 | eon bigint NOT NULL CHECK (eon >= 0), 11 | identity_prefix bytea NOT NULL, 12 | sender text NOT NULL, 13 | timestamp bigint NOT NULL, 14 | decrypted boolean NOT NULL DEFAULT false, 15 | identity bytea NOT NULL, 16 | PRIMARY KEY (identity_prefix, sender) 17 | ); 18 | 19 | CREATE TABLE identity_registered_events_synced_until( 20 | enforce_one_row bool PRIMARY KEY DEFAULT true, 21 | block_hash bytea NOT NULL, 22 | block_number bigint NOT NULL CHECK (block_number >= 0) 23 | ); 24 | 25 | CREATE TABLE current_decryption_trigger( 26 | eon bigint CHECK (eon >= 0), 27 | triggered_block_number bigint NOT NULL CHECK (triggered_block_number >= 0), 28 | identities_hash bytea NOT NULL, 29 | PRIMARY KEY (eon, triggered_block_number) 30 | ); 31 | 32 | CREATE TABLE decryption_signatures( 33 | eon bigint NOT NULL CHECK (eon >= 0), 34 | keyper_index bigint NOT NULL, 35 | identities_hash bytea NOT NULL, 36 | signature bytea NOT NULL, 37 | PRIMARY KEY (eon, keyper_index, identities_hash) 38 | ); -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/database/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/identitieshash.go: -------------------------------------------------------------------------------- 1 | package shutterservice 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/crypto" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" 8 | ) 9 | 10 | func computeIdentitiesHash(identityPreimages []identitypreimage.IdentityPreimage) []byte { 11 | identityPreimagesAsBytes := [][]byte{} 12 | for _, preimage := range identityPreimages { 13 | identityPreimagesAsBytes = append(identityPreimagesAsBytes, preimage) 14 | } 15 | return crypto.Keccak256(identityPreimagesAsBytes...) 16 | } 17 | 18 | func computeIdentitiesHashFromShares(shares []*p2pmsg.KeyShare) []byte { 19 | identityPreimges := []identitypreimage.IdentityPreimage{} 20 | for _, share := range shares { 21 | identityPreimges = append(identityPreimges, identitypreimage.IdentityPreimage(share.IdentityPreimage)) 22 | } 23 | return computeIdentitiesHash(identityPreimges) 24 | } 25 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/metrics.go: -------------------------------------------------------------------------------- 1 | package shutterservice 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | var metricsRegistryEventsSyncedUntil = prometheus.NewGauge( 6 | prometheus.GaugeOpts{ 7 | Namespace: "shutter", 8 | Subsystem: "shutter_api", 9 | Name: "registry_events_syned_until", 10 | Help: "Current value of the latest block fetched", 11 | }, 12 | ) 13 | 14 | func init() { 15 | prometheus.MustRegister(metricsRegistryEventsSyncedUntil) 16 | } 17 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/neweonpublickey.go: -------------------------------------------------------------------------------- 1 | package shutterservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper" 7 | ) 8 | 9 | func (kpr *Keyper) processNewEonPublicKey(_ context.Context, key keyper.EonPublicKey) error { //nolint: unparam 10 | kpr.eonKeyPublisher.Publish(key) 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/shutterservice/setup_test.go: -------------------------------------------------------------------------------- 1 | package shutterservice 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testsetup" 9 | ) 10 | 11 | type TestConfig struct{} 12 | 13 | var config = &TestConfig{} 14 | 15 | func (TestConfig) GetAddress() common.Address { 16 | return common.HexToAddress("0x2222222222222222222222222222222222222222") 17 | } 18 | 19 | func (TestConfig) GetInstanceID() uint64 { 20 | return 55 21 | } 22 | 23 | func (TestConfig) GetEon() uint64 { 24 | return 22 25 | } 26 | 27 | func (c *TestConfig) GetCollatorKey() *ecdsa.PrivateKey { 28 | return nil 29 | } 30 | 31 | func (c *TestConfig) GetMaxNumKeysPerMessage() uint64 { 32 | return 1024 33 | } 34 | 35 | var _ testsetup.TestConfig = &TestConfig{} 36 | -------------------------------------------------------------------------------- /rolling-shutter/keyperimpl/snapshot/database/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | chainobsdb "github.com/shutter-network/rolling-shutter/rolling-shutter/chainobserver/db" 5 | "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database" 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 7 | ) 8 | 9 | var Definition = db.NewAggregateDefinition( 10 | "snapshot", 11 | database.Definition, 12 | chainobsdb.CollatorDefinition, 13 | ) 14 | -------------------------------------------------------------------------------- /rolling-shutter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/shutter-network/rolling-shutter/rolling-shutter/cmd" 5 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/rootcmd" 6 | ) 7 | 8 | func main() { 9 | rootcmd.Main(cmd.Command()) 10 | } 11 | -------------------------------------------------------------------------------- /rolling-shutter/medley/beaconapiclient/beaconapiclient.go: -------------------------------------------------------------------------------- 1 | package beaconapiclient 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | type Client struct { 9 | c *http.Client 10 | url *url.URL 11 | } 12 | 13 | func New(rawURL string) (*Client, error) { 14 | parsedURL, err := url.Parse(rawURL) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return &Client{ 19 | c: &http.Client{}, 20 | url: parsedURL, 21 | }, nil 22 | } 23 | -------------------------------------------------------------------------------- /rolling-shutter/medley/broker/event.go: -------------------------------------------------------------------------------- 1 | package broker 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var ErrResultAlreadySet = errors.New("result has already been set") 10 | 11 | func NewEvent[T any](value T) *Event[T] { 12 | return &Event[T]{ 13 | Value: value, 14 | Time: time.Now(), 15 | resultC: make(chan *Result, 1), 16 | resMux: &sync.Mutex{}, 17 | } 18 | } 19 | 20 | type Result struct { 21 | OK bool 22 | Error error 23 | Time time.Time 24 | Where string 25 | } 26 | 27 | func (r *Result) Set(err error) { 28 | r.OK = bool(err == nil) 29 | r.Error = err 30 | r.Time = time.Now() 31 | } 32 | 33 | type Event[T any] struct { 34 | Value T 35 | Time time.Time 36 | 37 | resMux *sync.Mutex 38 | result *Result 39 | resultC chan *Result 40 | } 41 | 42 | func (e *Event[T]) Result() <-chan *Result { 43 | return e.resultC 44 | } 45 | 46 | func (e *Event[T]) setResult(err error) error { 47 | e.resMux.Lock() 48 | defer e.resMux.Unlock() 49 | if e.result != nil { 50 | return ErrResultAlreadySet 51 | } 52 | e.result = &Result{} 53 | e.result.Set(err) 54 | return nil 55 | } 56 | 57 | func (e *Event[T]) SetResult(err error) error { 58 | if err := e.setResult(err); err != nil { 59 | return err 60 | } 61 | // capacity 1 and empty channel, 62 | // so this can never block 63 | e.resultC <- e.result 64 | return nil 65 | } 66 | 67 | func (e *Event[T]) IsRecent(delta time.Duration) bool { 68 | return e.Time.After(time.Now().Add(-delta)) 69 | } 70 | -------------------------------------------------------------------------------- /rolling-shutter/medley/chainsync/event/events.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/core/types" 6 | 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/number" 8 | ) 9 | 10 | type ( 11 | KeyperSet struct { 12 | ActivationBlock uint64 13 | Members []common.Address 14 | Threshold uint64 15 | Eon uint64 16 | 17 | AtBlockNumber *number.BlockNumber `json:",omitempty"` 18 | } 19 | EonPublicKey struct { 20 | Eon uint64 21 | Key []byte 22 | 23 | AtBlockNumber *number.BlockNumber 24 | } 25 | ShutterState struct { 26 | Active bool 27 | 28 | AtBlockNumber *number.BlockNumber `json:",omitempty"` 29 | } 30 | LatestBlock struct { 31 | Number *number.BlockNumber 32 | BlockHash common.Hash 33 | Header *types.Header 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /rolling-shutter/medley/chainsync/event/handler.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import "context" 4 | 5 | type ( 6 | KeyperSetHandler func(context.Context, *KeyperSet) error 7 | EonPublicKeyHandler func(context.Context, *EonPublicKey) error 8 | BlockHandler func(context.Context, *LatestBlock) error 9 | ShutterStateHandler func(context.Context, *ShutterState) error 10 | ) 11 | -------------------------------------------------------------------------------- /rolling-shutter/medley/channel/channel.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import "context" 4 | 5 | func Forward[T any](ctx context.Context, 6 | receive <-chan T, 7 | send chan<- T, 8 | ) error { 9 | for { 10 | select { 11 | case val, ok := <-receive: 12 | if !ok { 13 | return nil 14 | } 15 | send <- val 16 | case <-ctx.Done(): 17 | return ctx.Err() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rolling-shutter/medley/channel/fanin.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 8 | ) 9 | 10 | func NewFanInService[T any](in ...<-chan T) *FanInService[T] { 11 | ch := make(chan T) 12 | return &FanInService[T]{ 13 | inBounds: in, 14 | outBound: ch, 15 | C: ch, 16 | } 17 | } 18 | 19 | type FanInService[T any] struct { 20 | inBounds []<-chan T 21 | outBound chan T 22 | C <-chan T 23 | } 24 | 25 | func (p *FanInService[T]) Start(ctx context.Context, runner service.Runner) error { 26 | wg := new(sync.WaitGroup) 27 | 28 | for _, inBound := range p.inBounds { 29 | wg.Add(1) 30 | // avoid capture of iteration variables 31 | in := inBound 32 | out := p.outBound 33 | runner.Go(func() error { 34 | defer wg.Done() 35 | return Forward(ctx, in, out) 36 | }) 37 | } 38 | runner.Go(func() error { 39 | wg.Wait() 40 | close(p.outBound) 41 | return nil 42 | }) 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /rolling-shutter/medley/channel/fanout.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 8 | ) 9 | 10 | func NewFanOutService[T any](in <-chan T, out ...chan T) *FanOutService[T] { 11 | return &FanOutService[T]{ 12 | inBound: in, 13 | outBounds: out, 14 | } 15 | } 16 | 17 | // FanOutService takes input from one channel and will distribute 18 | // it across multiple downstream out channels. 19 | // This will fan-out the values, but incoming values will only 20 | // be forwarded to ONE outbound channel. 21 | // So this is more reminiscent of a load balancer, 22 | // NOT a subscription service. 23 | type FanOutService[T any] struct { 24 | inBound <-chan T 25 | outBounds []chan T 26 | } 27 | 28 | func (p *FanOutService[T]) Start(ctx context.Context, runner service.Runner) error { 29 | wg := new(sync.WaitGroup) 30 | for _, outBound := range p.outBounds { 31 | wg.Add(1) 32 | runner.Go(func() error { 33 | defer wg.Done() 34 | in := p.inBound 35 | out := outBound 36 | return Forward(ctx, in, out) 37 | }) 38 | } 39 | runner.Go(func() error { 40 | wg.Wait() 41 | for _, outBound := range p.outBounds { 42 | close(outBound) 43 | } 44 | return nil 45 | }) 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /rolling-shutter/medley/comparer/comparer.go: -------------------------------------------------------------------------------- 1 | package comparer 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "time" 7 | 8 | "github.com/ethereum/go-ethereum/crypto/ecies" 9 | gocmp "github.com/google/go-cmp/cmp" 10 | p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" 11 | ) 12 | 13 | // P2PPrivKeyComparer is a gocmp comparer for use with gotest.tools/assert.DeepEqual. 14 | var P2PPrivKeyComparer = gocmp.Comparer(func(k1, k2 p2pcrypto.PrivKey) bool { 15 | d1, _ := p2pcrypto.MarshalPrivateKey(k1) 16 | d2, _ := p2pcrypto.MarshalPrivateKey(k2) 17 | return bytes.Equal(d1, d2) 18 | }) 19 | 20 | var EciesPublicKeyComparer = gocmp.Comparer(func(x, y *ecies.PublicKey) bool { 21 | return reflect.DeepEqual(x, y) 22 | }) 23 | 24 | var EciesPrivateKeyComparer = gocmp.Comparer(func(x, y *ecies.PrivateKey) bool { 25 | return reflect.DeepEqual(x, y) 26 | }) 27 | 28 | var ( 29 | DurationComparer5MsDeviation = MakeDurationComparer(5 * time.Millisecond) 30 | DurationComparerStrict = MakeDurationComparer(0 * time.Millisecond) 31 | ) 32 | 33 | func MakeDurationComparer(allowedDeviation time.Duration) gocmp.Option { 34 | return gocmp.Comparer( 35 | func(a, b time.Duration) bool { 36 | var d int64 37 | aNs := a.Abs().Nanoseconds() 38 | bNs := b.Abs().Nanoseconds() 39 | if aNs <= bNs { 40 | d = bNs - aNs 41 | } else { 42 | d = aNs - bNs 43 | } 44 | return d <= allowedDeviation.Nanoseconds() 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /rolling-shutter/medley/configuration/ethereum.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable/keys" 9 | ) 10 | 11 | var _ Config = &EthnodeConfig{} 12 | 13 | func NewEthnodeConfig() *EthnodeConfig { 14 | c := &EthnodeConfig{} 15 | c.Init() 16 | return c 17 | } 18 | 19 | type EthnodeConfig struct { 20 | PrivateKey *keys.ECDSAPrivate `shconfig:",required"` 21 | DeploymentDir string ` comment:"Contract source directory"` 22 | EthereumURL string ` comment:"The layer 1 JSON RPC endpoint"` 23 | } 24 | 25 | func (c *EthnodeConfig) Init() { 26 | c.PrivateKey = &keys.ECDSAPrivate{} 27 | } 28 | 29 | func (c *EthnodeConfig) Name() string { 30 | return "ethnode" 31 | } 32 | 33 | func (c *EthnodeConfig) Validate() error { 34 | return nil 35 | } 36 | 37 | func (c *EthnodeConfig) SetDefaultValues() error { 38 | c.EthereumURL = "http://127.0.0.1:8545/" 39 | c.DeploymentDir = "./deployments/localhost/" 40 | return nil 41 | } 42 | 43 | func (c *EthnodeConfig) SetExampleValues() error { 44 | err := c.SetDefaultValues() 45 | if err != nil { 46 | return err 47 | } 48 | 49 | c.PrivateKey, err = keys.GenerateECDSAKey(rand.Reader) 50 | if err != nil { 51 | return err 52 | } 53 | return nil 54 | } 55 | 56 | func (c EthnodeConfig) TOMLWriteHeader(w io.Writer) (int, error) { 57 | return fmt.Fprintf(w, "# Ethereum address: %s\n", c.PrivateKey.EthereumAddress()) 58 | } 59 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/connect.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v4" 7 | "github.com/jackc/pgx/v4/pgxpool" 8 | "github.com/pkg/errors" 9 | "github.com/rs/zerolog/log" 10 | 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/shdb" 13 | ) 14 | 15 | func ValidateDBVersion(ctx context.Context, dbpool *pgxpool.Pool, role string) error { 16 | err := dbpool.BeginFunc( 17 | ctx, 18 | func(tx pgx.Tx) error { 19 | return ValidateDatabaseVersion(ctx, tx, role) 20 | }, 21 | ) 22 | if err != nil { 23 | return errors.Wrap(err, "database is used for a different role already, preventing overwrite") 24 | } 25 | return nil 26 | } 27 | 28 | // Connect to the database `url` from within a runner.Start() method 29 | // and create the pgxpool.Pool. 30 | func Connect(ctx context.Context, runner service.Runner, url, version string) (*pgxpool.Pool, error) { 31 | dbpool, err := pgxpool.Connect(ctx, url) 32 | if err != nil { 33 | return nil, err 34 | } 35 | runner.Defer(dbpool.Close) 36 | 37 | err = ValidateDBVersion(ctx, dbpool, version) 38 | if err != nil { 39 | return nil, err 40 | } 41 | shdb.AddConnectionInfo(log.Info(), dbpool).Msg("connected to database") 42 | return dbpool, nil 43 | } 44 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package db 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/definition.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | ) 8 | 9 | //go:generate sqlc generate --file sql/sqlc.yaml 10 | 11 | //go:embed sql 12 | var files embed.FS 13 | var metaDefinition Definition 14 | 15 | func init() { 16 | var err error 17 | metaDefinition, err = NewSQLCDefinition(files, "sql/", "meta", 1) 18 | if err != nil { 19 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/meta.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | // source: meta.sql 5 | 6 | package db 7 | 8 | import ( 9 | "context" 10 | ) 11 | 12 | const getMeta = `-- name: GetMeta :one 13 | SELECT value FROM meta_inf WHERE key = $1 14 | ` 15 | 16 | func (q *Queries) GetMeta(ctx context.Context, key string) (string, error) { 17 | row := q.db.QueryRow(ctx, getMeta, key) 18 | var value string 19 | err := row.Scan(&value) 20 | return value, err 21 | } 22 | 23 | const insertMeta = `-- name: InsertMeta :exec 24 | INSERT INTO meta_inf (key, value) VALUES ($1, $2) 25 | ` 26 | 27 | type InsertMetaParams struct { 28 | Key string 29 | Value string 30 | } 31 | 32 | func (q *Queries) InsertMeta(ctx context.Context, arg InsertMetaParams) error { 33 | _, err := q.db.Exec(ctx, insertMeta, arg.Key, arg.Value) 34 | return err 35 | } 36 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package db 6 | 7 | type MetaInf struct { 8 | Key string 9 | Value string 10 | } 11 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/sql/queries/meta.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertMeta :exec 2 | INSERT INTO meta_inf (key, value) VALUES ($1, $2); 3 | 4 | -- name: GetMeta :one 5 | SELECT value FROM meta_inf WHERE key = $1; 6 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/sql/schemas/meta.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE meta_inf( 2 | key text PRIMARY KEY, 3 | value text NOT NULL 4 | ); 5 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "db" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/medley/db/transaction.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v4" 7 | ) 8 | 9 | type ( 10 | TxFunc func(pgx.Tx) error 11 | TxFuncWithContext func(context.Context, pgx.Tx) error 12 | ) 13 | 14 | // WrapContext is an adapter for using functions with a signature 15 | // `func(context.Context, pgx.Tx) error` and adapt them for functions 16 | // with a call signature of `func(pgx.Tx) error`. 17 | // The `ctx` will be passed to the wrapped source function as a closure. 18 | func WrapContext(ctx context.Context, f TxFuncWithContext) (context.Context, TxFunc) { 19 | wrapped := func(tx pgx.Tx) error { 20 | return f(ctx, tx) 21 | } 22 | return ctx, wrapped 23 | } 24 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/encodeable.go: -------------------------------------------------------------------------------- 1 | package encodeable 2 | 3 | import ( 4 | "encoding" 5 | ) 6 | 7 | type TextEncodeable interface { 8 | encoding.TextMarshaler 9 | encoding.TextUnmarshaler 10 | } 11 | 12 | func String(a encoding.TextMarshaler) string { 13 | res, err := a.MarshalText() 14 | if err != nil { 15 | return "" 16 | } 17 | return string(res) 18 | } 19 | 20 | func FromString[T encoding.TextUnmarshaler](a T, s string) error { 21 | err := a.UnmarshalText([]byte(s)) 22 | if err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/env/environment.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | //go:generate go-enum --marshal 4 | 5 | // ENUM(production, staging, local). 6 | type Environment int 7 | 8 | func (x *Environment) Equal(b *Environment) bool { 9 | return x == b 10 | } 11 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/hex/bytes.go: -------------------------------------------------------------------------------- 1 | package hex 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "io" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/encodeable" 9 | ) 10 | 11 | func EncodeHex(src []byte) []byte { 12 | // copied from the encoding/hex package, without the string->byte conversion: 13 | dst := make([]byte, hex.EncodedLen(len(src))) 14 | hex.Encode(dst, src) 15 | return dst 16 | } 17 | 18 | func DecodeHex(src []byte) ([]byte, error) { 19 | // copied from the encoding/hex package, without string -> byte conversion: 20 | 21 | // We can use the source slice itself as the destination 22 | // because the decode loop increments by one and then the 'seen' byte is not used anymore. 23 | n, err := hex.Decode(src, src) 24 | return src[:n], err 25 | } 26 | 27 | type Bytes struct { 28 | Value []byte 29 | } 30 | 31 | func ReadBytes(reader io.Reader, byteSize int) (Bytes, error) { 32 | value := make([]byte, byteSize) 33 | _, err := io.ReadFull(reader, value) 34 | if err != nil { 35 | return Bytes{}, err 36 | } 37 | return Bytes{value}, nil 38 | } 39 | 40 | func (a *Bytes) UnmarshalText(b []byte) error { 41 | v, err := DecodeHex(b) 42 | if err != nil { 43 | return err 44 | } 45 | a.Value = v 46 | return nil 47 | } 48 | 49 | func (a Bytes) MarshalText() ([]byte, error) { 50 | return EncodeHex(a.Value), nil 51 | } 52 | 53 | func (a *Bytes) Equal(b *Bytes) bool { 54 | return bytes.Equal(a.Value, b.Value) 55 | } 56 | 57 | func (a *Bytes) String() string { 58 | return encodeable.String(a) 59 | } 60 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/keys/keys.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "encoding" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type Algorithm int 12 | 13 | const ( 14 | Ed25519 Algorithm = iota 15 | ECDSASecp256k1 16 | LibP2P 17 | ) 18 | 19 | type key interface { 20 | encoding.TextMarshaler 21 | encoding.TextUnmarshaler 22 | fmt.Stringer 23 | Bytes() []byte 24 | Type() Algorithm 25 | } 26 | 27 | type Private interface { 28 | key 29 | Public() Public 30 | Sign([]byte) ([]byte, error) 31 | } 32 | 33 | type Public interface { 34 | key 35 | Verify([]byte, []byte) (bool, error) 36 | } 37 | 38 | var ErrUnknownKeyAlgorithm = errors.New("key algorithm not supported") 39 | 40 | type RepeatedByteReader struct { 41 | Value uint8 42 | } 43 | 44 | func (c RepeatedByteReader) Read(p []byte) (n int, err error) { //nolint:unparam 45 | for i := range p { 46 | p[i] = c.Value 47 | } 48 | return len(p), nil 49 | } 50 | 51 | // GenerateKeyPairWithReader returns a keypair of the given type and bitsize. 52 | func GenerateNew(algorithm Algorithm, src io.Reader) (Private, error) { 53 | switch algorithm { 54 | case Ed25519: 55 | return GenerateEd25519Key(src) 56 | case ECDSASecp256k1: 57 | return GenerateECDSAKey(src) 58 | case LibP2P: 59 | return GenerateLibp2pPrivate(src) 60 | default: 61 | return nil, ErrUnknownKeyAlgorithm 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/time/duration.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import _time "time" 4 | 5 | type Duration struct { 6 | _time.Duration 7 | } 8 | 9 | func (k *Duration) UnmarshalText(b []byte) error { 10 | dur, err := _time.ParseDuration(string(b)) 11 | if err != nil { 12 | return err 13 | } 14 | k.Duration = dur 15 | return nil 16 | } 17 | 18 | func (k *Duration) Equal(b *Duration) bool { 19 | aNs := k.Abs().Nanoseconds() 20 | bNs := b.Abs().Nanoseconds() 21 | return aNs == bNs 22 | } 23 | 24 | func (k *Duration) MarshalText() ([]byte, error) { //nolint: unparam 25 | return []byte(k.String()), nil 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/medley/encodeable/url/url.go: -------------------------------------------------------------------------------- 1 | package url 2 | 3 | import ( 4 | gourl "net/url" 5 | ) 6 | 7 | type URL struct { 8 | *gourl.URL 9 | } 10 | 11 | func (u *URL) Equal(b *URL) bool { 12 | return u.String() == b.String() 13 | } 14 | 15 | func (u *URL) MarshalText() (text []byte, err error) { //nolint: unparam 16 | return []byte(u.String()), nil 17 | } 18 | 19 | func (u *URL) UnmarshalText(text []byte) error { 20 | u1, err := gourl.Parse(string(text)) 21 | if err != nil { 22 | return err 23 | } 24 | u.URL = u1 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/medley/identitypreimage/identitypreimage.go: -------------------------------------------------------------------------------- 1 | package identitypreimage 2 | 3 | import ( 4 | "bytes" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | ) 9 | 10 | type IdentityPreimage []byte 11 | 12 | func BigToIdentityPreimage(n *big.Int) IdentityPreimage { 13 | return IdentityPreimage(n.Bytes()) 14 | } 15 | 16 | func HexToIdentityPreimage(n string) (IdentityPreimage, error) { 17 | b, err := hexutil.Decode(n) 18 | return IdentityPreimage(b), err 19 | } 20 | 21 | func Uint64ToIdentityPreimage(n uint64) IdentityPreimage { 22 | return BigToIdentityPreimage(new(big.Int).SetUint64(n)) 23 | } 24 | 25 | func (e IdentityPreimage) Bytes() []byte { 26 | return []byte(e) 27 | } 28 | 29 | func (e IdentityPreimage) Big() *big.Int { 30 | return new(big.Int).SetBytes(e) 31 | } 32 | 33 | func (e IdentityPreimage) Uint64() uint64 { 34 | return e.Big().Uint64() 35 | } 36 | 37 | func (e IdentityPreimage) Hex() string { 38 | return hexutil.Encode(e) 39 | } 40 | 41 | func (e IdentityPreimage) String() string { 42 | s := e.Hex() 43 | if len(s) < 10 { 44 | return s[2:] 45 | } 46 | return s[2:6] + ".." + s[len(s)-4:] 47 | } 48 | 49 | func Equal(a, b IdentityPreimage) bool { 50 | return bytes.Equal(a.Bytes(), b.Bytes()) 51 | } 52 | 53 | func (e IdentityPreimage) MarshalText() ([]byte, error) { //nolint:unparam 54 | return []byte(e.Hex()), nil 55 | } 56 | 57 | func (e *IdentityPreimage) UnmarshalText(input []byte) error { 58 | val, err := HexToIdentityPreimage(string(input)) 59 | *e = val.Bytes() 60 | return err 61 | } 62 | -------------------------------------------------------------------------------- /rolling-shutter/medley/logger/noop.go: -------------------------------------------------------------------------------- 1 | //nolint:revive 2 | package logger 3 | 4 | import ( 5 | "context" 6 | "log/slog" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | ) 10 | 11 | type NoopLogger struct{} 12 | 13 | func (n *NoopLogger) With(ctx ...interface{}) log.Logger { return n } 14 | func (n *NoopLogger) New(ctx ...interface{}) log.Logger { return &NoopLogger{} } 15 | func (n *NoopLogger) Log(level slog.Level, msg string, ctx ...interface{}) {} 16 | func (n *NoopLogger) Trace(msg string, ctx ...interface{}) {} 17 | func (n *NoopLogger) Debug(msg string, ctx ...interface{}) {} 18 | func (n *NoopLogger) Info(msg string, ctx ...interface{}) {} 19 | func (n *NoopLogger) Warn(msg string, ctx ...interface{}) {} 20 | func (n *NoopLogger) Error(msg string, ctx ...interface{}) {} 21 | func (n *NoopLogger) Crit(msg string, ctx ...interface{}) {} 22 | func (n *NoopLogger) Write(level slog.Level, msg string, attrs ...any) {} 23 | func (n *NoopLogger) Enabled(ctx context.Context, level slog.Level) bool { return false } 24 | func (n *NoopLogger) Handler() slog.Handler { return slog.Default().Handler() } 25 | -------------------------------------------------------------------------------- /rolling-shutter/medley/metricsserver/config.go: -------------------------------------------------------------------------------- 1 | package metricsserver 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 7 | ) 8 | 9 | var _ configuration.Config = &MetricsConfig{} 10 | 11 | func NewConfig() *MetricsConfig { 12 | c := &MetricsConfig{} 13 | c.Init() 14 | return c 15 | } 16 | 17 | type MetricsConfig struct { 18 | Enabled bool 19 | Host string 20 | Port uint16 21 | } 22 | 23 | func (mc *MetricsConfig) Init() { 24 | } 25 | 26 | func (mc *MetricsConfig) Name() string { 27 | return "metrics" 28 | } 29 | 30 | func (mc *MetricsConfig) Validate() error { 31 | return nil 32 | } 33 | 34 | func (mc *MetricsConfig) SetDefaultValues() error { 35 | mc.Enabled = false 36 | return nil 37 | } 38 | 39 | func (mc *MetricsConfig) SetExampleValues() error { 40 | mc.Enabled = false 41 | mc.Host = "[::]" 42 | mc.Port = 9100 43 | return nil 44 | } 45 | 46 | func (mc *MetricsConfig) TOMLWriteHeader(_ io.Writer) (int, error) { 47 | return 0, nil 48 | } 49 | -------------------------------------------------------------------------------- /rolling-shutter/medley/slots.go: -------------------------------------------------------------------------------- 1 | package medley 2 | 3 | func BlockTimestampToSlot(blockTimestamp uint64, genesisSlotTimestamp uint64, secondsPerSlot uint64) uint64 { 4 | return (blockTimestamp - genesisSlotTimestamp) / secondsPerSlot 5 | } 6 | 7 | func SlotToEpoch(slot uint64, slotsPerEpoch uint64) uint64 { 8 | return slot / slotsPerEpoch 9 | } 10 | 11 | func SlotToTimestamp(slot uint64, genesisSlotTimestamp uint64, secondsPerSlot uint64) uint64 { 12 | return genesisSlotTimestamp + slot*secondsPerSlot 13 | } 14 | -------------------------------------------------------------------------------- /rolling-shutter/medley/slotticker/slotticker_test.go: -------------------------------------------------------------------------------- 1 | package slotticker 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "gotest.tools/assert" 8 | ) 9 | 10 | func TestCalcNextSlot(t *testing.T) { 11 | duration := time.Second * 5 12 | genesisTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) 13 | 14 | epsilon := time.Millisecond 15 | 16 | for _, testCase := range []struct { 17 | timeSinceGenesis time.Duration 18 | offset time.Duration 19 | slot uint64 20 | }{ 21 | {0, 0, 0}, 22 | {-time.Second * 100, 0, 0}, 23 | {epsilon, 0, 1}, 24 | {duration / 2, 0, 1}, 25 | {duration, 0, 1}, 26 | {2 * duration, 0, 2}, 27 | {100*duration - epsilon, 0, 100}, 28 | 29 | {-time.Second, -time.Second, 0}, 30 | {0, -time.Second, 1}, 31 | {4 * time.Second, -time.Second, 1}, 32 | {4*time.Second + epsilon, -time.Second, 2}, 33 | {100*duration - time.Second, -time.Second, 100}, 34 | {100*duration - time.Second + epsilon, -time.Second, 101}, 35 | } { 36 | t.Run("", func(t *testing.T) { 37 | now := genesisTime.Add(testCase.timeSinceGenesis) 38 | slot, tick := calcNextTick(now, genesisTime, duration, testCase.offset) 39 | assert.Equal(t, testCase.slot, slot) 40 | expectedTick := genesisTime.Add(duration * time.Duration(testCase.slot)).Add(testCase.offset) 41 | assert.Equal(t, tick, expectedTick) 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rolling-shutter/medley/spit.go: -------------------------------------------------------------------------------- 1 | package medley 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/afero" 7 | ) 8 | 9 | // SecureSpit creates a new file with the given path and writes the given content to it. The file 10 | // is created with with mode 0600. SecureSpit will not overwrite an existing file unless asked. 11 | func SecureSpit(fs afero.Fs, path string, content []byte, overwrite bool) error { 12 | var err error 13 | flags := os.O_RDWR | os.O_CREATE 14 | if !overwrite { 15 | flags |= os.O_EXCL 16 | } 17 | file, err := fs.OpenFile(path, flags, 0o600) 18 | if err != nil { 19 | return err 20 | } 21 | defer file.Close() 22 | _, err = file.Write(content) 23 | if err != nil { 24 | return err 25 | } 26 | return file.Sync() 27 | } 28 | -------------------------------------------------------------------------------- /rolling-shutter/medley/syncranges.go: -------------------------------------------------------------------------------- 1 | package medley 2 | 3 | func GetSyncRanges(start, end, maxRange uint64) [][2]uint64 { 4 | ranges := [][2]uint64{} 5 | for i := start; i <= end; i += maxRange { 6 | s := i 7 | e := i + maxRange - 1 8 | ranges = append(ranges, [2]uint64{s, e}) 9 | if e > end { 10 | ranges[len(ranges)-1][1] = end 11 | break 12 | } 13 | } 14 | return ranges 15 | } 16 | -------------------------------------------------------------------------------- /rolling-shutter/medley/syncranges_test.go: -------------------------------------------------------------------------------- 1 | package medley 2 | 3 | import ( 4 | "testing" 5 | 6 | "gotest.tools/v3/assert" 7 | ) 8 | 9 | func TestGetSyncRanges(t *testing.T) { 10 | var maxRange uint64 = 3 11 | testCases := []struct { 12 | start uint64 13 | end uint64 14 | ranges [][2]uint64 15 | }{ 16 | {start: 0, end: 0, ranges: [][2]uint64{{0, 0}}}, 17 | {start: 3, end: 3, ranges: [][2]uint64{{3, 3}}}, 18 | {start: 0, end: 2, ranges: [][2]uint64{{0, 2}}}, 19 | {start: 3, end: 5, ranges: [][2]uint64{{3, 5}}}, 20 | {start: 0, end: 5, ranges: [][2]uint64{{0, 2}, {3, 5}}}, 21 | {start: 3, end: 8, ranges: [][2]uint64{{3, 5}, {6, 8}}}, 22 | {start: 0, end: 1, ranges: [][2]uint64{{0, 1}}}, 23 | {start: 3, end: 4, ranges: [][2]uint64{{3, 4}}}, 24 | {start: 0, end: 4, ranges: [][2]uint64{{0, 2}, {3, 4}}}, 25 | {start: 1, end: 5, ranges: [][2]uint64{{1, 3}, {4, 5}}}, 26 | } 27 | for _, testCase := range testCases { 28 | ranges := GetSyncRanges(testCase.start, testCase.end, maxRange) 29 | assert.DeepEqual(t, ranges, testCase.ranges) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rolling-shutter/medley/testlog/testlog.go: -------------------------------------------------------------------------------- 1 | // Package testlog configures zerolog for use in tests. 2 | package testlog 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "strings" 8 | "sync" 9 | 10 | "github.com/rs/zerolog" 11 | "github.com/rs/zerolog/log" 12 | ) 13 | 14 | var once sync.Once 15 | 16 | // colorize returns the string s wrapped in ANSI code c, unless disabled is true. 17 | func colorize(s interface{}, c int, disabled bool) string { 18 | if disabled { 19 | return fmt.Sprintf("%s", s) 20 | } 21 | return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s) 22 | } 23 | 24 | func setup() { 25 | zerolog.SetGlobalLevel(zerolog.DebugLevel) 26 | pathsep := string(os.PathSeparator) 27 | zerolog.CallerMarshalFunc = func(_ uintptr, file string, line int) string { 28 | return fmt.Sprintf("%s:%d", file[1+strings.LastIndex(file, pathsep):], line) 29 | } 30 | 31 | zerolog.TimeFieldFormat = " " // hack to indent log messages 32 | nocolor := os.Getenv("NOCOLOR") == "1" 33 | log.Logger = zerolog.New(zerolog.ConsoleWriter{ 34 | NoColor: nocolor, 35 | Out: os.Stderr, 36 | TimeFormat: zerolog.TimeFieldFormat, 37 | PartsOrder: []string{ 38 | zerolog.TimestampFieldName, 39 | zerolog.LevelFieldName, 40 | zerolog.CallerFieldName, 41 | zerolog.MessageFieldName, 42 | }, 43 | FormatCaller: func(i interface{}) string { 44 | return colorize(fmt.Sprintf("[%20s]", i), 1, nocolor) 45 | }, 46 | }).With().Caller().Timestamp().Logger() 47 | } 48 | 49 | // Setup sets up zerolog for use in tests. 50 | func Setup() { 51 | once.Do(setup) 52 | } 53 | -------------------------------------------------------------------------------- /rolling-shutter/medley/validatorregistry/signature.go: -------------------------------------------------------------------------------- 1 | package validatorregistry 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/crypto" 5 | blst "github.com/supranational/blst/bindings/go" 6 | ) 7 | 8 | var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") 9 | 10 | func VerifyAggregateSignature(sig *blst.P2Affine, pks []*blst.P1Affine, msg *AggregateRegistrationMessage) bool { 11 | if msg.Version < AggregateValidatorRegistrationMessageVersion { 12 | return false 13 | } 14 | if len(pks) != int(msg.Count) { 15 | return false 16 | } 17 | msgHash := crypto.Keccak256(msg.Marshal()) 18 | msgs := make([][]byte, len(pks)) 19 | for i := range pks { 20 | msgs[i] = msgHash 21 | } 22 | return sig.AggregateVerify(true, pks, true, msgs, dst) 23 | } 24 | 25 | func VerifySignature(sig *blst.P2Affine, pubkey *blst.P1Affine, msg *LegacyRegistrationMessage) bool { 26 | msgHash := crypto.Keccak256(msg.Marshal()) 27 | return sig.Verify(true, pubkey, true, msgHash, dst) 28 | } 29 | 30 | func CreateAggregateSignature(sks []*blst.SecretKey, msg *AggregateRegistrationMessage) *blst.P2Affine { 31 | msgHash := crypto.Keccak256(msg.Marshal()) 32 | aggregate := new(blst.P2Aggregate) 33 | for _, sk := range sks { 34 | aff := new(blst.P2Affine) 35 | sig := aff.Sign(sk, msgHash, dst) 36 | ok := aggregate.Add(sig, true) 37 | if !ok { 38 | return nil 39 | } 40 | } 41 | return aggregate.ToAffine() 42 | } 43 | 44 | func CreateSignature(sk *blst.SecretKey, msg *LegacyRegistrationMessage) *blst.P2Affine { 45 | msgHash := crypto.Keccak256(msg.Marshal()) 46 | return new(blst.P2Affine).Sign(sk, msgHash, dst) 47 | } 48 | -------------------------------------------------------------------------------- /rolling-shutter/medley/validatorregistry/validatorregistry_test.go: -------------------------------------------------------------------------------- 1 | package validatorregistry 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "gotest.tools/v3/assert" 9 | ) 10 | 11 | func TestRegistrationMessageMarshalRoundtrip(t *testing.T) { 12 | m := &LegacyRegistrationMessage{ 13 | Version: 1, 14 | ChainID: 2, 15 | ValidatorRegistryAddress: common.HexToAddress("0x1234567890123456789012345678901234567890"), 16 | ValidatorIndex: 3, 17 | Nonce: 4, 18 | IsRegistration: true, 19 | } 20 | marshaled := m.Marshal() 21 | unmarshaled := new(LegacyRegistrationMessage) 22 | err := unmarshaled.Unmarshal(marshaled) 23 | assert.NilError(t, err) 24 | assert.DeepEqual(t, m, unmarshaled) 25 | } 26 | 27 | func TestRegistrationMessageInvalidUnmarshal(t *testing.T) { 28 | base := bytes.Repeat([]byte{0}, 46) 29 | assert.NilError(t, new(LegacyRegistrationMessage).Unmarshal(base)) 30 | 31 | for _, b := range [][]byte{ 32 | {}, 33 | bytes.Repeat([]byte{0}, 45), 34 | bytes.Repeat([]byte{0}, 47), 35 | bytes.Repeat([]byte{0}, 92), 36 | } { 37 | err := new(LegacyRegistrationMessage).Unmarshal(b) 38 | assert.ErrorContains(t, err, "invalid registration message length") 39 | } 40 | 41 | for _, isRegistrationByte := range []byte{2, 3, 255} { 42 | b := bytes.Repeat([]byte{0}, 46) 43 | b[45] = isRegistrationByte 44 | err := new(LegacyRegistrationMessage).Unmarshal(b) 45 | assert.ErrorContains(t, err, "invalid registration message type byte") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rolling-shutter/p2p/dht_test.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/libp2p/go-libp2p/core/peer" 9 | "gotest.tools/v3/assert" 10 | ) 11 | 12 | func TestRandomisePeers(t *testing.T) { 13 | peerAddr := make([]peer.AddrInfo, 0) 14 | 15 | for i := 0; i < 5; i++ { 16 | peerAddr = append(peerAddr, peer.AddrInfo{ 17 | ID: peer.ID(fmt.Sprintf("%d", i)), 18 | }) 19 | } 20 | 21 | randomisedPeers := randomizePeers(peerAddr) 22 | 23 | equal := reflect.DeepEqual(peerAddr, randomisedPeers) 24 | assert.Assert(t, !equal, "randomized unsuccessful") 25 | } 26 | -------------------------------------------------------------------------------- /rolling-shutter/p2p/floodsubpeerdiscovery/peer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;floodsubpeerdiscovery"; 4 | 5 | message Peer { 6 | bytes publicKey = 1; 7 | repeated bytes addrs = 2; 8 | } -------------------------------------------------------------------------------- /rolling-shutter/p2p/message.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | pubsub "github.com/libp2p/go-libp2p-pubsub" 8 | "github.com/pkg/errors" 9 | 10 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" 11 | ) 12 | 13 | func UnmarshalPubsubMessage(msg *pubsub.Message) (p2pmsg.Message, *p2pmsg.TraceContext, error) { 14 | var err error 15 | 16 | unmshl, traceContext, err := p2pmsg.Unmarshal(msg.GetData()) 17 | if err != nil { 18 | return nil, traceContext, errors.Wrap(err, "failed to unmarshal message") 19 | } 20 | 21 | err = unmshl.Validate() 22 | if err != nil { 23 | return nil, traceContext, errors.Wrap(err, fmt.Sprintf("verification failed <%s>", reflect.TypeOf(unmshl).String())) 24 | } 25 | return unmshl, traceContext, nil 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/p2p/multiaddr.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "github.com/libp2p/go-libp2p/core/peer" 5 | "github.com/multiformats/go-multiaddr" 6 | ) 7 | 8 | // MustMultiaddr converts the given string to a multiaddr.Multiaddr. 9 | func MustMultiaddr(s string) multiaddr.Multiaddr { 10 | a, err := multiaddr.NewMultiaddr(s) 11 | if err != nil { 12 | panic(err) 13 | } 14 | return a 15 | } 16 | 17 | func MustAddrInfo(s string) peer.AddrInfo { 18 | a, err := peer.AddrInfoFromString(s) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return *a 23 | } 24 | -------------------------------------------------------------------------------- /rolling-shutter/p2p/topic.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "context" 5 | 6 | pubsub "github.com/libp2p/go-libp2p-pubsub" 7 | "github.com/libp2p/go-libp2p/core/peer" 8 | ) 9 | 10 | // gossipRoom represents a subscription to a single PubSub topic. Messages 11 | // can be published to the topic with gossipRoom.Publish, and received 12 | // messages are pushed to the Messages channel. 13 | type gossipRoom struct { 14 | pubSub *pubsub.PubSub 15 | topic *pubsub.Topic 16 | subscription *pubsub.Subscription 17 | topicName string 18 | self peer.ID 19 | } 20 | 21 | // Publish sends a message to the pubsub topic. 22 | func (room *gossipRoom) Publish(ctx context.Context, message []byte) error { 23 | return room.topic.Publish(ctx, message) 24 | } 25 | 26 | func (room *gossipRoom) ListPeers() []peer.ID { 27 | return room.pubSub.ListPeers(room.topicName) 28 | } 29 | 30 | // readLoop pulls messages from the pubsub topic and pushes them onto the given messages channel. 31 | func (room *gossipRoom) readLoop(ctx context.Context, messages chan *pubsub.Message) error { 32 | for { 33 | msg, err := room.subscription.Next(ctx) 34 | if err != nil { 35 | return err 36 | } 37 | // only forward messages delivered by others 38 | if msg.ReceivedFrom == room.self { 39 | continue 40 | } 41 | // send valid messages onto the Messages channel 42 | select { 43 | case messages <- msg: 44 | case <-ctx.Done(): 45 | return ctx.Err() 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rolling-shutter/p2pmsg/decryptiontrigger.go: -------------------------------------------------------------------------------- 1 | package p2pmsg 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/binary" 6 | 7 | "golang.org/x/crypto/sha3" 8 | 9 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" 10 | ) 11 | 12 | var triggerHashPrefix = []byte{0x19, 't', 'r', 'i', 'g', 'g', 'e', 'r'} 13 | 14 | func NewSignedDecryptionTrigger( 15 | instanceID uint64, identityPreimage identitypreimage.IdentityPreimage, blockNumber uint64, txHash []byte, privKey *ecdsa.PrivateKey, 16 | ) (*DecryptionTrigger, error) { 17 | trigger := &DecryptionTrigger{ 18 | InstanceId: instanceID, 19 | IdentityPreimage: identityPreimage.Bytes(), 20 | BlockNumber: blockNumber, 21 | TransactionsHash: txHash, 22 | } 23 | err := Sign(trigger, privKey) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return trigger, nil 28 | } 29 | 30 | func (trigger *DecryptionTrigger) SetSignature(s []byte) { 31 | trigger.Signature = s 32 | } 33 | 34 | func (trigger *DecryptionTrigger) Hash() []byte { 35 | hash := sha3.New256() 36 | hash.Write(triggerHashPrefix) 37 | _ = binary.Write(hash, binary.BigEndian, trigger.InstanceId) 38 | _ = binary.Write(hash, binary.BigEndian, trigger.IdentityPreimage) 39 | hash.Write(trigger.TransactionsHash) 40 | return hash.Sum(nil) 41 | } 42 | 43 | func HashByteList(l [][]byte) []byte { 44 | hash := sha3.New256() 45 | for _, bytes := range l { 46 | h := sha3.Sum256(bytes) 47 | hash.Write(h[:]) 48 | } 49 | return hash.Sum(nil) 50 | } 51 | -------------------------------------------------------------------------------- /rolling-shutter/p2pmsg/doc.go: -------------------------------------------------------------------------------- 1 | package p2pmsg 2 | 3 | //go:generate protoc gossip.proto --go_out=./ 4 | -------------------------------------------------------------------------------- /rolling-shutter/p2pmsg/eonpublickey.go: -------------------------------------------------------------------------------- 1 | package p2pmsg 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/binary" 6 | 7 | "golang.org/x/crypto/sha3" 8 | ) 9 | 10 | var eonPubKeyHashPrefix = []byte{0x19, 'e', 'o', 'n', 'p', 'u', 'b'} 11 | 12 | // NewSignedEonPublicKey creates a new eon public key and signs it with the given private key. 13 | func NewSignedEonPublicKey( 14 | instanceID uint64, 15 | eonPublicKey []byte, 16 | activationBlock uint64, 17 | keyperConfigIndex uint64, 18 | eon uint64, 19 | privKey *ecdsa.PrivateKey, 20 | ) (*EonPublicKey, error) { 21 | candidate := &EonPublicKey{ 22 | InstanceId: instanceID, 23 | PublicKey: eonPublicKey, 24 | ActivationBlock: activationBlock, 25 | KeyperConfigIndex: keyperConfigIndex, 26 | Eon: eon, 27 | } 28 | err := Sign(candidate, privKey) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return candidate, nil 33 | } 34 | 35 | func (e *EonPublicKey) SetSignature(s []byte) { 36 | e.Signature = s 37 | } 38 | 39 | func (e *EonPublicKey) Hash() []byte { 40 | hash := sha3.New256() 41 | hash.Write(eonPubKeyHashPrefix) 42 | _ = binary.Write(hash, binary.BigEndian, e.InstanceId) 43 | _ = binary.Write(hash, binary.BigEndian, e.ActivationBlock) 44 | _ = binary.Write(hash, binary.BigEndian, e.KeyperConfigIndex) 45 | _ = binary.Write(hash, binary.BigEndian, e.Eon) 46 | hash.Write(e.PublicKey) 47 | return hash.Sum(nil) 48 | } 49 | -------------------------------------------------------------------------------- /rolling-shutter/p2pmsg/signing.go: -------------------------------------------------------------------------------- 1 | package p2pmsg 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 8 | ) 9 | 10 | type Signable interface { 11 | Hash() []byte 12 | SetSignature([]byte) 13 | GetSignature() []byte 14 | } 15 | 16 | func Sign(s Signable, privKey *ecdsa.PrivateKey) error { 17 | signature, err := ethcrypto.Sign(s.Hash(), privKey) 18 | if err != nil { 19 | return err 20 | } 21 | s.SetSignature(signature) 22 | return nil 23 | } 24 | 25 | func RecoverAddress(s Signable) (common.Address, error) { 26 | pubkey, err := ethcrypto.SigToPub(s.Hash(), s.GetSignature()) 27 | if err != nil { 28 | return common.Address{}, err 29 | } 30 | return ethcrypto.PubkeyToAddress(*pubkey), nil 31 | } 32 | 33 | func VerifySignature(s Signable, address common.Address) (bool, error) { 34 | recoveredAddress, err := RecoverAddress(s) 35 | if err != nil { 36 | return false, err 37 | } 38 | return recoveredAddress == address, nil 39 | } 40 | -------------------------------------------------------------------------------- /rolling-shutter/p2pnode/config.go: -------------------------------------------------------------------------------- 1 | package p2pnode 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration" 7 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/metricsserver" 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/p2p" 9 | ) 10 | 11 | var _ configuration.Config = &Config{} 12 | 13 | func NewConfig() *Config { 14 | c := &Config{} 15 | c.Init() 16 | return c 17 | } 18 | 19 | type Config struct { 20 | ListenMessages bool `comment:"whether to register handlers on the messages and log them"` 21 | 22 | P2P *p2p.Config 23 | Metrics *metricsserver.MetricsConfig 24 | } 25 | 26 | func (c *Config) Init() { 27 | c.P2P = p2p.NewConfig() 28 | c.Metrics = metricsserver.NewConfig() 29 | } 30 | 31 | func (c *Config) Name() string { 32 | return "p2pnode" 33 | } 34 | 35 | func (c *Config) Validate() error { 36 | return nil 37 | } 38 | 39 | func (c *Config) SetDefaultValues() error { 40 | c.ListenMessages = true 41 | return nil 42 | } 43 | 44 | func (c *Config) SetExampleValues() error { 45 | return c.SetDefaultValues() 46 | } 47 | 48 | func (c Config) TOMLWriteHeader(w io.Writer) (int, error) { 49 | return w.Write([]byte("# Peer role: bootstrap\n")) 50 | } 51 | -------------------------------------------------------------------------------- /rolling-shutter/sandbox/ganache.go: -------------------------------------------------------------------------------- 1 | package sandbox 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | ) 8 | 9 | var ganacheHexKeys = []string{ 10 | "4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d", 11 | "6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1", 12 | "6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c", 13 | "646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913", 14 | "add53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743", 15 | "395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd", 16 | "e485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52", 17 | "a453611d9419d0e56f499079478fd72c37b251a94bfde4d19872c44cf65386e3", 18 | "829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4", 19 | "b0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773", 20 | } 21 | 22 | // GanacheKey returns the nth key used by ganache when started with '-d'. 23 | func GanacheKey(n int) *ecdsa.PrivateKey { 24 | privkey, err := crypto.HexToECDSA(ganacheHexKeys[n]) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return privkey 29 | } 30 | 31 | func NumGanacheKeys() int { 32 | return len(ganacheHexKeys) 33 | } 34 | -------------------------------------------------------------------------------- /rolling-shutter/sandbox/keygen/keygen.go: -------------------------------------------------------------------------------- 1 | // simple tool to generate random eon keys and corresponding decryption keys. 2 | package main 3 | 4 | import ( 5 | "bytes" 6 | "crypto/rand" 7 | "fmt" 8 | 9 | "github.com/shutter-network/shutter/shlib/shcrypto" 10 | 11 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" 12 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testkeygen" 13 | ) 14 | 15 | func main() { 16 | keys, err := testkeygen.NewEonKeys(rand.Reader, 3, 2) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | var prevEonPublicKey *shcrypto.EonPublicKey 22 | for i := uint64(0); i < 200; i++ { 23 | identityPreimage := identitypreimage.Uint64ToIdentityPreimage(i) 24 | eonPublicKey := keys.EonPublicKey() 25 | decryptionKey, err := keys.EpochSecretKey(identityPreimage) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | if prevEonPublicKey == nil || !bytes.Equal(eonPublicKey.Marshal(), prevEonPublicKey.Marshal()) { 31 | if prevEonPublicKey != nil { 32 | fmt.Printf("\n") 33 | } 34 | fmt.Printf("eon key: %X\n\n", eonPublicKey.Marshal()) 35 | fmt.Printf("epoch id | decryption key\n") 36 | } 37 | prevEonPublicKey = eonPublicKey 38 | 39 | fmt.Printf("%X | %X\n", identityPreimage.Bytes(), decryptionKey.Marshal()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rolling-shutter/sandbox/signing_test.go: -------------------------------------------------------------------------------- 1 | // Sandbox just contains example code how to use certain libraries 2 | package sandbox 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | "golang.org/x/crypto/sha3" 10 | ) 11 | 12 | // TestSigning shows how to sign messages and check signatures. 13 | func TestSigning(t *testing.T) { 14 | privateKey, err := crypto.GenerateKey() 15 | if err != nil { 16 | t.Fatalf("fatal: %s", err) 17 | } 18 | 19 | msg := []byte("message to be signed") 20 | 21 | hash := sha3.New256() 22 | hash.Write(msg) 23 | h := hash.Sum(nil) 24 | 25 | address := crypto.PubkeyToAddress(privateKey.PublicKey) 26 | t.Logf("generated key for address %s", address.Hex()) 27 | signature, err := crypto.Sign(h, privateKey) 28 | if err != nil { 29 | t.Fatalf("error %s %s", signature, err) 30 | } else { 31 | t.Logf("signature: %s", hexutil.Encode(signature)) 32 | } 33 | 34 | // Now check the signature 35 | 36 | pubkey, err := crypto.SigToPub(h, signature) 37 | if err != nil { 38 | t.Fatalf("error %s", err) 39 | } 40 | signerAddress := crypto.PubkeyToAddress(*pubkey) 41 | t.Logf("signer %s", signerAddress.Hex()) 42 | if signerAddress != address { 43 | t.Fatalf("addresses to not match") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rolling-shutter/shmsg/doc.go: -------------------------------------------------------------------------------- 1 | // Package shmsg contains the protocol buffer defined messages and auto-generated code as well as 2 | // some utility code to work with those 'shutter messages'. 3 | package shmsg 4 | 5 | //go:generate protoc shmsg.proto --go_out=./ 6 | -------------------------------------------------------------------------------- /rolling-shutter/shmsg/encode_test.go: -------------------------------------------------------------------------------- 1 | package shmsg 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/ethereum/go-ethereum/crypto" 8 | "gotest.tools/v3/assert" 9 | ) 10 | 11 | func makeMessage() *MessageWithNonce { 12 | msg := &Message{ 13 | Payload: &Message_CheckIn{ 14 | CheckIn: &CheckIn{ 15 | ValidatorPublicKey: bytes.Repeat([]byte("x"), 32), 16 | EncryptionPublicKey: bytes.Repeat([]byte("y"), 33), 17 | }, 18 | }, 19 | } 20 | msgWithNonce := &MessageWithNonce{ 21 | Msg: msg, 22 | RandomNonce: 123, 23 | } 24 | return msgWithNonce 25 | } 26 | 27 | func TestSignMessage(t *testing.T) { 28 | privateKey, err := crypto.GenerateKey() 29 | address := crypto.PubkeyToAddress(privateKey.PublicKey) 30 | 31 | if err != nil { 32 | t.Fatalf("fatal: %s", err) 33 | } 34 | signedMessage, err := SignMessage(makeMessage(), privateKey) 35 | assert.NilError(t, err) 36 | t.Logf("signed message size %d", len(signedMessage)) 37 | signer, err := GetSigner(signedMessage) 38 | if err != nil { 39 | t.Fatalf("could not get signer: %s", err) 40 | } 41 | if signer != address { 42 | t.Fatalf("wrong signer %s, expected %s", signer, address) 43 | } 44 | msg, err := GetMessage(signedMessage) 45 | if err != nil { 46 | t.Fatalf("could not get message: %s", err) 47 | } 48 | if msg.Msg.GetCheckIn() == nil { 49 | t.Fatal("got no check in") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rolling-shutter/shmsg/messages_test.go: -------------------------------------------------------------------------------- 1 | package shmsg 2 | 3 | import ( 4 | "crypto/rand" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "gotest.tools/v3/assert" 10 | 11 | shcrypto "github.com/shutter-network/shutter/shlib/shcrypto" 12 | ) 13 | 14 | func TestNewPolyCommitmentMsg(t *testing.T) { 15 | eon := uint64(10) 16 | threshold := uint64(5) 17 | poly, err := shcrypto.RandomPolynomial(rand.Reader, threshold) 18 | assert.NilError(t, err) 19 | gammas := poly.Gammas() 20 | 21 | msgContainer := NewPolyCommitment(eon, gammas) 22 | msg := msgContainer.GetPolyCommitment() 23 | assert.Assert(t, msg != nil) 24 | 25 | assert.Equal(t, eon, msg.Eon) 26 | assert.Equal(t, int(threshold)+1, len(msg.Gammas)) 27 | for i := 0; i < int(threshold)+1; i++ { 28 | gammaBytes := msg.Gammas[i] 29 | assert.DeepEqual(t, gammaBytes, (*gammas)[i].Compress()) 30 | } 31 | } 32 | 33 | func TestNewPolyEvalMsg(t *testing.T) { 34 | eon := uint64(10) 35 | receiver := common.BigToAddress(big.NewInt(0xaabbcc)) 36 | encryptedEval := []byte("secret") 37 | 38 | msgContainer := NewPolyEval(eon, []common.Address{receiver}, [][]byte{encryptedEval}) 39 | msg := msgContainer.GetPolyEval() 40 | assert.Assert(t, msg != nil) 41 | 42 | assert.Equal(t, eon, msg.Eon) 43 | assert.DeepEqual(t, receiver.Bytes(), msg.Receivers[0]) 44 | } 45 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/db.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgconn" 11 | "github.com/jackc/pgx/v4" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/definition.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/rs/zerolog/log" 7 | 8 | "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/db" 9 | ) 10 | 11 | //go:generate sqlc generate --file sql/sqlc.yaml 12 | 13 | //go:embed sql 14 | var files embed.FS 15 | var Definition db.Definition 16 | 17 | func init() { 18 | def, err := db.NewSQLCDefinition(files, "sql/", "snapshot", 22) 19 | if err != nil { 20 | log.Fatal().Err(err).Msg("failed to initialize DB metadata") 21 | } 22 | Definition = db.NewAggregateDefinition( 23 | "snapshot", 24 | def, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/models.sqlc.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.28.0 4 | 5 | package database 6 | 7 | type DecryptionKey struct { 8 | EpochID []byte 9 | Key []byte 10 | } 11 | 12 | type EonPublicKey struct { 13 | EonID int64 14 | EonPublicKey []byte 15 | } 16 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/sql/queries/snapshot.sql: -------------------------------------------------------------------------------- 1 | -- name: GetDecryptionKey :one 2 | SELECT * 3 | FROM decryption_key 4 | WHERE epoch_id = $1; 5 | 6 | -- name: GetDecryptionKeyCount :one 7 | SELECT COUNT(DISTINCT epoch_id) 8 | FROM decryption_key; 9 | 10 | -- name: InsertDecryptionKey :execrows 11 | INSERT INTO decryption_key ( 12 | epoch_id, 13 | key 14 | ) VALUES ( 15 | $1, $2 16 | ) 17 | ON CONFLICT DO NOTHING; 18 | 19 | -- name: InsertEonPublicKey :execrows 20 | INSERT INTO eon_public_key ( 21 | eon_id, 22 | eon_public_key 23 | ) VALUES ( 24 | $1, $2 25 | ) 26 | ON CONFLICT DO NOTHING; 27 | 28 | 29 | -- name: GetEonPublicKey :one 30 | SELECT eon_public_key 31 | FROM eon_public_key 32 | WHERE eon_id = $1; 33 | 34 | 35 | -- name: GetEonPublicKeyLatest :one 36 | SELECT eon_id, eon_public_key 37 | FROM eon_public_key 38 | ORDER BY eon_id DESC 39 | LIMIT 1; 40 | 41 | 42 | -- name: GetEonCount :one 43 | SELECT COUNT(DISTINCT eon_id) 44 | FROM eon_public_key; 45 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/sql/schemas/snapshot.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS decryption_key ( 2 | epoch_id bytea PRIMARY KEY, 3 | key bytea 4 | ); 5 | CREATE TABLE IF NOT EXISTS eon_public_key ( 6 | eon_id bigint PRIMARY KEY, 7 | eon_public_key bytea 8 | ); 9 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/database/sql/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - schema: "schemas" 4 | queries: "queries" 5 | engine: "postgresql" 6 | gen: 7 | go: 8 | package: "database" 9 | out: "../" 10 | sql_package: "pgx/v4" 11 | output_db_file_name: "db.sqlc.gen.go" 12 | output_models_file_name: "models.sqlc.gen.go" 13 | output_files_suffix: "c.gen" 14 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/hubapi/hubapi.go: -------------------------------------------------------------------------------- 1 | package hubapi 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | "log" 7 | "strconv" 8 | 9 | "github.com/AdamSLevy/jsonrpc2/v14" 10 | ) 11 | 12 | type HubAPI struct { 13 | BaseURL string 14 | Client jsonrpc2.Client 15 | } 16 | 17 | func New(hubURL string) *HubAPI { 18 | return &HubAPI{ 19 | BaseURL: hubURL, 20 | Client: jsonrpc2.Client{}, 21 | } 22 | } 23 | 24 | func (hub *HubAPI) SubmitEonKey(eonID uint64, key []byte) error { 25 | params := []string{strconv.FormatUint(eonID, 10), hex.EncodeToString(key)} 26 | var result bool 27 | err := hub.Client.Request(context.TODO(), hub.BaseURL, "shutter_set_eon_pubkey", params, &result) 28 | if err != nil { 29 | log.Printf("Error posting to HUB: %v", err) 30 | return err 31 | } 32 | return nil 33 | } 34 | 35 | func (hub *HubAPI) SubmitProposalKey(proposalID []byte, key []byte) error { 36 | params := []string{hex.EncodeToString(proposalID), hex.EncodeToString(key)} 37 | var result bool 38 | err := hub.Client.Request(context.TODO(), hub.BaseURL, "shutter_set_proposal_key", params, &result) 39 | if err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /rolling-shutter/snapshot/metrics.go: -------------------------------------------------------------------------------- 1 | package snapshot 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | ) 8 | 9 | var metricKeysGenerated = prometheus.NewCounter( 10 | prometheus.CounterOpts{ 11 | Namespace: "shutter", 12 | Subsystem: "snapshot", 13 | Name: "proposal_keys_generated_total", 14 | Help: "Number of generated proposal keys", 15 | }, 16 | ) 17 | 18 | var metricEons = prometheus.NewCounter( 19 | prometheus.CounterOpts{ 20 | Namespace: "shutter", 21 | Subsystem: "snapshot", 22 | Name: "eons_total", 23 | Help: "Number of eons", 24 | }, 25 | ) 26 | 27 | func (snp *Snapshot) initMetrics(ctx context.Context) error { 28 | prometheus.MustRegister(metricEons) 29 | prometheus.MustRegister(metricKeysGenerated) 30 | 31 | eonCount, err := snp.db.GetEonCount(ctx) 32 | if err != nil { 33 | return err 34 | } 35 | metricEons.Add(float64(eonCount)) 36 | 37 | keyCount, err := snp.db.GetDecryptionKeyCount(ctx) 38 | if err != nil { 39 | return err 40 | } 41 | metricKeysGenerated.Add(float64(keyCount)) 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /rolling-shutter/trace/noop.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace" 7 | "go.opentelemetry.io/otel/sdk/metric" 8 | "go.opentelemetry.io/otel/sdk/metric/metricdata" 9 | tracepb "go.opentelemetry.io/proto/otlp/trace/v1" 10 | ) 11 | 12 | type ( 13 | NoopTraceClient struct{} 14 | NoopMetricsExporter struct{} 15 | ) 16 | 17 | var ( 18 | _ metric.Exporter = NoopMetricsExporter{} 19 | _ otlptrace.Client = NoopTraceClient{} 20 | ) 21 | 22 | func (NoopTraceClient) Start(context.Context) error { 23 | return nil 24 | } 25 | 26 | func (NoopTraceClient) Stop(context.Context) error { 27 | return nil 28 | } 29 | 30 | func (NoopTraceClient) UploadTraces(context.Context, []*tracepb.ResourceSpans) error { 31 | return nil 32 | } 33 | 34 | func (NoopMetricsExporter) Temporality(metric.InstrumentKind) metricdata.Temporality { 35 | return metricdata.DeltaTemporality 36 | } 37 | 38 | func (NoopMetricsExporter) Aggregation(metric.InstrumentKind) metric.Aggregation { 39 | return metric.AggregationDrop{} 40 | } 41 | 42 | func (NoopMetricsExporter) Export(context.Context, *metricdata.ResourceMetrics) error { 43 | return nil 44 | } 45 | 46 | func (NoopMetricsExporter) ForceFlush(context.Context) error { 47 | return nil 48 | } 49 | 50 | func (NoopMetricsExporter) Shutdown(context.Context) error { 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /rolling-shutter/trace/test.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | "time" 8 | 9 | "golang.org/x/sync/errgroup" 10 | "gotest.tools/assert" 11 | ) 12 | 13 | func SetupTestTracing(t *testing.T) (context.Context, error) { 14 | t.Helper() 15 | 16 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 17 | 18 | eg, ectx := errgroup.WithContext(ctx) 19 | eg.Go(func() error { 20 | client := NoopTraceClient{} 21 | mExporter := NoopMetricsExporter{} 22 | return Run(ectx, client, mExporter, "test", "0.0.1") 23 | }) 24 | 25 | tick := time.NewTicker(10 * time.Millisecond) 26 | t.Cleanup(func() { 27 | tick.Stop() 28 | cancel() 29 | err := eg.Wait() 30 | assert.NilError(t, err) 31 | }) 32 | 33 | // HACK:this is a little bit ugly: 34 | // we have to poll the global until it is set 35 | for !IsEnabled() { 36 | select { 37 | case <-tick.C: 38 | continue 39 | case <-ctx.Done(): 40 | t.Fail() 41 | return ctx, errors.New("setting up tracing timed out") 42 | } 43 | } 44 | return ctx, nil 45 | } 46 | -------------------------------------------------------------------------------- /testdata/signedRegistrations_0x01.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "0x0100000000000027d8a9289a3dd14febe10611119be81e5d35eaac30840000000000000000000001f40000000001", 3 | "signature": "0x941698fb21c716ac4b9eae341c2eb63cbafb1206e1382986130ca06a3879a5f6a111a582f95233517abef65aac9e4a1912ea81b5b7584635ea3773969c9af747f6cc5d5c1e2aa628b6779fa56c02f6147a151ca46677b53bc03cbba1a9a079ed" 4 | } 5 | -------------------------------------------------------------------------------- /tools/asdf-install-plugins.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Helper to install the asdf plugins required by this project 4 | 5 | set -euo pipefail 6 | 7 | YELLOW='\033[1;33m' 8 | DEFAULT='\033[0m' 9 | 10 | BASE_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 11 | 12 | INSTALLED_PLUGINS=$(asdf plugin list --urls) 13 | 14 | while read -r plugin_name plugin_url; do 15 | { [[ -z $plugin_name ]] || [[ $plugin_name == \#* ]]; } && continue 16 | 17 | { 18 | installed_plugin_source=$(echo -e "$INSTALLED_PLUGINS" | grep -e "^$plugin_name") 19 | is_plugin_installed=$? 20 | } || : 21 | if [[ $is_plugin_installed -eq 0 ]]; then 22 | # Plugin is installed, check if repo url matches if given in plugin config file 23 | read -r _ installed_tool_url <<<"$installed_plugin_source" 24 | if [[ -n $plugin_url ]] && [[ $installed_tool_url != "$plugin_url" ]]; then 25 | echo -e "asdf plugin ${YELLOW}${plugin_name}${DEFAULT} is installed but using the wrong source - reinstalling" 26 | asdf plugin remove "$plugin_name" 27 | asdf plugin add "$plugin_name" "$plugin_url" 28 | fi 29 | else 30 | echo -e "asdf plugin ${YELLOW}${plugin_name}${DEFAULT} is missing, installing" 31 | asdf plugin add "$plugin_name" "$plugin_url" 32 | fi 33 | done <"${BASE_DIR}/../.asdf-plugins" 34 | -------------------------------------------------------------------------------- /tools/snapshot/dummyserver/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shutter-network/rolling-shutter/b4955a863b7c6eed05fd181b0397fb7477dcc72a/tools/snapshot/dummyserver/README.md -------------------------------------------------------------------------------- /tools/snapshot/dummyserver/dummyserver/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shutter-network/rolling-shutter/b4955a863b7c6eed05fd181b0397fb7477dcc72a/tools/snapshot/dummyserver/dummyserver/__init__.py -------------------------------------------------------------------------------- /tools/snapshot/dummyserver/dummyserver/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import signal 3 | 4 | import click 5 | from jsonrpcserver import method, Result, Success, serve 6 | from structlog import get_logger 7 | 8 | log = get_logger(__name__) 9 | 10 | 11 | @method 12 | def shutter_set_proposal_key(proposalId: str, key: str) -> Result: 13 | log.info("JSONRPC", method='shutter_set_proposal_key', proposal_id=proposalId, key=key) 14 | return Success(True) 15 | 16 | 17 | @method 18 | def shutter_set_eon_pubkey(eonId: str, key: str) -> Result: 19 | log.info("JSONRPC", method='shutter_set_eon_pubkey', eon_id=eonId, key=key) 20 | return Success(True) 21 | 22 | 23 | @click.command() 24 | @click.option('-h', '--host', default='0.0.0.0') 25 | @click.option('-p', '--port', default=5000) 26 | def main(host: str, port: int) -> None: 27 | signal.signal(signal.SIGTERM, lambda sig, frame: os.exit(0)) 28 | signal.signal(signal.SIGINT, lambda sig, frame: os.exit(0)) 29 | log.info("Starting Snapshot dummyserver on %s:%d", host, port) 30 | serve(host, port) 31 | 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /tools/snapshot/dummyserver/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "dummyserver" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Ulrich Petri "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | jsonrpcserver = "^5.0.9" 12 | click = "^8.1.3" 13 | structlog = "^23.1.0" 14 | 15 | [tool.poetry.scripts] 16 | dummyserver = "dummyserver.main:main" 17 | 18 | [build-system] 19 | requires = ["poetry-core"] 20 | build-backend = "poetry.core.masonry.api" 21 | --------------------------------------------------------------------------------