├── .cargo └── config.toml ├── .dockerignore ├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── adapters └── sovereign │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── lib.rs │ ├── service.rs │ ├── service │ └── client.rs │ ├── spec.rs │ ├── types │ ├── address.rs │ ├── blob.rs │ ├── block.rs │ ├── hash.rs │ ├── header.rs │ └── mod.rs │ └── verifier.rs ├── demo ├── rollkit │ ├── .gitignore │ ├── api │ │ └── gm │ │ │ └── gm │ │ │ ├── genesis.pulsar.go │ │ │ ├── module │ │ │ └── module.pulsar.go │ │ │ ├── params.pulsar.go │ │ │ ├── query.pulsar.go │ │ │ ├── query_grpc.pb.go │ │ │ ├── tx.pulsar.go │ │ │ └── tx_grpc.pb.go │ ├── app │ │ ├── app.go │ │ ├── app_config.go │ │ ├── export.go │ │ ├── genesis.go │ │ ├── genesis_account.go │ │ ├── ibc.go │ │ ├── sim_bench_test.go │ │ └── sim_test.go │ ├── buf.work.yaml │ ├── cmd │ │ └── gmd │ │ │ ├── cmd │ │ │ ├── commands.go │ │ │ ├── config.go │ │ │ └── root.go │ │ │ └── main.go │ ├── config.yml │ ├── docs │ │ ├── docs.go │ │ ├── static │ │ │ └── openapi.yml │ │ └── template │ │ │ └── index.tpl │ ├── go.mod │ ├── go.sum │ ├── init-local.sh │ ├── proto │ │ ├── buf.gen.gogo.yaml │ │ ├── buf.gen.pulsar.yaml │ │ ├── buf.gen.sta.yaml │ │ ├── buf.gen.swagger.yaml │ │ ├── buf.gen.ts.yaml │ │ ├── buf.lock │ │ ├── buf.yaml │ │ └── gm │ │ │ └── gm │ │ │ ├── genesis.proto │ │ │ ├── module │ │ │ └── module.proto │ │ │ ├── params.proto │ │ │ ├── query.proto │ │ │ └── tx.proto │ ├── readme.md │ ├── testutil │ │ ├── keeper │ │ │ └── gm.go │ │ ├── network │ │ │ └── network.go │ │ ├── nullify │ │ │ └── nullify.go │ │ └── sample │ │ │ └── sample.go │ ├── tools │ │ └── tools.go │ └── x │ │ └── gm │ │ ├── keeper │ │ ├── keeper.go │ │ ├── msg_server.go │ │ ├── msg_server_test.go │ │ ├── msg_update_params.go │ │ ├── msg_update_params_test.go │ │ ├── params.go │ │ ├── params_test.go │ │ ├── query.go │ │ ├── query_params.go │ │ └── query_params_test.go │ │ ├── module │ │ ├── autocli.go │ │ ├── genesis.go │ │ ├── genesis_test.go │ │ ├── module.go │ │ └── simulation.go │ │ ├── simulation │ │ └── helpers.go │ │ └── types │ │ ├── codec.go │ │ ├── errors.go │ │ ├── expected_keepers.go │ │ ├── genesis.go │ │ ├── genesis.pb.go │ │ ├── genesis_test.go │ │ ├── keys.go │ │ ├── msg_update_params.go │ │ ├── params.go │ │ ├── params.pb.go │ │ ├── query.pb.go │ │ ├── query.pb.gw.go │ │ ├── tx.pb.go │ │ └── types.go └── sovereign │ ├── .dockerignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── const-rollup-config │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs │ ├── constants.json │ ├── demo-rollup │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── benches │ │ ├── node │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── rollup_bench.rs │ │ │ ├── rollup_coarse_measure.rs │ │ │ └── rollup_config.toml │ │ └── prover │ │ │ ├── README.md │ │ │ ├── blocks.hex │ │ │ ├── prover_bench.rs │ │ │ └── rollup_config.toml │ ├── build.rs │ ├── proptest-regressions │ │ └── test_rpc.txt │ ├── provers │ │ └── risc0 │ │ │ ├── .cargo │ │ │ └── config.toml │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ ├── guest-ikura │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ └── rollup.rs │ │ │ ├── guest-mock │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ └── mock_da.rs │ │ │ └── src │ │ │ └── lib.rs │ ├── rollup_config.toml │ ├── src │ │ ├── bin │ │ │ └── sov_nft_script.rs │ │ ├── eth.rs │ │ ├── ikura_rollup.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── mock_rollup.rs │ │ ├── sov-cli │ │ │ ├── README.md │ │ │ └── main.rs │ │ └── test_rpc.rs │ ├── stf │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── genesis_config.rs │ │ │ ├── hooks_impl.rs │ │ │ ├── lib.rs │ │ │ ├── runtime.rs │ │ │ └── tests │ │ │ ├── da_simulation.rs │ │ │ ├── mod.rs │ │ │ ├── stf_tests.rs │ │ │ └── tx_revert_tests.rs │ ├── submitting_1.sh │ ├── submitting_2.sh │ ├── test_create_token.sh │ └── tests │ │ ├── all_tests.rs │ │ ├── bank │ │ └── mod.rs │ │ ├── evm │ │ ├── mod.rs │ │ ├── test_client.rs │ │ └── uniswap │ │ │ ├── .gitignore │ │ │ ├── Readme.md │ │ │ ├── WETH9.json │ │ │ ├── contracts │ │ │ ├── Tether.sol │ │ │ └── UsdCoin.sol │ │ │ ├── hardhat.config.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── scripts │ │ │ ├── 01_deploy.js │ │ │ └── 02_swap.js │ │ │ └── yarn.lock │ │ └── test_helpers.rs │ ├── docker │ ├── Dockerfile │ └── rollup_config.docker.toml │ ├── simple-nft-module │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── call.rs │ │ ├── genesis.rs │ │ ├── lib.rs │ │ ├── query.rs │ │ └── tests.rs │ └── tests │ │ └── nft_test.rs │ └── test-data │ ├── genesis │ ├── demo-tests │ │ ├── accounts.json │ │ ├── bank.json │ │ ├── chain_state.json │ │ ├── evm.json │ │ ├── nft.json │ │ ├── sequencer_registry.json │ │ └── value_setter.json │ └── integration-tests │ │ ├── accounts.json │ │ ├── bank.json │ │ ├── chain_state.json │ │ ├── evm.json │ │ ├── nft.json │ │ ├── sequencer_registry.json │ │ └── value_setter.json │ ├── keys │ ├── minter_private_key.json │ ├── token_deployer_private_key.json │ └── tx_signer_private_key.json │ └── requests │ ├── burn.json │ ├── create_token.json │ ├── mint.json │ ├── nft │ ├── create_collection.json │ ├── freeze_collection.json │ ├── mint_nft.json │ ├── transfer_nft.json │ └── update_token_uri.json │ ├── register_sequencer.json │ └── transfer.json ├── docker ├── docker-compose.yml ├── gha-runner.Dockerfile └── risczero.Dockerfile ├── docs-site ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── intro.md │ ├── node-operators │ │ └── getting-started.md │ └── protocol │ │ └── data-availability.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ ├── index.module.css │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ └── ikura_can_128.png └── yarn.lock ├── ikura ├── chain │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── node │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ ├── chain_spec │ │ │ ├── mod.rs │ │ │ └── res │ │ │ │ └── gondatsu.json │ │ │ ├── cli.rs │ │ │ ├── command.rs │ │ │ ├── command │ │ │ └── export_genesis_metadata.rs │ │ │ ├── main.rs │ │ │ ├── proposer.rs │ │ │ ├── rpc.rs │ │ │ └── service.rs │ ├── pallets │ │ ├── blobs │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ └── src │ │ │ │ ├── benchmarking.rs │ │ │ │ ├── lib.rs │ │ │ │ ├── mock.rs │ │ │ │ ├── namespace_param.rs │ │ │ │ ├── tests.rs │ │ │ │ └── weights.rs │ │ └── length-fee-adjustment │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── mock.rs │ │ │ └── tests.rs │ ├── primitives │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── namespace.rs │ └── runtimes │ │ ├── gondatsu │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── src │ │ │ ├── constants.rs │ │ │ ├── lib.rs │ │ │ ├── weights │ │ │ │ ├── block_weights.rs │ │ │ │ ├── extrinsic_weights.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── paritydb_weights.rs │ │ │ │ └── rocksdb_weights.rs │ │ │ └── xcm_config.rs │ │ └── tests │ │ │ ├── fee_adjustments.rs │ │ │ └── transactions.rs │ │ └── test │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── src │ │ ├── lib.rs │ │ ├── weights │ │ │ ├── block_weights.rs │ │ │ ├── extrinsic_weights.rs │ │ │ ├── mod.rs │ │ │ ├── paritydb_weights.rs │ │ │ └── rocksdb_weights.rs │ │ └── xcm_config.rs │ │ └── tests │ │ ├── fee_adjustments.rs │ │ └── transactions.rs ├── nmt │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── blob_metadata.rs │ │ ├── leaf.rs │ │ ├── lib.rs │ │ ├── ns.rs │ │ ├── ns_proof.rs │ │ ├── root.rs │ │ ├── tests.rs │ │ └── tree.rs ├── serde-util │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── shim │ ├── Cargo.toml │ ├── Dockerfile │ ├── build.rs │ ├── common │ │ └── sovereign │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ └── lib.rs │ ├── proto │ │ └── da.proto │ └── src │ │ ├── cli.rs │ │ ├── cmd │ │ ├── mod.rs │ │ ├── query │ │ │ ├── blob.rs │ │ │ ├── block.rs │ │ │ ├── mod.rs │ │ │ └── submit.rs │ │ └── serve.rs │ │ ├── dock │ │ ├── mod.rs │ │ ├── rollkit.rs │ │ ├── rpc_error.rs │ │ └── sovereign.rs │ │ ├── ikura_rpc │ │ ├── conn.rs │ │ └── mod.rs │ │ ├── key.rs │ │ └── main.rs └── subxt-autogen │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── gen.sh │ └── src │ ├── gen.rs │ └── lib.rs ├── testnet.toml ├── xtask ├── Cargo.toml └── src │ ├── build.rs │ ├── cli.rs │ ├── logging.rs │ ├── main.rs │ ├── shim.rs │ ├── sovereign.rs │ └── zombienet.rs └── zombienet.sh /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | xtest = "run --package xtask -- test" 4 | xzombienet = "run --package xtask -- zombienet" 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | /target/ 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style=space 4 | indent_size=space 5 | tab_width=4 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=100 10 | insert_final_newline=true 11 | 12 | [*.yml] 13 | indent_style=space 14 | indent_size=2 15 | tab_width=8 16 | end_of_line=lf 17 | 18 | [*.sh] 19 | indent_style=space 20 | indent_size=4 21 | tab_width=8 22 | end_of_line=lf 23 | 24 | [*.json] 25 | indent_style=space 26 | indent_size=2 27 | tab_width=8 28 | end_of_line=lf 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | fmt: 14 | name: fmt 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | - name: Install rustfmt from the rust stable toolchain 20 | uses: dtolnay/rust-toolchain@stable 21 | with: 22 | components: rustfmt 23 | - name: Rustfmt 24 | run: cargo fmt --all -- --check 25 | build_and_test: 26 | name: testing 27 | runs-on: self-hosted 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: cargo build 31 | run: cargo build --verbose --all 32 | - name: cargo test 33 | run: cargo test --verbose --all 34 | - name: cargo xtask test 35 | run: cargo xtask test 36 | - name: print logs (on failure) 37 | run: cat test_log/*.log 38 | if: failure() 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Common ignores 2 | .DS_Store 3 | .idea 4 | .vscode 5 | .envrc 6 | 7 | /zombienet 8 | /demo/sovereign/target 9 | /demo/sovereign/demo-rollup/demo_data 10 | /target 11 | /test_log 12 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /adapters/sovereign/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /adapters/sovereign/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-da-adapter" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | parity-scale-codec = { workspace = true } 14 | # The only required dependency for a DA adapter is the Sovereign SDK... 15 | sov-rollup-interface = { workspace = true } 16 | 17 | tracing = { workspace = true } 18 | serde = { workspace = true, default-features = true, features = ["derive"] } 19 | hex = { workspace = true, features = ["serde"] } 20 | # Derive borsh for instant implementations of the Sovereign SDK's encode/decode traits 21 | borsh = { workspace = true, features = ["bytes"] } 22 | 23 | # While not used by this crate, we need bytes to include serde. 24 | bytes = { workspace = true, features = ["serde"] } 25 | 26 | sha2 = { workspace = true } 27 | ikura-nmt = { workspace = true } 28 | jsonrpsee = { workspace = true, optional = true, features = ["ws-client"] } 29 | tokio = { workspace = true, optional = true } 30 | ikura-shim-common-sovereign = { workspace = true, optional = true, features = ["client"] } 31 | anyhow = { workspace = true } 32 | async-trait = { workspace = true } 33 | digest = { workspace = true } 34 | 35 | [features] 36 | default = ["native"] 37 | native = [ 38 | "sov-rollup-interface/native", 39 | "ikura-nmt/serde", 40 | "ikura-nmt/native", 41 | "dep:tokio", 42 | "dep:jsonrpsee", 43 | "dep:ikura-shim-common-sovereign", 44 | ] 45 | verifier = ["ikura-nmt/serde"] 46 | -------------------------------------------------------------------------------- /adapters/sovereign/README.md: -------------------------------------------------------------------------------- 1 | # Da Adapter Template 2 | 3 | This repository provides a template for implementing an adapter between the Sovereign SDK 4 | and a data availability layer. As the template suggests, such adapters are _libraries_, intended 5 | to be consumed by a downstream binary crate which implements the Rollup full node. 6 | 7 | To implement an adaptor, simply define the types used by your DA layer in the `DaSpec` in `lib.rs`, 8 | then implement the two core traits scaffolded in this repository - `DaVerifier` and `DaService`. 9 | 10 | ### The DaVerifier Trait 11 | 12 | The DaVerifier trait is the simpler of the two core traits. Its job is to take a list of BlobTransactions from a DA layer block 13 | and verify that the list is _complete_ and _correct_. Once deployed in a rollup, the data verified by this trait 14 | will be passed to the state transition function, so non-determinism should be strictly avoided. 15 | 16 | The logic inside this trait will get compiled down into your rollup's proof system, so it's important to have a high 17 | degree of confidence in its correctness (upgrading SNARKs is hard!) and think carefully about performance. 18 | 19 | At a bare minimum, you should ensure that the verifier rejects... 20 | 21 | 1. If the order of the blobs in an otherwise valid input is changed 22 | 1. If the sender of any of the blobs is tampered with 23 | 1. If any blob is omitted 24 | 1. If any blob is duplicated 25 | 1. If any extra blobs are added 26 | 27 | For compatibility, we also recommend (but don't require) that any logic in the `DaVerifier` be able to build with `no_std`. 28 | This maximizes your odds of being compatible with any given zk proof system. 29 | 30 | ### The DaService Trait 31 | 32 | The `DaService` trait is slightly more complicated than the `DaVerifier`. Thankfully, it exists entirely outside of the 33 | rollup's state machine - so it never has to be proven in zk. This means that its perfomance is less critical, and that 34 | upgrading it in response to a vulnerability is much easier. 35 | 36 | The job of the `DAService` is to allow the Sovereign SDK's node software to communicate with a DA layer. It has two related 37 | responsibilities. The first is to interact with DA layer nodes via RPC - retrieving data for the rollup as it becomes 38 | available. The second is to process that data into the form expected by the `DaVerifier`. For example, almost all DA layers 39 | provide data in JSON format via RPC - but, parsing JSON in a zk-SNARK would be horribly inefficient. So, the `DaService` 40 | is responsible for both querying the RPC service and transforming its responses into a more useful format. 41 | -------------------------------------------------------------------------------- /adapters/sovereign/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "native"), no_std)] 2 | 3 | extern crate alloc; 4 | 5 | #[cfg(feature = "native")] 6 | pub mod service; 7 | pub mod spec; 8 | pub mod types; 9 | pub mod verifier; 10 | -------------------------------------------------------------------------------- /adapters/sovereign/src/service/client.rs: -------------------------------------------------------------------------------- 1 | //! A subxt client that is sync to initialize, but provides async interface. 2 | 3 | use std::sync::Arc; 4 | use std::time::Duration; 5 | use tokio::sync::Mutex; 6 | 7 | #[derive(Clone)] 8 | pub struct Client { 9 | inner: Arc>, 10 | } 11 | 12 | struct Inner { 13 | url: String, 14 | request_timeout: Duration, 15 | client: Option, 16 | } 17 | 18 | #[derive(Clone)] 19 | pub struct ClientRef { 20 | client: Arc, 21 | } 22 | 23 | impl std::ops::Deref for ClientRef { 24 | type Target = jsonrpsee::ws_client::WsClient; 25 | 26 | fn deref(&self) -> &Self::Target { 27 | &self.client 28 | } 29 | } 30 | 31 | impl Client { 32 | pub fn new(url: String, request_timeout: Duration) -> Self { 33 | Self { 34 | inner: Arc::new(Mutex::new(Inner { 35 | url, 36 | request_timeout, 37 | client: None, 38 | })), 39 | } 40 | } 41 | 42 | pub async fn ensure_connected(&self) -> anyhow::Result { 43 | let mut inner = self.inner.lock().await; 44 | if let Some(client) = &inner.client { 45 | return Ok(client.clone()); 46 | } 47 | 48 | let client = jsonrpsee::ws_client::WsClientBuilder::new() 49 | .request_timeout(inner.request_timeout) 50 | .build(inner.url.clone()) 51 | .await?; 52 | let client = ClientRef { 53 | client: Arc::new(client), 54 | }; 55 | inner.client = Some(client.clone()); 56 | Ok(client) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /adapters/sovereign/src/spec.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::{Address, BlobTransaction, Hash, Header}, 3 | verifier::ChainValidityCondition, 4 | }; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] 8 | pub struct DaLayerSpec; 9 | 10 | pub struct ChainParams { 11 | pub namespace_id: [u8; ikura_nmt::NS_ID_SIZE], 12 | } 13 | 14 | impl sov_rollup_interface::da::DaSpec for DaLayerSpec { 15 | type SlotHash = Hash; 16 | type BlockHeader = Header; 17 | type BlobTransaction = BlobTransaction; 18 | type Address = Address; 19 | type ValidityCondition = ChainValidityCondition; 20 | type InclusionMultiProof = ikura_nmt::NamespaceProof; 21 | type CompletenessProof = (); 22 | type ChainParams = ChainParams; 23 | } 24 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/address.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{Display, Formatter}, 3 | str::FromStr, 4 | }; 5 | use serde::{Deserialize, Serialize}; 6 | use sov_rollup_interface::BasicAddress; 7 | 8 | #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Eq, Hash)] 9 | pub struct Address(pub [u8; 32]); 10 | 11 | impl BasicAddress for Address {} 12 | 13 | impl Display for Address { 14 | fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { 15 | let hash = hex::encode(&self.0); 16 | write!(f, "{hash}") 17 | } 18 | } 19 | 20 | impl AsRef<[u8]> for Address { 21 | fn as_ref(&self) -> &[u8] { 22 | self.0.as_ref() 23 | } 24 | } 25 | 26 | impl From<[u8; 32]> for Address { 27 | fn from(value: [u8; 32]) -> Self { 28 | Self(value) 29 | } 30 | } 31 | 32 | impl<'a> TryFrom<&'a [u8]> for Address { 33 | type Error = anyhow::Error; 34 | 35 | fn try_from(value: &'a [u8]) -> Result { 36 | Ok(Self(<[u8; 32]>::try_from(value)?)) 37 | } 38 | } 39 | 40 | impl FromStr for Address { 41 | type Err = anyhow::Error; 42 | fn from_str(_s: &str) -> Result { 43 | // TODO 44 | unimplemented!() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/blob.rs: -------------------------------------------------------------------------------- 1 | use super::{Address, Hash}; 2 | use alloc::vec::Vec; 3 | use serde::{Deserialize, Serialize}; 4 | use sov_rollup_interface::{da::CountedBufReader, Bytes}; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug)] 7 | pub struct BlobTransaction { 8 | pub sender: Address, 9 | /// Sha2 hash of the blob 10 | pub hash: Hash, 11 | pub blob: CountedBufReader, 12 | } 13 | 14 | impl BlobTransaction { 15 | pub fn new(sender: Address, blob: Vec) -> Self { 16 | use sha2::Digest; 17 | let hash: [u8; 32] = sha2::Sha256::digest(&blob).into(); 18 | let hash = Hash(hash); 19 | Self { 20 | sender, 21 | hash, 22 | blob: CountedBufReader::new(Bytes::from(blob)), 23 | } 24 | } 25 | } 26 | 27 | impl sov_rollup_interface::da::BlobReaderTrait for BlobTransaction { 28 | type Address = Address; 29 | 30 | fn sender(&self) -> Address { 31 | self.sender.clone() 32 | } 33 | 34 | fn hash(&self) -> [u8; 32] { 35 | self.hash.0 36 | } 37 | 38 | fn verified_data(&self) -> &[u8] { 39 | self.blob.accumulator() 40 | } 41 | 42 | #[cfg(feature = "native")] 43 | fn advance(&mut self, num_bytes: usize) -> &[u8] { 44 | self.blob.advance(num_bytes); 45 | self.verified_data() 46 | } 47 | 48 | fn total_len(&self) -> usize { 49 | self.blob.total_len() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/block.rs: -------------------------------------------------------------------------------- 1 | use super::{BlobTransaction, Header}; 2 | use crate::verifier::ChainValidityCondition; 3 | use alloc::vec::Vec; 4 | use serde::{Deserialize, Serialize}; 5 | use sov_rollup_interface::{da::BlockHeaderTrait, services::da::SlotData}; 6 | 7 | #[derive(Serialize, Deserialize, Clone, Debug)] 8 | pub struct Block { 9 | pub header: Header, 10 | pub transactions: Vec, 11 | pub blob_proof: ikura_nmt::NamespaceProof, 12 | } 13 | 14 | impl SlotData for Block { 15 | type BlockHeader = Header; 16 | type Cond = ChainValidityCondition; 17 | 18 | fn hash(&self) -> [u8; 32] { 19 | self.header.hash().0 20 | } 21 | 22 | fn header(&self) -> &Self::BlockHeader { 23 | &self.header 24 | } 25 | 26 | fn validity_condition(&self) -> Self::Cond { 27 | ChainValidityCondition { 28 | prev_hash: self.header.prev_hash().0, 29 | block_hash: self.header.hash().0, 30 | } 31 | } 32 | } 33 | 34 | impl PartialEq for Block { 35 | fn eq(&self, other: &Self) -> bool { 36 | self.header == other.header 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/hash.rs: -------------------------------------------------------------------------------- 1 | //! Definition of the hash. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use sov_rollup_interface::da::BlockHashTrait; 5 | 6 | #[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] 7 | pub struct Hash(pub [u8; 32]); 8 | 9 | impl BlockHashTrait for Hash {} 10 | 11 | impl AsRef<[u8]> for Hash { 12 | fn as_ref(&self) -> &[u8] { 13 | self.0.as_ref() 14 | } 15 | } 16 | 17 | impl From for [u8; 32] { 18 | fn from(hash: Hash) -> Self { 19 | hash.0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/header.rs: -------------------------------------------------------------------------------- 1 | //! Definition of the header. 2 | 3 | use super::Hash; 4 | use ikura_nmt::TreeRoot; 5 | use serde::{Deserialize, Serialize}; 6 | use sov_rollup_interface::da::{BlockHeaderTrait, Time}; 7 | 8 | #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] 9 | pub struct Header { 10 | pub hash: Hash, 11 | pub prev_hash: Hash, 12 | pub nmt_root: TreeRoot, 13 | pub height: u64, 14 | pub time: Time, 15 | } 16 | 17 | impl Header { 18 | pub fn new( 19 | hash: Hash, 20 | prev_hash: Hash, 21 | nmt_root: TreeRoot, 22 | height: u64, 23 | timestamp: u64, 24 | ) -> Self { 25 | Self { 26 | hash, 27 | prev_hash, 28 | nmt_root, 29 | height, 30 | // timestamp is in milliseconds so / 1000 31 | time: Time::from_secs((timestamp / 1000) as i64), 32 | } 33 | } 34 | } 35 | 36 | impl BlockHeaderTrait for Header { 37 | type Hash = Hash; 38 | 39 | fn prev_hash(&self) -> Self::Hash { 40 | self.prev_hash.clone() 41 | } 42 | 43 | fn hash(&self) -> Self::Hash { 44 | self.hash.clone() 45 | } 46 | 47 | fn height(&self) -> u64 { 48 | self.height 49 | } 50 | 51 | fn time(&self) -> Time { 52 | self.time.clone() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /adapters/sovereign/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod blob; 3 | mod block; 4 | mod hash; 5 | mod header; 6 | 7 | pub use address::Address; 8 | pub use blob::BlobTransaction; 9 | pub use block::Block; 10 | pub use hash::Hash; 11 | pub use header::Header; 12 | -------------------------------------------------------------------------------- /demo/rollkit/.gitignore: -------------------------------------------------------------------------------- 1 | vue/node_modules 2 | vue/dist 3 | release/ 4 | .idea/ 5 | .vscode/ 6 | .DS_Store 7 | *.dot 8 | *.log 9 | *.ign 10 | -------------------------------------------------------------------------------- /demo/rollkit/app/genesis.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // GenesisState of the blockchain is represented here as a map of raw json 8 | // messages key'd by a identifier string. 9 | // The identifier is used to determine which module genesis information belongs 10 | // to so it may be appropriately routed during init chain. 11 | // Within this application default genesis information is retrieved from 12 | // the ModuleBasicManager which populates json from each BasicModule 13 | // object provided to it during init. 14 | type GenesisState map[string]json.RawMessage 15 | -------------------------------------------------------------------------------- /demo/rollkit/app/genesis_account.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "errors" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 8 | ) 9 | 10 | var _ authtypes.GenesisAccount = (*GenesisAccount)(nil) 11 | 12 | // GenesisAccount defines a type that implements the GenesisAccount interface 13 | // to be used for simulation accounts in the genesis state. 14 | type GenesisAccount struct { 15 | *authtypes.BaseAccount 16 | 17 | // vesting account fields 18 | OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization 19 | DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation 20 | DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation 21 | StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time) 22 | EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time) 23 | 24 | // module account fields 25 | ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account 26 | ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account 27 | } 28 | 29 | // Validate checks for errors on the vesting and module account parameters 30 | func (sga GenesisAccount) Validate() error { 31 | if !sga.OriginalVesting.IsZero() { 32 | if sga.StartTime >= sga.EndTime { 33 | return errors.New("vesting start-time cannot be before end-time") 34 | } 35 | } 36 | 37 | if sga.ModuleName != "" { 38 | ma := authtypes.ModuleAccount{ 39 | BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions, 40 | } 41 | if err := ma.Validate(); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return sga.BaseAccount.Validate() 47 | } 48 | -------------------------------------------------------------------------------- /demo/rollkit/buf.work.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | directories: 3 | - proto 4 | -------------------------------------------------------------------------------- /demo/rollkit/cmd/gmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 8 | 9 | "gm/app" 10 | "gm/cmd/gmd/cmd" 11 | ) 12 | 13 | func main() { 14 | rootCmd := cmd.NewRootCmd() 15 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { 16 | fmt.Fprintln(rootCmd.OutOrStderr(), err) 17 | os.Exit(1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/rollkit/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | accounts: 3 | - name: alice 4 | coins: 5 | - 20000token 6 | - 200000000stake 7 | - name: bob 8 | coins: 9 | - 10000token 10 | - 100000000stake 11 | client: 12 | openapi: 13 | path: docs/static/openapi.yml 14 | faucet: 15 | name: bob 16 | coins: 17 | - 5token 18 | - 100000stake 19 | validators: 20 | - name: alice 21 | bonded: 100000000stake 22 | -------------------------------------------------------------------------------- /demo/rollkit/docs/docs.go: -------------------------------------------------------------------------------- 1 | package docs 2 | 3 | import ( 4 | "embed" 5 | httptemplate "html/template" 6 | "net/http" 7 | 8 | "github.com/gorilla/mux" 9 | ) 10 | 11 | const ( 12 | apiFile = "/static/openapi.yml" 13 | indexFile = "template/index.tpl" 14 | ) 15 | 16 | //go:embed static 17 | var Static embed.FS 18 | 19 | //go:embed template 20 | var template embed.FS 21 | 22 | func RegisterOpenAPIService(appName string, rtr *mux.Router) { 23 | rtr.Handle(apiFile, http.FileServer(http.FS(Static))) 24 | rtr.HandleFunc("/", handler(appName)) 25 | } 26 | 27 | // handler returns an http handler that servers OpenAPI console for an OpenAPI spec at specURL. 28 | func handler(title string) http.HandlerFunc { 29 | t, _ := httptemplate.ParseFS(template, indexFile) 30 | 31 | return func(w http.ResponseWriter, req *http.Request) { 32 | t.Execute(w, struct { 33 | Title string 34 | URL string 35 | }{ 36 | title, 37 | apiFile, 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/rollkit/docs/template/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ .Title }} 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 24 | 25 | 26 | Footer 27 | © 2022 GitHub, Inc. 28 | Footer navigation 29 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.gen.gogo.yaml: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from Ignite. You can edit 2 | # the file content but do not change the file name or path. 3 | # 4 | # buf.gen.gogo.yaml 5 | # 6 | version: v1 7 | plugins: 8 | - name: gocosmos 9 | out: . 10 | opt: 11 | - plugins=grpc 12 | - Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types 13 | - Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm 14 | - name: grpc-gateway 15 | out: . 16 | opt: 17 | - logtostderr=true 18 | - allow_colon_final_segments=true 19 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.gen.pulsar.yaml: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from Ignite. You can edit 2 | # the file content but do not change the file name or path. 3 | # 4 | # buf.gen.pulsar.yaml 5 | # 6 | version: v1 7 | managed: 8 | enabled: true 9 | go_package_prefix: 10 | default: cosmossdk.io/api 11 | except: 12 | - buf.build/googleapis/googleapis 13 | - buf.build/cosmos/gogo-proto 14 | - buf.build/cosmos/cosmos-proto 15 | override: 16 | plugins: 17 | - name: go-pulsar 18 | out: ./api 19 | opt: paths=source_relative 20 | - name: go-grpc 21 | out: ./api 22 | opt: paths=source_relative 23 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.gen.sta.yaml: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from Ignite. You can edit 2 | # the file content but do not change the file name or path. 3 | # 4 | # buf.gen.sta.yaml 5 | # 6 | version: v1 7 | plugins: 8 | - name: openapiv2 9 | out: . 10 | opt: 11 | - logtostderr=true 12 | - openapi_naming_strategy=simple 13 | - ignore_comments=true 14 | - simple_operation_ids=false 15 | - json_names_for_fields=false 16 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.gen.swagger.yaml: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from Ignite. You can edit 2 | # the file content but do not change the file name or path. 3 | # 4 | # buf.gen.swagger.yaml 5 | # 6 | version: v1 7 | plugins: 8 | - name: openapiv2 9 | out: . 10 | opt: 11 | - logtostderr=true 12 | - openapi_naming_strategy=fqn 13 | - json_names_for_fields=false 14 | - generate_unbound_methods=true -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.gen.ts.yaml: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from Ignite. You can edit 2 | # the file content but do not change the file name or path. 3 | # 4 | # buf.gen.ts.yaml 5 | # 6 | version: v1 7 | managed: 8 | enabled: true 9 | plugins: 10 | - plugin: buf.build/community/stephenh-ts-proto 11 | out: . 12 | opt: 13 | - logtostderr=true 14 | - allow_merge=true 15 | - json_names_for_fields=false 16 | - ts_proto_opt=snakeToCamel=true 17 | - ts_proto_opt=esModuleInterop=true 18 | - ts_proto_out=. 19 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: cosmos 6 | repository: cosmos-proto 7 | commit: 1935555c206d4afb9e94615dfd0fad31 8 | digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377 9 | - remote: buf.build 10 | owner: cosmos 11 | repository: cosmos-sdk 12 | commit: aa25660f4ff746388669ce36b3778442 13 | digest: shake256:a20eb29eb7284d9d0b76e94324a6e24e3665d13682bed0d5beac647d7109b7b2f22080301276779a91f394c97dab334da36dfc01d4252d9f869b090bfc8248aa 14 | - remote: buf.build 15 | owner: cosmos 16 | repository: gogo-proto 17 | commit: 34d970b699f84aa382f3c29773a60836 18 | digest: shake256:3d3bee5229ba579e7d19ffe6e140986a228b48a8c7fe74348f308537ab95e9135210e81812489d42cd8941d33ff71f11583174ccc5972e86e6112924b6ce9f04 19 | - remote: buf.build 20 | owner: cosmos 21 | repository: ibc 22 | commit: b32ecf3ebbcb45f3b727ae95d9ea317d 23 | digest: shake256:0010f681e8a1b37d5331dbd93fa07486572caaea564137fa040438e81c99b7f516adcef4991752972331771d95e757b794c4f28ffcd0f6c832f0793f8089f30d 24 | - remote: buf.build 25 | owner: cosmos 26 | repository: ics23 27 | commit: 3c44d8daa8b44059ac744cd17d4a49d7 28 | - remote: buf.build 29 | owner: googleapis 30 | repository: googleapis 31 | commit: 75b4300737fb4efca0831636be94e517 32 | - remote: buf.build 33 | owner: protocolbuffers 34 | repository: wellknowntypes 35 | commit: 44e83bc050a4497fa7b36b34d95ca156 36 | -------------------------------------------------------------------------------- /demo/rollkit/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | breaking: 2 | use: 3 | - FILE 4 | deps: 5 | - buf.build/protocolbuffers/wellknowntypes 6 | - buf.build/cosmos/cosmos-sdk 7 | - buf.build/cosmos/cosmos-proto 8 | - buf.build/cosmos/gogo-proto 9 | - buf.build/googleapis/googleapis 10 | - buf.build/cosmos/ics23 11 | - buf.build/cosmos/ibc 12 | lint: 13 | except: 14 | - UNARY_RPC 15 | - COMMENT_FIELD 16 | - SERVICE_SUFFIX 17 | - PACKAGE_VERSION_SUFFIX 18 | - RPC_REQUEST_STANDARD_NAME 19 | ignore: 20 | - tendermint 21 | use: 22 | - DEFAULT 23 | - COMMENTS 24 | - FILE_LOWER_SNAKE_CASE 25 | version: v1 26 | -------------------------------------------------------------------------------- /demo/rollkit/proto/gm/gm/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package gm.gm; 3 | 4 | import "amino/amino.proto"; 5 | import "gogoproto/gogo.proto"; 6 | import "gm/gm/params.proto"; 7 | 8 | option go_package = "gm/x/gm/types"; 9 | 10 | // GenesisState defines the gm module's genesis state. 11 | message GenesisState { 12 | // params defines all the parameters of the module. 13 | Params params = 1 [ 14 | (gogoproto.nullable) = false, 15 | (amino.dont_omitempty) = true 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /demo/rollkit/proto/gm/gm/module/module.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package gm.gm.module; 3 | 4 | import "cosmos/app/v1alpha1/module.proto"; 5 | 6 | // Module is the config object for the module. 7 | message Module { 8 | option (cosmos.app.v1alpha1.module) = { 9 | go_import: "gm/x/gm" 10 | }; 11 | 12 | // authority defines the custom module authority. If not set, defaults to the governance module. 13 | string authority = 1; 14 | } -------------------------------------------------------------------------------- /demo/rollkit/proto/gm/gm/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package gm.gm; 3 | 4 | import "amino/amino.proto"; 5 | import "gogoproto/gogo.proto"; 6 | 7 | option go_package = "gm/x/gm/types"; 8 | 9 | // Params defines the parameters for the module. 10 | message Params { 11 | option (amino.name) = "gm/x/gm/Params"; 12 | option (gogoproto.equal) = true; 13 | 14 | 15 | } -------------------------------------------------------------------------------- /demo/rollkit/proto/gm/gm/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package gm.gm; 3 | 4 | import "amino/amino.proto"; 5 | import "gogoproto/gogo.proto"; 6 | import "google/api/annotations.proto"; 7 | import "cosmos/base/query/v1beta1/pagination.proto"; 8 | import "gm/gm/params.proto"; 9 | 10 | option go_package = "gm/x/gm/types"; 11 | 12 | // Query defines the gRPC querier service. 13 | service Query { 14 | // Parameters queries the parameters of the module. 15 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { 16 | option (google.api.http).get = "/gm/gm/params"; 17 | } 18 | } 19 | 20 | // QueryParamsRequest is request type for the Query/Params RPC method. 21 | message QueryParamsRequest {} 22 | 23 | // QueryParamsResponse is response type for the Query/Params RPC method. 24 | message QueryParamsResponse { 25 | // params holds all the parameters of this module. 26 | Params params = 1 [ 27 | (gogoproto.nullable) = false, 28 | (amino.dont_omitempty) = true 29 | ]; 30 | } -------------------------------------------------------------------------------- /demo/rollkit/proto/gm/gm/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package gm.gm; 3 | 4 | import "amino/amino.proto"; 5 | import "cosmos/msg/v1/msg.proto"; 6 | import "cosmos_proto/cosmos.proto"; 7 | import "gogoproto/gogo.proto"; 8 | import "gm/gm/params.proto"; 9 | 10 | option go_package = "gm/x/gm/types"; 11 | 12 | // Msg defines the Msg service. 13 | service Msg { 14 | option (cosmos.msg.v1.service) = true; 15 | 16 | // UpdateParams defines a (governance) operation for updating the module 17 | // parameters. The authority defaults to the x/gov module account. 18 | rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); 19 | } 20 | 21 | // MsgUpdateParams is the Msg/UpdateParams request type. 22 | message MsgUpdateParams { 23 | option (cosmos.msg.v1.signer) = "authority"; 24 | option (amino.name) = "gm/x/gm/MsgUpdateParams"; 25 | 26 | // authority is the address that controls the module (defaults to x/gov unless overwritten). 27 | string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; 28 | 29 | // params defines the module parameters to update. 30 | // 31 | // NOTE: All parameters must be supplied. 32 | Params params = 2 [ 33 | (gogoproto.nullable) = false, 34 | (amino.dont_omitempty) = true 35 | ]; 36 | } 37 | 38 | // MsgUpdateParamsResponse defines the response structure for executing a 39 | // MsgUpdateParams message. 40 | message MsgUpdateParamsResponse {} -------------------------------------------------------------------------------- /demo/rollkit/readme.md: -------------------------------------------------------------------------------- 1 | # gm 2 | **gm** is a blockchain built using Cosmos SDK and Tendermint and created with [Ignite CLI](https://ignite.com/cli). 3 | 4 | ## Get started 5 | 6 | ``` 7 | ignite chain serve 8 | ``` 9 | 10 | `serve` command installs dependencies, builds, initializes, and starts your blockchain in development. 11 | 12 | ### Configure 13 | 14 | Your blockchain in development can be configured with `config.yml`. To learn more, see the [Ignite CLI docs](https://docs.ignite.com). 15 | 16 | ### Web Frontend 17 | 18 | Additionally, Ignite CLI offers both Vue and React options for frontend scaffolding: 19 | 20 | For a Vue frontend, use: `ignite scaffold vue` 21 | For a React frontend, use: `ignite scaffold react` 22 | These commands can be run within your scaffolded blockchain project. 23 | 24 | 25 | For more information see the [monorepo for Ignite front-end development](https://github.com/ignite/web). 26 | 27 | ## Release 28 | To release a new version of your blockchain, create and push a new tag with `v` prefix. A new draft release with the configured targets will be created. 29 | 30 | ``` 31 | git tag v0.1 32 | git push origin v0.1 33 | ``` 34 | 35 | After a draft release is created, make your final changes from the release page and publish it. 36 | 37 | ### Install 38 | To install the latest version of your blockchain node's binary, execute the following command on your machine: 39 | 40 | ``` 41 | curl https://get.ignite.com/username/gm@latest! | sudo bash 42 | ``` 43 | `username/gm` should match the `username` and `repo_name` of the Github repository to which the source code was pushed. Learn more about [the install process](https://github.com/allinbits/starport-installer). 44 | 45 | ## Learn more 46 | 47 | - [Ignite CLI](https://ignite.com/cli) 48 | - [Tutorials](https://docs.ignite.com/guide) 49 | - [Ignite CLI docs](https://docs.ignite.com) 50 | - [Cosmos SDK docs](https://docs.cosmos.network) 51 | - [Developer Chat](https://discord.gg/ignite) 52 | -------------------------------------------------------------------------------- /demo/rollkit/testutil/keeper/gm.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "testing" 5 | 6 | "cosmossdk.io/log" 7 | "cosmossdk.io/store" 8 | "cosmossdk.io/store/metrics" 9 | storetypes "cosmossdk.io/store/types" 10 | cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 11 | dbm "github.com/cosmos/cosmos-db" 12 | "github.com/cosmos/cosmos-sdk/codec" 13 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 14 | "github.com/cosmos/cosmos-sdk/runtime" 15 | sdk "github.com/cosmos/cosmos-sdk/types" 16 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 17 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" 18 | "github.com/stretchr/testify/require" 19 | 20 | "gm/x/gm/keeper" 21 | "gm/x/gm/types" 22 | ) 23 | 24 | func GmKeeper(t testing.TB) (keeper.Keeper, sdk.Context) { 25 | storeKey := storetypes.NewKVStoreKey(types.StoreKey) 26 | 27 | db := dbm.NewMemDB() 28 | stateStore := store.NewCommitMultiStore(db, log.NewNopLogger(), metrics.NewNoOpMetrics()) 29 | stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) 30 | require.NoError(t, stateStore.LoadLatestVersion()) 31 | 32 | registry := codectypes.NewInterfaceRegistry() 33 | cdc := codec.NewProtoCodec(registry) 34 | authority := authtypes.NewModuleAddress(govtypes.ModuleName) 35 | 36 | k := keeper.NewKeeper( 37 | cdc, 38 | runtime.NewKVStoreService(storeKey), 39 | log.NewNopLogger(), 40 | authority.String(), 41 | ) 42 | 43 | ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger()) 44 | 45 | // Initialize params 46 | k.SetParams(ctx, types.DefaultParams()) 47 | 48 | return k, ctx 49 | } 50 | -------------------------------------------------------------------------------- /demo/rollkit/testutil/network/network.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/cosmos/cosmos-sdk/testutil/network" 8 | "github.com/stretchr/testify/require" 9 | 10 | "gm/app" 11 | ) 12 | 13 | type ( 14 | Network = network.Network 15 | Config = network.Config 16 | ) 17 | 18 | // New creates instance with fully configured cosmos network. 19 | // Accepts optional config, that will be used in place of the DefaultConfig() if provided. 20 | func New(t *testing.T, configs ...Config) *Network { 21 | t.Helper() 22 | if len(configs) > 1 { 23 | panic("at most one config should be provided") 24 | } 25 | var cfg network.Config 26 | if len(configs) == 0 { 27 | cfg = DefaultConfig() 28 | } else { 29 | cfg = configs[0] 30 | } 31 | net, err := network.New(t, t.TempDir(), cfg) 32 | require.NoError(t, err) 33 | _, err = net.WaitForHeight(1) 34 | require.NoError(t, err) 35 | t.Cleanup(net.Cleanup) 36 | return net 37 | } 38 | 39 | // DefaultConfig will initialize config for the network with custom application, 40 | // genesis and single validator. All other parameters are inherited from cosmos-sdk/testutil/network.DefaultConfig 41 | func DefaultConfig() network.Config { 42 | cfg, err := network.DefaultConfigWithAppConfig(app.AppConfig()) 43 | if err != nil { 44 | panic(err) 45 | } 46 | ports, err := freePorts(3) 47 | if err != nil { 48 | panic(err) 49 | } 50 | if cfg.APIAddress == "" { 51 | cfg.APIAddress = fmt.Sprintf("tcp://0.0.0.0:%s", ports[0]) 52 | } 53 | if cfg.RPCAddress == "" { 54 | cfg.RPCAddress = fmt.Sprintf("tcp://0.0.0.0:%s", ports[1]) 55 | } 56 | if cfg.GRPCAddress == "" { 57 | cfg.GRPCAddress = fmt.Sprintf("0.0.0.0:%s", ports[2]) 58 | } 59 | return cfg 60 | } 61 | 62 | // freePorts return the available ports based on the number of requested ports. 63 | func freePorts(n int) ([]string, error) { 64 | closeFns := make([]func() error, n) 65 | ports := make([]string, n) 66 | for i := 0; i < n; i++ { 67 | _, port, closeFn, err := network.FreeTCPAddr() 68 | if err != nil { 69 | return nil, err 70 | } 71 | ports[i] = port 72 | closeFns[i] = closeFn 73 | } 74 | for _, closeFn := range closeFns { 75 | if err := closeFn(); err != nil { 76 | return nil, err 77 | } 78 | } 79 | return ports, nil 80 | } 81 | -------------------------------------------------------------------------------- /demo/rollkit/testutil/nullify/nullify.go: -------------------------------------------------------------------------------- 1 | // Package nullify provides methods to init nil values structs for test assertion. 2 | package nullify 3 | 4 | import ( 5 | "reflect" 6 | "unsafe" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | var ( 12 | coinType = reflect.TypeOf(sdk.Coin{}) 13 | coinsType = reflect.TypeOf(sdk.Coins{}) 14 | ) 15 | 16 | // Fill analyze all struct fields and slices with 17 | // reflection and initialize the nil and empty slices, 18 | // structs, and pointers. 19 | func Fill(x interface{}) interface{} { 20 | v := reflect.Indirect(reflect.ValueOf(x)) 21 | switch v.Kind() { 22 | case reflect.Slice: 23 | for i := 0; i < v.Len(); i++ { 24 | obj := v.Index(i) 25 | objPt := reflect.NewAt(obj.Type(), unsafe.Pointer(obj.UnsafeAddr())).Interface() 26 | objPt = Fill(objPt) 27 | obj.Set(reflect.ValueOf(objPt)) 28 | } 29 | case reflect.Struct: 30 | for i := 0; i < v.NumField(); i++ { 31 | f := reflect.Indirect(v.Field(i)) 32 | if !f.CanSet() { 33 | continue 34 | } 35 | switch f.Kind() { 36 | case reflect.Slice: 37 | f.Set(reflect.MakeSlice(f.Type(), 0, 0)) 38 | case reflect.Struct: 39 | switch f.Type() { 40 | case coinType: 41 | coin := reflect.New(coinType).Interface() 42 | s := reflect.ValueOf(coin).Elem() 43 | f.Set(s) 44 | case coinsType: 45 | coins := reflect.New(coinsType).Interface() 46 | s := reflect.ValueOf(coins).Elem() 47 | f.Set(s) 48 | default: 49 | objPt := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Interface() 50 | s := Fill(objPt) 51 | f.Set(reflect.ValueOf(s)) 52 | } 53 | } 54 | } 55 | } 56 | return reflect.Indirect(v).Interface() 57 | } 58 | -------------------------------------------------------------------------------- /demo/rollkit/testutil/sample/sample.go: -------------------------------------------------------------------------------- 1 | package sample 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | // AccAddress returns a sample account address 9 | func AccAddress() string { 10 | pk := ed25519.GenPrivKey().PubKey() 11 | addr := pk.Address() 12 | return sdk.AccAddress(addr).String() 13 | } 14 | -------------------------------------------------------------------------------- /demo/rollkit/tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/bufbuild/buf/cmd/buf" 7 | _ "github.com/cosmos/cosmos-proto/cmd/protoc-gen-go-pulsar" 8 | _ "github.com/cosmos/gogoproto/protoc-gen-gocosmos" 9 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" 10 | _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" 11 | _ "golang.org/x/tools/cmd/goimports" 12 | _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" 13 | _ "google.golang.org/protobuf/cmd/protoc-gen-go" 14 | ) 15 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "cosmossdk.io/core/store" 7 | "cosmossdk.io/log" 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | 11 | "gm/x/gm/types" 12 | ) 13 | 14 | type ( 15 | Keeper struct { 16 | cdc codec.BinaryCodec 17 | storeService store.KVStoreService 18 | logger log.Logger 19 | 20 | // the address capable of executing a MsgUpdateParams message. Typically, this 21 | // should be the x/gov module account. 22 | authority string 23 | } 24 | ) 25 | 26 | func NewKeeper( 27 | cdc codec.BinaryCodec, 28 | storeService store.KVStoreService, 29 | logger log.Logger, 30 | authority string, 31 | 32 | ) Keeper { 33 | if _, err := sdk.AccAddressFromBech32(authority); err != nil { 34 | panic(fmt.Sprintf("invalid authority address: %s", authority)) 35 | } 36 | 37 | return Keeper{ 38 | cdc: cdc, 39 | storeService: storeService, 40 | authority: authority, 41 | logger: logger, 42 | } 43 | } 44 | 45 | // GetAuthority returns the module's authority. 46 | func (k Keeper) GetAuthority() string { 47 | return k.authority 48 | } 49 | 50 | // Logger returns a module-specific logger. 51 | func (k Keeper) Logger() log.Logger { 52 | return k.logger.With("module", fmt.Sprintf("x/%s", types.ModuleName)) 53 | } 54 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/msg_server.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "gm/x/gm/types" 5 | ) 6 | 7 | type msgServer struct { 8 | Keeper 9 | } 10 | 11 | // NewMsgServerImpl returns an implementation of the MsgServer interface 12 | // for the provided Keeper. 13 | func NewMsgServerImpl(keeper Keeper) types.MsgServer { 14 | return &msgServer{Keeper: keeper} 15 | } 16 | 17 | var _ types.MsgServer = msgServer{} 18 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/msg_server_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | keepertest "gm/testutil/keeper" 10 | "gm/x/gm/keeper" 11 | "gm/x/gm/types" 12 | ) 13 | 14 | func setupMsgServer(t testing.TB) (keeper.Keeper, types.MsgServer, context.Context) { 15 | k, ctx := keepertest.GmKeeper(t) 16 | return k, keeper.NewMsgServerImpl(k), ctx 17 | } 18 | 19 | func TestMsgServer(t *testing.T) { 20 | k, ms, ctx := setupMsgServer(t) 21 | require.NotNil(t, ms) 22 | require.NotNil(t, ctx) 23 | require.NotEmpty(t, k) 24 | } 25 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/msg_update_params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | errorsmod "cosmossdk.io/errors" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | 9 | "gm/x/gm/types" 10 | ) 11 | 12 | func (k msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { 13 | if k.GetAuthority() != req.Authority { 14 | return nil, errorsmod.Wrapf(types.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), req.Authority) 15 | } 16 | 17 | ctx := sdk.UnwrapSDKContext(goCtx) 18 | if err := k.SetParams(ctx, req.Params); err != nil { 19 | return nil, err 20 | } 21 | 22 | return &types.MsgUpdateParamsResponse{}, nil 23 | } 24 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/msg_update_params_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/stretchr/testify/require" 8 | 9 | "gm/x/gm/types" 10 | ) 11 | 12 | func TestMsgUpdateParams(t *testing.T) { 13 | k, ms, ctx := setupMsgServer(t) 14 | params := types.DefaultParams() 15 | require.NoError(t, k.SetParams(ctx, params)) 16 | wctx := sdk.UnwrapSDKContext(ctx) 17 | 18 | // default params 19 | testCases := []struct { 20 | name string 21 | input *types.MsgUpdateParams 22 | expErr bool 23 | expErrMsg string 24 | }{ 25 | { 26 | name: "invalid authority", 27 | input: &types.MsgUpdateParams{ 28 | Authority: "invalid", 29 | Params: params, 30 | }, 31 | expErr: true, 32 | expErrMsg: "invalid authority", 33 | }, 34 | { 35 | name: "send enabled param", 36 | input: &types.MsgUpdateParams{ 37 | Authority: k.GetAuthority(), 38 | Params: types.Params{}, 39 | }, 40 | expErr: false, 41 | }, 42 | { 43 | name: "all good", 44 | input: &types.MsgUpdateParams{ 45 | Authority: k.GetAuthority(), 46 | Params: params, 47 | }, 48 | expErr: false, 49 | }, 50 | } 51 | 52 | for _, tc := range testCases { 53 | t.Run(tc.name, func(t *testing.T) { 54 | _, err := ms.UpdateParams(wctx, tc.input) 55 | 56 | if tc.expErr { 57 | require.Error(t, err) 58 | require.Contains(t, err.Error(), tc.expErrMsg) 59 | } else { 60 | require.NoError(t, err) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cosmos/cosmos-sdk/runtime" 7 | 8 | "gm/x/gm/types" 9 | ) 10 | 11 | // GetParams get all parameters as types.Params 12 | func (k Keeper) GetParams(ctx context.Context) (params types.Params) { 13 | store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) 14 | bz := store.Get(types.ParamsKey) 15 | if bz == nil { 16 | return params 17 | } 18 | 19 | k.cdc.MustUnmarshal(bz, ¶ms) 20 | return params 21 | } 22 | 23 | // SetParams set the params 24 | func (k Keeper) SetParams(ctx context.Context, params types.Params) error { 25 | store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) 26 | bz, err := k.cdc.Marshal(¶ms) 27 | if err != nil { 28 | return err 29 | } 30 | store.Set(types.ParamsKey, bz) 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/params_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | keepertest "gm/testutil/keeper" 9 | "gm/x/gm/types" 10 | ) 11 | 12 | func TestGetParams(t *testing.T) { 13 | k, ctx := keepertest.GmKeeper(t) 14 | params := types.DefaultParams() 15 | 16 | require.NoError(t, k.SetParams(ctx, params)) 17 | require.EqualValues(t, params, k.GetParams(ctx)) 18 | } 19 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "gm/x/gm/types" 5 | ) 6 | 7 | var _ types.QueryServer = Keeper{} 8 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/query_params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "google.golang.org/grpc/codes" 8 | "google.golang.org/grpc/status" 9 | 10 | "gm/x/gm/types" 11 | ) 12 | 13 | func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { 14 | if req == nil { 15 | return nil, status.Error(codes.InvalidArgument, "invalid request") 16 | } 17 | ctx := sdk.UnwrapSDKContext(goCtx) 18 | 19 | return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil 20 | } 21 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/keeper/query_params_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | keepertest "gm/testutil/keeper" 9 | "gm/x/gm/types" 10 | ) 11 | 12 | func TestParamsQuery(t *testing.T) { 13 | keeper, ctx := keepertest.GmKeeper(t) 14 | params := types.DefaultParams() 15 | require.NoError(t, keeper.SetParams(ctx, params)) 16 | 17 | response, err := keeper.Params(ctx, &types.QueryParamsRequest{}) 18 | require.NoError(t, err) 19 | require.Equal(t, &types.QueryParamsResponse{Params: params}, response) 20 | } 21 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/module/autocli.go: -------------------------------------------------------------------------------- 1 | package gm 2 | 3 | import ( 4 | autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" 5 | 6 | modulev1 "gm/api/gm/gm" 7 | ) 8 | 9 | // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface. 10 | func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { 11 | return &autocliv1.ModuleOptions{ 12 | Query: &autocliv1.ServiceCommandDescriptor{ 13 | Service: modulev1.Query_ServiceDesc.ServiceName, 14 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 15 | { 16 | RpcMethod: "Params", 17 | Use: "params", 18 | Short: "Shows the parameters of the module", 19 | }, 20 | // this line is used by ignite scaffolding # autocli/query 21 | }, 22 | }, 23 | Tx: &autocliv1.ServiceCommandDescriptor{ 24 | Service: modulev1.Msg_ServiceDesc.ServiceName, 25 | EnhanceCustomCommand: true, // only required if you want to use the custom command 26 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 27 | { 28 | RpcMethod: "UpdateParams", 29 | Skip: true, // skipped because authority gated 30 | }, 31 | // this line is used by ignite scaffolding # autocli/tx 32 | }, 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/module/genesis.go: -------------------------------------------------------------------------------- 1 | package gm 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "gm/x/gm/keeper" 7 | "gm/x/gm/types" 8 | ) 9 | 10 | // InitGenesis initializes the module's state from a provided genesis state. 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 12 | // this line is used by starport scaffolding # genesis/module/init 13 | k.SetParams(ctx, genState.Params) 14 | } 15 | 16 | // ExportGenesis returns the module's exported genesis. 17 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 18 | genesis := types.DefaultGenesis() 19 | genesis.Params = k.GetParams(ctx) 20 | 21 | // this line is used by starport scaffolding # genesis/module/export 22 | 23 | return genesis 24 | } 25 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/module/genesis_test.go: -------------------------------------------------------------------------------- 1 | package gm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | keepertest "gm/testutil/keeper" 7 | "gm/testutil/nullify" 8 | gm "gm/x/gm/module" 9 | "gm/x/gm/types" 10 | 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestGenesis(t *testing.T) { 15 | genesisState := types.GenesisState{ 16 | Params: types.DefaultParams(), 17 | 18 | // this line is used by starport scaffolding # genesis/test/state 19 | } 20 | 21 | k, ctx := keepertest.GmKeeper(t) 22 | gm.InitGenesis(ctx, k, genesisState) 23 | got := gm.ExportGenesis(ctx, k) 24 | require.NotNil(t, got) 25 | 26 | nullify.Fill(&genesisState) 27 | nullify.Fill(got) 28 | 29 | // this line is used by starport scaffolding # genesis/test/assert 30 | } 31 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/module/simulation.go: -------------------------------------------------------------------------------- 1 | package gm 2 | 3 | import ( 4 | "math/rand" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/module" 8 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 9 | "github.com/cosmos/cosmos-sdk/x/simulation" 10 | 11 | "gm/testutil/sample" 12 | gmsimulation "gm/x/gm/simulation" 13 | "gm/x/gm/types" 14 | ) 15 | 16 | // avoid unused import issue 17 | var ( 18 | _ = gmsimulation.FindAccount 19 | _ = rand.Rand{} 20 | _ = sample.AccAddress 21 | _ = sdk.AccAddress{} 22 | _ = simulation.MsgEntryKind 23 | ) 24 | 25 | const ( 26 | // this line is used by starport scaffolding # simapp/module/const 27 | ) 28 | 29 | // GenerateGenesisState creates a randomized GenState of the module. 30 | func (AppModule) GenerateGenesisState(simState *module.SimulationState) { 31 | accs := make([]string, len(simState.Accounts)) 32 | for i, acc := range simState.Accounts { 33 | accs[i] = acc.Address.String() 34 | } 35 | gmGenesis := types.GenesisState{ 36 | Params: types.DefaultParams(), 37 | // this line is used by starport scaffolding # simapp/module/genesisState 38 | } 39 | simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&gmGenesis) 40 | } 41 | 42 | // RegisterStoreDecoder registers a decoder. 43 | func (am AppModule) RegisterStoreDecoder(_ simtypes.StoreDecoderRegistry) {} 44 | 45 | // ProposalContents doesn't return any content functions for governance proposals. 46 | func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { 47 | return nil 48 | } 49 | 50 | // WeightedOperations returns the all the gov module operations with their respective weights. 51 | func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 52 | operations := make([]simtypes.WeightedOperation, 0) 53 | 54 | // this line is used by starport scaffolding # simapp/module/operation 55 | 56 | return operations 57 | } 58 | 59 | // ProposalMsgs returns msgs used for governance proposals for simulations. 60 | func (am AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg { 61 | return []simtypes.WeightedProposalMsg{ 62 | // this line is used by starport scaffolding # simapp/module/OpMsg 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/simulation/helpers.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 6 | ) 7 | 8 | // FindAccount find a specific address from an account list 9 | func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { 10 | creator, err := sdk.AccAddressFromBech32(address) 11 | if err != nil { 12 | panic(err) 13 | } 14 | return simtypes.FindAccount(accs, creator) 15 | } 16 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | "github.com/cosmos/cosmos-sdk/types/msgservice" 7 | // this line is used by starport scaffolding # 1 8 | ) 9 | 10 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 11 | // this line is used by starport scaffolding # 3 12 | 13 | registry.RegisterImplementations((*sdk.Msg)(nil), 14 | &MsgUpdateParams{}, 15 | ) 16 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 17 | } 18 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | sdkerrors "cosmossdk.io/errors" 7 | ) 8 | 9 | // x/gm module sentinel errors 10 | var ( 11 | ErrInvalidSigner = sdkerrors.Register(ModuleName, 1100, "expected gov account as only signer for proposal message") 12 | ErrSample = sdkerrors.Register(ModuleName, 1101, "sample error") 13 | ) 14 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "context" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // AccountKeeper defines the expected interface for the Account module. 10 | type AccountKeeper interface { 11 | GetAccount(context.Context, sdk.AccAddress) sdk.AccountI // only used for simulation 12 | // Methods imported from account should be defined here 13 | } 14 | 15 | // BankKeeper defines the expected interface for the Bank module. 16 | type BankKeeper interface { 17 | SpendableCoins(context.Context, sdk.AccAddress) sdk.Coins 18 | // Methods imported from bank should be defined here 19 | } 20 | 21 | // ParamSubspace defines the expected Subspace interface for parameters. 22 | type ParamSubspace interface { 23 | Get(context.Context, []byte, interface{}) 24 | Set(context.Context, []byte, interface{}) 25 | } 26 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // this line is used by starport scaffolding # genesis/types/import 4 | 5 | // DefaultIndex is the default global index 6 | const DefaultIndex uint64 = 1 7 | 8 | // DefaultGenesis returns the default genesis state 9 | func DefaultGenesis() *GenesisState { 10 | return &GenesisState{ 11 | // this line is used by starport scaffolding # genesis/types/default 12 | Params: DefaultParams(), 13 | } 14 | } 15 | 16 | // Validate performs basic genesis state validation returning an error upon any 17 | // failure. 18 | func (gs GenesisState) Validate() error { 19 | // this line is used by starport scaffolding # genesis/types/validate 20 | 21 | return gs.Params.Validate() 22 | } 23 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "gm/x/gm/types" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestGenesisState_Validate(t *testing.T) { 12 | tests := []struct { 13 | desc string 14 | genState *types.GenesisState 15 | valid bool 16 | }{ 17 | { 18 | desc: "default is valid", 19 | genState: types.DefaultGenesis(), 20 | valid: true, 21 | }, 22 | { 23 | desc: "valid genesis state", 24 | genState: &types.GenesisState{ 25 | 26 | // this line is used by starport scaffolding # types/genesis/validField 27 | }, 28 | valid: true, 29 | }, 30 | // this line is used by starport scaffolding # types/genesis/testcase 31 | } 32 | for _, tc := range tests { 33 | t.Run(tc.desc, func(t *testing.T) { 34 | err := tc.genState.Validate() 35 | if tc.valid { 36 | require.NoError(t, err) 37 | } else { 38 | require.Error(t, err) 39 | } 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "gm" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // MemStoreKey defines the in-memory store key 11 | MemStoreKey = "mem_gm" 12 | ) 13 | 14 | var ( 15 | ParamsKey = []byte("p_gm") 16 | ) 17 | 18 | func KeyPrefix(p string) []byte { 19 | return []byte(p) 20 | } 21 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/msg_update_params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | errorsmod "cosmossdk.io/errors" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | var _ sdk.Msg = &MsgUpdateParams{} 9 | 10 | // ValidateBasic does a sanity check on the provided data. 11 | func (m *MsgUpdateParams) ValidateBasic() error { 12 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { 13 | return errorsmod.Wrap(err, "invalid authority address") 14 | } 15 | 16 | if err := m.Params.Validate(); err != nil { 17 | return err 18 | } 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 5 | ) 6 | 7 | var _ paramtypes.ParamSet = (*Params)(nil) 8 | 9 | // ParamKeyTable the param key table for launch module 10 | func ParamKeyTable() paramtypes.KeyTable { 11 | return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) 12 | } 13 | 14 | // NewParams creates a new Params instance 15 | func NewParams() Params { 16 | return Params{} 17 | } 18 | 19 | // DefaultParams returns a default set of parameters 20 | func DefaultParams() Params { 21 | return NewParams() 22 | } 23 | 24 | // ParamSetPairs get the params.ParamSet 25 | func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { 26 | return paramtypes.ParamSetPairs{} 27 | } 28 | 29 | // Validate validates the set of params 30 | func (p Params) Validate() error { 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /demo/rollkit/x/gm/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | -------------------------------------------------------------------------------- /demo/sovereign/.dockerignore: -------------------------------------------------------------------------------- 1 | docker/ 2 | !docker/rollup_config.docker.toml 3 | -------------------------------------------------------------------------------- /demo/sovereign/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "const-rollup-config", 5 | "demo-rollup", 6 | "simple-nft-module", 7 | ] 8 | -------------------------------------------------------------------------------- /demo/sovereign/const-rollup-config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const-rollup-config" 3 | version = "0.3.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Sovereign Labs "] 7 | publish = false 8 | 9 | [dependencies] 10 | hex-literal = "0.3.4" 11 | -------------------------------------------------------------------------------- /demo/sovereign/const-rollup-config/README.md: -------------------------------------------------------------------------------- 1 | # Const Rollup Config 2 | 3 | In Sovereign, many state transition functions require consensus critical configuration. For example, rollups on Celestia 4 | need to configure a namespace which they check for data. This consensus critical configuration needs to be available 5 | to packages at compile time, so that it is baked into the binary which is fed to the zkVM. Otherwise, a malicious 6 | prover might be able to overwrite this configuration at runtime and create valid-looking proofs that were run 7 | over the wrong namespace. 8 | 9 | This package demonstrates how you can accomplish such configuration. You can see its usage in the [`main` function of demo-rollup](../demo-rollup/src/main.rs). 10 | -------------------------------------------------------------------------------- /demo/sovereign/const-rollup-config/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// The namespace used by the rollup to store its data. This is a raw slice of 8 bytes. 2 | /// The rollup stores its data in the namespace b"sov-test" on Celestia. Which in this case is encoded using the 3 | /// ascii representation of each character. 4 | pub const ROLLUP_NAMESPACE_RAW: [u8; 16] = 3_u128.to_be_bytes(); 5 | -------------------------------------------------------------------------------- /demo/sovereign/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "Sovereign SDK constants", 3 | "gas": { 4 | "Bank": { 5 | "create_token": [ 6 | 4, 7 | 4 8 | ], 9 | "transfer": [ 10 | 5, 11 | 5 12 | ], 13 | "burn": [ 14 | 2, 15 | 2 16 | ], 17 | "mint": [ 18 | 2, 19 | 2 20 | ], 21 | "freeze": [ 22 | 1, 23 | 1 24 | ] 25 | } 26 | }, 27 | "constants": { 28 | "DEFERRED_SLOTS_COUNT": 2 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/benches/node/Makefile: -------------------------------------------------------------------------------- 1 | # Default values for num blocks and transactions per block 2 | BLOCKS ?= 100 3 | TXNS_PER_BLOCK ?= 10000 4 | 5 | export BLOCKS 6 | export TXNS_PER_BLOCK 7 | 8 | criterion: 9 | @echo "Running criterion bench with $(TXNS_PER_BLOCK) transactions per block" 10 | @echo "Method: Criterion" 11 | @echo "Output: Criterion" 12 | @cd ../.. && cargo bench --features="bench" rollup_bench 13 | 14 | basic: 15 | @echo "Running basic benchmark with $(BLOCKS) blocks and $(TXNS_PER_BLOCK) transactions per block" 16 | @echo "Method: Coarse Timers" 17 | @echo "Output: Standard" 18 | @cd ../.. && cargo bench --features="bench" rollup_coarse_measure 19 | 20 | prometheus: 21 | @echo "Running basic benchmark with $(BLOCKS) blocks and $(TXNS_PER_BLOCK) transactions per block" 22 | @echo "Method: Coarse Timers" 23 | @echo "Output: Prometheus" 24 | @cd ../.. && PROMETHEUS=1 cargo bench --features="bench" rollup_coarse_measure 25 | 26 | flamegraph: 27 | @echo "Running basic benchmark with $(BLOCKS) blocks and $(TXNS_PER_BLOCK) transactions per block" 28 | @echo "Method: Coarse Timers" 29 | @echo "Output: Flamegraph" 30 | @echo "WARNING: Flamegraph requires sudo. The Makefile does cleanup, but there is a unforeseen risk of files being owned by root after the script is done. The Makefile also does full cleanup so subsequent builds with default user will be from scratch." 31 | @read -p "Proceed (y/n): " REPLY; if [ $$REPLY = "y" ]; then \ 32 | cd .. && sudo BLOCKS=$(BLOCKS) TXNS_PER_BLOCK=$(TXNS_PER_BLOCK) cargo flamegraph -o benches/flamegraph.svg --bench rollup_coarse_measure && sudo rm -rf benches/demo_data ; \ 33 | sudo rm -rf ../../../../target ; \ 34 | fi 35 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/benches/node/rollup_config.toml: -------------------------------------------------------------------------------- 1 | [da] 2 | # The JWT used to authenticate with the celestia light client. Instructions for generating this token can be found in the README 3 | celestia_rpc_auth_token = "MY.SECRET.TOKEN" 4 | # The address of the *trusted* Celestia light client to interact with 5 | celestia_rpc_address = "http://localhost:11111/" 6 | # The largest response the rollup will accept from the Celestia node. Defaults to 100 MB 7 | max_celestia_response_body_size = 104_857_600 8 | # The maximum time to wait for a response to an RPC query against Celestia node. Defaults to 60 seconds. 9 | celestia_rpc_timeout_seconds = 60 10 | 11 | [storage] 12 | # The path to the rollup's data directory. Paths that do not begin with `/` are interpreted as relative paths. 13 | path = "benches/demo_data" 14 | 15 | [runner] 16 | # We define the rollup's genesis to occur at block number `start_height`. The rollup will ignore 17 | # any blocks before this height 18 | start_height = 1 19 | 20 | [runner.rpc_config] 21 | # the host and port to bind the rpc server for 22 | bind_host = "127.0.0.1" 23 | bind_port = 12345 24 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/benches/prover/rollup_config.toml: -------------------------------------------------------------------------------- 1 | [da] 2 | # The JWT used to authenticate with the celestia light client. Instructions for generating this token can be found in the README 3 | celestia_rpc_auth_token = "MY.SECRET.TOKEN" 4 | # The address of the *trusted* Celestia light client to interact with 5 | celestia_rpc_address = "http://localhost:11111/" 6 | # The largest response the rollup will accept from the Celestia node. Defaults to 100 MB 7 | max_celestia_response_body_size = 104_857_600 8 | 9 | [storage] 10 | # The path to the rollup's data directory. Paths that do not begin with `/` are interpreted as relative paths. 11 | path = "benches/demo_data" 12 | 13 | [runner] 14 | # We define the rollup's genesis to occur at block number `start_height`. The rollup will ignore 15 | # any blocks before this height 16 | start_height = 1 17 | 18 | [runner.rpc_config] 19 | # the host and port to bind the rpc server for 20 | bind_host = "127.0.0.1" 21 | bind_port = 12345 22 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/build.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::process::ExitStatusExt; 2 | use std::process::{Command, ExitStatus}; 3 | fn main() { 4 | let is_risczero_installed = Command::new("cargo") 5 | .args(["risczero", "help"]) 6 | .status() 7 | .unwrap_or(ExitStatus::from_raw(1)); // If we can't execute the command, assume risczero isn't installed since duplicate install attempts are no-ops. 8 | 9 | if !is_risczero_installed.success() { 10 | // If installation fails, just exit silently. The user can try again. 11 | let _ = Command::new("cargo") 12 | .args(["install", "cargo-risczero"]) 13 | .status(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [patch.crates-io] 2 | cc = { git = "https://github.com/rust-lang/cc-rs", rev = "e5bbdfa" } 3 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0" 3 | version = "0.3.0" 4 | edition = "2021" 5 | resolver = "2" 6 | license = "MIT OR Apache-2.0" 7 | publish = false 8 | 9 | [build-dependencies] 10 | risc0-build = "0.18" 11 | 12 | [package.metadata.risc0] 13 | methods = ["guest-ikura", "guest-mock"] 14 | 15 | [features] 16 | bench = [] 17 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/build.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | fn main() { 4 | if std::env::var("SKIP_GUEST_BUILD").is_ok() { 5 | println!("Skipping guest build for CI run"); 6 | let out_dir = std::env::var_os("OUT_DIR").unwrap(); 7 | let out_dir = std::path::Path::new(&out_dir); 8 | let methods_path = out_dir.join("methods.rs"); 9 | 10 | let elf = r#" 11 | pub const ROLLUP_ELF: &[u8] = &[]; 12 | pub const MOCK_DA_ELF: &[u8] = &[]; 13 | "#; 14 | 15 | std::fs::write(methods_path, elf).expect("Failed to write mock rollup elf"); 16 | } else { 17 | let guest_pkg_to_options = get_guest_options(); 18 | risc0_build::embed_methods_with_options(guest_pkg_to_options); 19 | } 20 | } 21 | 22 | #[cfg(not(feature = "bench"))] 23 | fn get_guest_options() -> HashMap<&'static str, risc0_build::GuestOptions> { 24 | HashMap::new() 25 | } 26 | 27 | #[cfg(feature = "bench")] 28 | fn get_guest_options() -> HashMap<&'static str, risc0_build::GuestOptions> { 29 | let mut guest_pkg_to_options = HashMap::new(); 30 | guest_pkg_to_options.insert( 31 | "sov-demo-prover-guest-ikura", 32 | risc0_build::GuestOptions { 33 | features: vec!["bench".to_string()], 34 | }, 35 | ); 36 | guest_pkg_to_options 37 | } 38 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/guest-ikura/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sov-demo-prover-guest-ikura" 3 | version = "0.3.0" 4 | edition = "2021" 5 | resolver = "2" 6 | 7 | [workspace] 8 | 9 | [dependencies] 10 | anyhow = "1.0.68" 11 | risc0-zkvm = { version = "0.18", default-features = false, features = ["std"] } 12 | risc0-zkvm-platform = "0.18" 13 | demo-stf = { path = "../../../stf" } 14 | sov-risc0-adapter = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 15 | const-rollup-config = { path = "../../../../const-rollup-config" } 16 | ikura-da-adapter = { path = "../../../../../../adapters/sovereign", default-features = false, features = ["verifier"] } 17 | sov-modules-api = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 18 | sov-state = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 19 | sov-modules-stf-template = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 20 | [patch.crates-io] 21 | sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", rev = "sha2/v0.10.6-risc0" } 22 | 23 | [profile.dev] 24 | opt-level = 3 25 | 26 | [profile.dev.build-override] 27 | opt-level = 3 28 | 29 | [profile.release] 30 | debug = 1 31 | lto = true 32 | 33 | [profile.release.build-override] 34 | opt-level = 3 35 | 36 | #[features] 37 | #bench = [ 38 | # "sov-celestia-adapter/bench", 39 | #] 40 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/guest-ikura/src/bin/rollup.rs: -------------------------------------------------------------------------------- 1 | // TODO: Rename this file to change the name of this method from METHOD_NAME 2 | 3 | #![no_main] 4 | 5 | use const_rollup_config::ROLLUP_NAMESPACE_RAW; 6 | use demo_stf::runtime::Runtime; 7 | use demo_stf::AppVerifier; 8 | use ikura_da_adapter::verifier::IkuraVerifier; 9 | use sov_modules_api::default_context::ZkDefaultContext; 10 | use sov_modules_stf_template::kernels::basic::BasicKernel; 11 | use sov_modules_stf_template::AppTemplate; 12 | use sov_risc0_adapter::guest::Risc0Guest; 13 | use sov_state::ZkStorage; 14 | 15 | risc0_zkvm::guest::entry!(main); 16 | 17 | pub fn main() { 18 | let guest = Risc0Guest::new(); 19 | let storage = ZkStorage::new(); 20 | let app: AppTemplate, BasicKernel<_>> = 21 | AppTemplate::new(); 22 | 23 | let mut stf_verifier = AppVerifier::new(app, IkuraVerifier::from_raw(ROLLUP_NAMESPACE_RAW)); 24 | 25 | // This block execution implies a commit to the new state root, 26 | // the commit will be visible in the receipt (as public output) 27 | stf_verifier 28 | .run_block(guest, storage) 29 | .expect("Prover must be honest"); 30 | } 31 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/guest-mock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sov-demo-prover-guest-mock" 3 | version = "0.3.0" 4 | edition = "2021" 5 | resolver = "2" 6 | 7 | [workspace] 8 | 9 | [dependencies] 10 | anyhow = "1.0.68" 11 | risc0-zkvm = { version = "0.18", default-features = false, features = ["std"] } 12 | risc0-zkvm-platform = "0.18" 13 | sov-rollup-interface = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", features = ["mocks"], rev = "6058b31" } 14 | demo-stf = { path = "../../../stf" } 15 | sov-risc0-adapter = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 16 | sov-modules-api = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 17 | sov-state = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 18 | sov-modules-stf-template = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 19 | 20 | [patch.crates-io] 21 | sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", rev = "sha2/v0.10.6-risc0" } 22 | 23 | [profile.dev] 24 | opt-level = 3 25 | 26 | [profile.dev.build-override] 27 | opt-level = 3 28 | 29 | [profile.release] 30 | debug = 1 31 | lto = true 32 | 33 | [profile.release.build-override] 34 | opt-level = 3 35 | 36 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/guest-mock/src/bin/mock_da.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use demo_stf::runtime::Runtime; 3 | use demo_stf::AppVerifier; 4 | use sov_modules_api::default_context::ZkDefaultContext; 5 | use sov_modules_stf_template::{kernels::basic::BasicKernel, AppTemplate}; 6 | use sov_risc0_adapter::guest::Risc0Guest; 7 | use sov_rollup_interface::mocks::MockDaVerifier; 8 | use sov_state::ZkStorage; 9 | 10 | risc0_zkvm::guest::entry!(main); 11 | 12 | pub fn main() { 13 | let guest = Risc0Guest::new(); 14 | let storage = ZkStorage::new(); 15 | let app: AppTemplate, BasicKernel<_>> = 16 | AppTemplate::new(); 17 | 18 | let mut stf_verifier = AppVerifier::new(app, MockDaVerifier {}); 19 | 20 | stf_verifier 21 | .run_block(guest, storage) 22 | .expect("Prover must be honest"); 23 | } 24 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/provers/risc0/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 2 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/rollup_config.toml: -------------------------------------------------------------------------------- 1 | [da] 2 | 3 | [storage] 4 | # The path to the rollup's data directory. Paths that do not begin with `/` are interpreted as relative paths. 5 | path = "demo_data" 6 | 7 | # We define the rollup's genesis to occur at block number `start_height`. The rollup will ignore 8 | # any blocks before this height 9 | [runner] 10 | start_height = 1 11 | 12 | [runner.rpc_config] 13 | # the host and port to bind the rpc server for 14 | bind_host = "127.0.0.1" 15 | bind_port = 12345 16 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/src/eth.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use anyhow::Context as _; 4 | use sov_cli::wallet_state::PrivateKeyAndAddress; 5 | use sov_ethereum::experimental::EthRpcConfig; 6 | use sov_ethereum::GasPriceOracleConfig; 7 | use sov_modules_api::default_context::DefaultContext; 8 | use sov_modules_api::default_signature::private_key::DefaultPrivateKey; 9 | use sov_rollup_interface::services::da::DaService; 10 | use sov_state::ProverStorage; 11 | 12 | const TX_SIGNER_PRIV_KEY_PATH: &str = "../test-data/keys/tx_signer_private_key.json"; 13 | 14 | /// Ethereum RPC wraps EVM transaction in a rollup transaction. 15 | /// This function reads the private key of the rollup transaction signer. 16 | fn read_sov_tx_signer_priv_key() -> Result { 17 | let data = std::fs::read_to_string(TX_SIGNER_PRIV_KEY_PATH).context("Unable to read file")?; 18 | 19 | let key_and_address: PrivateKeyAndAddress = serde_json::from_str(&data) 20 | .unwrap_or_else(|_| panic!("Unable to convert data {} to PrivateKeyAndAddress", &data)); 21 | 22 | Ok(key_and_address.private_key) 23 | } 24 | 25 | // register ethereum methods. 26 | pub(crate) fn register_ethereum( 27 | da_service: Da, 28 | storage: ProverStorage, 29 | methods: &mut jsonrpsee::RpcModule<()>, 30 | ) -> Result<(), anyhow::Error> { 31 | let eth_rpc_config = { 32 | let eth_signer = eth_dev_signer(); 33 | EthRpcConfig:: { 34 | min_blob_size: Some(1), 35 | sov_tx_signer_priv_key: read_sov_tx_signer_priv_key()?, 36 | eth_signer, 37 | gas_price_oracle_config: GasPriceOracleConfig::default(), 38 | } 39 | }; 40 | 41 | let ethereum_rpc = 42 | sov_ethereum::get_ethereum_rpc::(da_service, eth_rpc_config, storage); 43 | methods 44 | .merge(ethereum_rpc) 45 | .context("Failed to merge Ethereum RPC modules") 46 | } 47 | 48 | // TODO: #840 49 | fn eth_dev_signer() -> sov_ethereum::DevSigner { 50 | sov_ethereum::DevSigner::new(vec![secp256k1::SecretKey::from_str( 51 | "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", 52 | ) 53 | .unwrap()]) 54 | } 55 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![doc = include_str!("../README.md")] 3 | 4 | mod mock_rollup; 5 | pub use mock_rollup::*; 6 | mod ikura_rollup; 7 | pub use ikura_rollup::*; 8 | #[cfg(feature = "experimental")] 9 | mod eth; 10 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/src/sov-cli/main.rs: -------------------------------------------------------------------------------- 1 | use demo_stf::runtime::RuntimeSubcommand; 2 | use sov_demo_rollup::IkuraDemoRollup; 3 | use sov_modules_api::cli::{FileNameArg, JsonStringArg}; 4 | use sov_modules_rollup_template::WalletTemplate; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<(), anyhow::Error> { 8 | IkuraDemoRollup::run_wallet::< 9 | RuntimeSubcommand, 10 | RuntimeSubcommand, 11 | >() 12 | .await 13 | } 14 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/stf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![doc = include_str!("../README.md")] 3 | 4 | #[cfg(feature = "native")] 5 | pub mod genesis_config; 6 | mod hooks_impl; 7 | pub mod runtime; 8 | #[cfg(test)] 9 | mod tests; 10 | 11 | use sov_modules_stf_template::AppTemplate; 12 | use sov_rollup_interface::da::DaVerifier; 13 | use sov_stf_runner::verifier::StateTransitionVerifier; 14 | 15 | /// Alias for StateTransitionVerifier. 16 | pub type AppVerifier = 17 | StateTransitionVerifier::Spec, Vm, RT, K>, DA, Vm>; 18 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/stf/src/tests/da_simulation.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use sov_data_generators::bank_data::{ 4 | BadNonceBankCallMessages, BadSerializationBankCallMessages, BadSignatureBankCallMessages, 5 | BankMessageGenerator, 6 | }; 7 | use sov_data_generators::value_setter_data::{ValueSetterMessage, ValueSetterMessages}; 8 | use sov_data_generators::MessageGenerator; 9 | use sov_modules_api::default_context::DefaultContext; 10 | use sov_modules_api::default_signature::private_key::DefaultPrivateKey; 11 | use sov_modules_stf_template::RawTx; 12 | use sov_rollup_interface::mocks::MockDaSpec; 13 | 14 | use crate::runtime::Runtime; 15 | 16 | type C = DefaultContext; 17 | type Da = MockDaSpec; 18 | 19 | pub fn simulate_da(value_setter_admin: DefaultPrivateKey) -> Vec { 20 | let mut messages = Vec::default(); 21 | 22 | let bank_generator = BankMessageGenerator::::default(); 23 | let bank_txs = bank_generator.create_raw_txs::>(); 24 | 25 | let value_setter = ValueSetterMessages::new(vec![ValueSetterMessage { 26 | admin: Rc::new(value_setter_admin), 27 | messages: vec![99, 33], 28 | }]); 29 | messages.extend(value_setter.create_raw_txs::>()); 30 | messages.extend(bank_txs); 31 | messages 32 | } 33 | 34 | pub fn simulate_da_with_revert_msg() -> Vec { 35 | let mut messages = Vec::default(); 36 | let bank_generator = BankMessageGenerator::::create_invalid_transfer(); 37 | let bank_txns = bank_generator.create_raw_txs::>(); 38 | messages.extend(bank_txns); 39 | messages 40 | } 41 | 42 | pub fn simulate_da_with_bad_sig() -> Vec { 43 | let b: BadSignatureBankCallMessages = Default::default(); 44 | b.create_raw_txs::>() 45 | } 46 | 47 | pub fn simulate_da_with_bad_nonce() -> Vec { 48 | let b: BadNonceBankCallMessages = Default::default(); 49 | b.create_raw_txs::>() 50 | } 51 | 52 | pub fn simulate_da_with_bad_serialization() -> Vec { 53 | let b: BadSerializationBankCallMessages = Default::default(); 54 | b.create_raw_txs::>() 55 | } 56 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/stf/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use sov_modules_api::default_context::DefaultContext; 4 | use sov_modules_api::DaSpec; 5 | use sov_modules_stf_template::kernels::basic::BasicKernel; 6 | use sov_modules_stf_template::AppTemplate; 7 | use sov_rollup_interface::mocks::MockDaSpec; 8 | use sov_state::storage_manager::ProverStorageManager; 9 | use sov_state::DefaultStorageSpec; 10 | 11 | use crate::genesis_config::{get_genesis_config, GenesisPaths}; 12 | use crate::runtime::{GenesisConfig, Runtime}; 13 | 14 | mod da_simulation; 15 | mod stf_tests; 16 | mod tx_revert_tests; 17 | pub(crate) type C = DefaultContext; 18 | pub(crate) type Da = MockDaSpec; 19 | 20 | pub(crate) type RuntimeTest = Runtime; 21 | pub(crate) type AppTemplateTest = AppTemplate< 22 | DefaultContext, 23 | Da, 24 | sov_rollup_interface::mocks::MockZkvm, 25 | RuntimeTest, 26 | BasicKernel, 27 | >; 28 | 29 | pub(crate) fn create_storage_manager_for_tests( 30 | path: impl AsRef, 31 | ) -> ProverStorageManager { 32 | let config = sov_state::config::Config { 33 | path: path.as_ref().to_path_buf(), 34 | }; 35 | ProverStorageManager::new(config).unwrap() 36 | } 37 | 38 | pub(crate) fn get_genesis_config_for_tests() -> GenesisConfig { 39 | get_genesis_config::(&GenesisPaths::from_dir( 40 | "../../test-data/genesis/integration-tests", 41 | )) 42 | .unwrap() 43 | } 44 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/submitting_1.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | RPC_ENDPOINT="http://127.0.0.1:12345" 4 | PRIVATE_KEY="../test-data/keys/token_deployer_private_key.json" 5 | SOV_CLI="../../target/debug/sov-cli" 6 | 7 | echo "Preparing..." 8 | $SOV_CLI submit-transaction "$PRIVATE_KEY" Bank ../test-data/requests/create_token.json 0 "$RPC_ENDPOINT" 9 | $SOV_CLI submit-transaction "$PRIVATE_KEY" SequencerRegistry ../test-data/requests/register_sequencer.json 1 "$RPC_ENDPOINT" 10 | $SOV_CLI publish-batch "$RPC_ENDPOINT" 11 | 12 | 13 | sleep 1 14 | echo "Starting submitting transfers" 15 | for nonce in {2..30}; do 16 | echo "Submitting transaction with nonce $nonce" 17 | $SOV_CLI submit-transaction "$PRIVATE_KEY" Bank ../test-data/requests/transfer.json "$nonce" "$RPC_ENDPOINT" 18 | if [ $((nonce % 3)) -eq 0 ]; then 19 | $SOV_CLI publish-batch "$RPC_ENDPOINT" 20 | fi 21 | done -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/submitting_2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | RPC_ENDPOINT="http://127.0.0.1:12346" 4 | PRIVATE_KEY="../test-data/keys/minter_private_key.json" 5 | SOV_CLI="../../target/debug/sov-cli" 6 | 7 | echo "Starting !!!" 8 | 9 | for nonce in {0..30}; do 10 | echo "Submitting transaction with nonce $nonce" 11 | $SOV_CLI submit-transaction "$PRIVATE_KEY" Bank ../test-data/requests/transfer.json "$nonce" "$RPC_ENDPOINT" 12 | if [ $((nonce % 3)) -eq 0 ]; then 13 | $SOV_CLI publish-batch "$RPC_ENDPOINT" 14 | fi 15 | done -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/test_create_token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo build --bin sov-cli 4 | SOV_CLI="../target/debug/sov-cli" 5 | 6 | # setup rpc endpoint 7 | $SOV_CLI rpc set-url http://127.0.0.1:12345 8 | 9 | # import keys 10 | $SOV_CLI keys import --nickname token_deployer --path ../test-data/keys/token_deployer_private_key.json 11 | 12 | # Create and mint a new token 13 | $SOV_CLI transactions import from-file bank --path ../test-data/requests/create_token.json 14 | $SOV_CLI transactions import from-file bank --path ../test-data/requests/mint.json 15 | 16 | # submit batch with two transactions 17 | $SOV_CLI rpc submit-batch by-nickname token_deployer 18 | 19 | # way to let the rollup fetch from the DA the transaction and process it and then 20 | # query the rollup node to check the correct amout of tokens were minted 21 | sleep 30 22 | echo "4000 tokens should be minted" 23 | curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72"],"id":1}' http://127.0.0.1:12345 24 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/all_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "experimental"))] 2 | mod bank; 3 | #[cfg(feature = "experimental")] 4 | mod evm; 5 | mod test_helpers; 6 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | 8 | # Hardhat files 9 | cache 10 | artifacts 11 | 12 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/Readme.md: -------------------------------------------------------------------------------- 1 | ### Uniswap demo folow these steps: 2 | 3 | 1. Deploys `uniswap-v2` contracts. 4 | 2. Adds liquidity to the USDT <> USDC pair. 5 | 3. Executes a swap. 6 | 7 | ### How to execute the demo: 8 | 1. Install `anvil` see: https://github.com/foundry-rs/foundry 9 | 2. Run `npm install` inside uniswap directory. 10 | 3. Start `anvil` in another terminal. 11 | 4. Deploy `uniswap-v2` contracts and add liquidity with: 12 | `npx hardhat run --network localhost scripts/01_deploy.js` 13 | 3. Execute a swap: 14 | `npx hardhat run --network localhost scripts/02_swap.js` 15 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/contracts/Tether.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | contract Tether is ERC20, Ownable { 7 | constructor() ERC20('Tether', 'USDT') {} 8 | 9 | function mint(address to, uint256 amount) public onlyOwner { 10 | _mint(to, amount); 11 | } 12 | } -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/contracts/UsdCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | contract UsdCoin is ERC20, Ownable { 7 | constructor() ERC20('UsdCoin', 'USDC') {} 8 | 9 | function mint(address to, uint256 amount) public onlyOwner { 10 | _mint(to, amount); 11 | } 12 | } -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-ethers"); 2 | 3 | /** @type import('hardhat/config').HardhatUserConfig */ 4 | module.exports = { 5 | solidity: { 6 | version: "0.8.17", 7 | settings: { 8 | optimizer: { 9 | enabled: true, 10 | runs: 5000, 11 | details: { yul: false }, 12 | }, 13 | } 14 | }, 15 | networks: { 16 | sov: { 17 | url: "http://127.0.0.1:12345" 18 | }, 19 | }, 20 | }; -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "both", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@openzeppelin/contracts": "^4.7.3", 14 | "@uniswap/v2-core": "^1.0.1", 15 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 16 | "hardhat": "^2.11.2", 17 | "dotenv": "^16.0.3" 18 | }, 19 | "devDependencies": { 20 | "@nomiclabs/hardhat-ethers": "^2.2.3", 21 | "ethers": "^5.7.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/evm/uniswap/scripts/02_swap.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const fs = require('fs'); 3 | const { promisify } = require('util'); 4 | 5 | const { providers, Contract, utils, constants } = require('ethers'); 6 | const routerArtifact = require('@uniswap/v2-periphery/build/UniswapV2Router02.json') 7 | const usdtArtifact = require("../artifacts/contracts/Tether.sol/Tether.json") 8 | const usdcArtifact = require("../artifacts/contracts/UsdCoin.sol/UsdCoin.json") 9 | 10 | USDT_ADDRESS = process.env.USDT_ADDRESS 11 | USDC_ADDRESS = process.env.USDC_ADDRESS 12 | WETH_ADDRESS = process.env.WETH_ADDRESS 13 | FACTORY_ADDRESS = process.env.FACTORY_ADDRESS 14 | PAIR_ADDRESS = process.env.PAIR_ADDRESS 15 | ROUTER_ADDRESS = process.env.ROUTER_ADDRESS 16 | 17 | 18 | 19 | const provider = new providers.JsonRpcProvider('http://127.0.0.1:8545/') 20 | 21 | const router = new Contract( 22 | ROUTER_ADDRESS, 23 | routerArtifact.abi, 24 | provider 25 | ) 26 | 27 | const usdt = new Contract( 28 | USDT_ADDRESS, 29 | usdtArtifact.abi, 30 | provider 31 | ) 32 | 33 | const usdc = new Contract( 34 | USDC_ADDRESS, 35 | usdcArtifact.abi, 36 | provider 37 | ) 38 | 39 | const logBalance = async (signerObj) => { 40 | let ethBalance 41 | let usdtBalance 42 | let usdcBalance 43 | let balances 44 | ethBalance = await signerObj.getBalance() 45 | usdtBalance = await usdt.balanceOf(signerObj.address) 46 | usdcBalance = await usdc.balanceOf(signerObj.address) 47 | balances = { 48 | ethBalance: ethBalance, 49 | usdtBalance: usdtBalance, 50 | usdcBalance: usdcBalance, 51 | } 52 | console.log('balances', balances) 53 | 54 | } 55 | 56 | const main = async () => { 57 | const [owner, trader] = await ethers.getSigners() 58 | 59 | await logBalance(trader) 60 | 61 | const tx = await router.connect(trader).swapExactTokensForTokens( 62 | utils.parseUnits('2', 18), 63 | utils.parseUnits('1', 18), 64 | [USDT_ADDRESS, USDC_ADDRESS], 65 | trader.address, 66 | Math.floor(Date.now() / 1000) + (60 * 10), 67 | { 68 | gasLimit: 1000000, 69 | } 70 | ) 71 | 72 | await tx.wait() 73 | await logBalance(trader) 74 | } 75 | 76 | 77 | main() 78 | .then(() => process.exit(0)) 79 | .catch((error) => { 80 | console.error(error); 81 | process.exit(1); 82 | }); -------------------------------------------------------------------------------- /demo/sovereign/demo-rollup/tests/test_helpers.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use demo_stf::genesis_config::GenesisPaths; 4 | use sov_demo_rollup::MockDemoRollup; 5 | use sov_modules_rollup_template::{RollupProverConfig, RollupTemplate}; 6 | use sov_rollup_interface::mocks::{MockAddress, MockDaConfig}; 7 | use sov_stf_runner::{RollupConfig, RpcConfig, RunnerConfig, StorageConfig}; 8 | use tokio::sync::oneshot; 9 | 10 | pub async fn start_rollup( 11 | rpc_reporting_channel: oneshot::Sender, 12 | genesis_paths: GenesisPaths, 13 | rollup_prover_config: Option, 14 | ) { 15 | let temp_dir = tempfile::tempdir().unwrap(); 16 | let temp_path = temp_dir.path(); 17 | 18 | let rollup_config = RollupConfig { 19 | storage: StorageConfig { 20 | path: temp_path.to_path_buf(), 21 | }, 22 | runner: RunnerConfig { 23 | start_height: 0, 24 | rpc_config: RpcConfig { 25 | bind_host: "127.0.0.1".into(), 26 | bind_port: 0, 27 | }, 28 | }, 29 | da: MockDaConfig { 30 | sender_address: MockAddress { addr: [0; 32] }, 31 | }, 32 | }; 33 | 34 | let mock_demo_rollup = MockDemoRollup {}; 35 | 36 | let rollup = mock_demo_rollup 37 | .create_new_rollup(&genesis_paths, rollup_config, rollup_prover_config) 38 | .await 39 | .unwrap(); 40 | 41 | rollup 42 | .run_and_report_rpc_port(Some(rpc_reporting_channel)) 43 | .await 44 | .unwrap(); 45 | 46 | // Close the tempdir explicitly to ensure that rustc doesn't see that it's unused and drop it unexpectedly 47 | temp_dir.close().unwrap(); 48 | } 49 | -------------------------------------------------------------------------------- /demo/sovereign/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/thrumdev/risczero:latest as risczero 2 | 3 | FROM ubuntu:20.04 as builder 4 | 5 | LABEL org.opencontainers.image.source=https://github.com/thrumdev/blobs 6 | 7 | ARG RUSTC_VERSION=nightly-2023-10-16 8 | 9 | ENV CARGO_INCREMENTAL=0 10 | ENV CARGO_HOME=/cargo 11 | ENV CARGO_TARGET_DIR=/cargo_target 12 | ENV RUSTFLAGS="-Cdebuginfo=0" 13 | ENV RUSTUP_HOME=/rustup 14 | 15 | RUN mkdir -p /cargo && \ 16 | mkdir -p /cargo_target && \ 17 | mkdir -p /rustup 18 | 19 | RUN \ 20 | apt-get update && \ 21 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 22 | ca-certificates \ 23 | protobuf-compiler \ 24 | curl \ 25 | git \ 26 | llvm \ 27 | clang \ 28 | cmake \ 29 | make \ 30 | libssl-dev \ 31 | pkg-config 32 | 33 | RUN \ 34 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain $RUSTC_VERSION 35 | 36 | COPY --from=risczero $CARGO_HOME/bin/cargo-risczero $CARGO_HOME/bin/cargo-risczero 37 | COPY --from=risczero $CARGO_HOME/bin/r0vm $CARGO_HOME/bin/r0vm 38 | COPY --from=risczero /rustup/toolchains/risc0 $RUSTUP_HOME/toolchains/risc0 39 | 40 | WORKDIR /ikura 41 | COPY . /ikura 42 | 43 | ENV CONSTANTS_MANIFEST=/ikura/demo/sovereign/constants.json 44 | RUN \ 45 | --mount=type=cache,id=demo-sovereign,target=/cargo/git \ 46 | --mount=type=cache,id=demo-sovereign,target=/cargo/registry \ 47 | --mount=type=cache,id=demo-sovereign,target=/cargo_target \ 48 | cd demo/sovereign \ 49 | && $CARGO_HOME/bin/cargo build --release --locked \ 50 | && cp $CARGO_TARGET_DIR/release/sov-demo-rollup /usr/bin/sov-demo-rollup \ 51 | && cp $CARGO_TARGET_DIR/release/sov-cli /usr/bin/sov-cli 52 | 53 | FROM ubuntu:20.04 as prod 54 | 55 | RUN \ 56 | apt-get update && \ 57 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 58 | libssl-dev \ 59 | pkg-config 60 | 61 | ENV TINI_VERSION v0.19.0 62 | ARG TARGETARCH 63 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} /tini 64 | RUN chmod +x /tini 65 | 66 | COPY --from=builder /usr/bin/sov-demo-rollup /usr/bin/sov-demo-rollup 67 | COPY --from=builder /usr/bin/sov-cli /usr/bin/sov-cli 68 | 69 | COPY ./demo/sovereign /ikura/demo/sovereign 70 | COPY ./demo/sovereign/docker/rollup_config.docker.toml /ikura/demo/sovereign/demo-rollup/rollup_config.toml 71 | WORKDIR /ikura/demo/sovereign/demo-rollup 72 | 73 | EXPOSE 12345 74 | 75 | ENTRYPOINT ["/tini", "--", "/usr/bin/sov-demo-rollup"] 76 | -------------------------------------------------------------------------------- /demo/sovereign/docker/rollup_config.docker.toml: -------------------------------------------------------------------------------- 1 | [da] 2 | ikura_rpc = "ws://localhost:10995/" 3 | rpc_timeout_seconds = 180 4 | 5 | [storage] 6 | # The path to the rollup's data directory. Paths that do not begin with `/` are interpreted as relative paths. 7 | path = "/demo_data" 8 | 9 | # We define the rollup's genesis to occur at block number `start_height`. The rollup will ignore 10 | # any blocks before this height 11 | [runner] 12 | start_height = 1 13 | 14 | [runner.rpc_config] 15 | # the host and port to bind the rpc server for 16 | bind_host = "0.0.0.0" 17 | bind_port = 12345 18 | -------------------------------------------------------------------------------- /demo/sovereign/simple-nft-module/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple-nft-module" 3 | version = "0.3.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Sovereign Labs "] 7 | publish = false 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | anyhow = { version = "1.0.68", default-features = false } 13 | borsh = { version = "0.10.3", default-features = false, features = ["rc"]} 14 | serde = { version = "1.0.190", default-features = false, features = ["alloc", "derive"] } 15 | 16 | sov-modules-api = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 17 | sov-state = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 18 | 19 | clap = { version = "4.4.7", optional = true, features = ["derive"] } 20 | schemars = { version = "0.8.12", features = ["derive"], optional = true } 21 | serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } 22 | jsonrpsee = { version = "0.20.1", features = ["macros", "client-core", "server", "jsonrpsee-types"], optional = true } 23 | 24 | [dev-dependencies] 25 | sov-rollup-interface = { git = "https://github.com/Sovereign-Labs/sovereign-sdk", rev = "6058b31" } 26 | tempfile = "3.8" 27 | simple-nft-module = { version = "*", features = ["native"], path = "." } 28 | 29 | 30 | [features] 31 | default = [] 32 | native = ["sov-state/native", "sov-modules-api/native", "jsonrpsee", "schemars", "serde_json", "clap"] 33 | test = ["native"] 34 | -------------------------------------------------------------------------------- /demo/sovereign/simple-nft-module/src/genesis.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | use serde::{Deserialize, Serialize}; 3 | use sov_modules_api::{Context, WorkingSet}; 4 | 5 | use crate::NonFungibleToken; 6 | 7 | /// Config for the NonFungibleToken module. 8 | /// Sets admin and existing owners. 9 | #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] 10 | pub struct NonFungibleTokenConfig { 11 | /// Admin of the NonFungibleToken module. 12 | pub admin: C::Address, 13 | /// Existing owners of the NonFungibleToken module. 14 | pub owners: Vec<(u64, C::Address)>, 15 | } 16 | 17 | impl NonFungibleToken { 18 | pub(crate) fn init_module( 19 | &self, 20 | config: &::Config, 21 | working_set: &mut WorkingSet, 22 | ) -> Result<()> { 23 | self.admin.set(&config.admin, working_set); 24 | for (id, owner) in config.owners.iter() { 25 | if self.owners.get(id, working_set).is_some() { 26 | bail!("Token id {} already exists", id); 27 | } 28 | self.owners.set(id, owner, working_set); 29 | } 30 | Ok(()) 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod test { 36 | use sov_modules_api::default_context::DefaultContext; 37 | use sov_modules_api::utils::generate_address; 38 | use sov_modules_api::Spec; 39 | 40 | use super::NonFungibleTokenConfig; 41 | 42 | #[test] 43 | fn test_config_serialization() { 44 | let address: ::Address = 45 | generate_address::("admin"); 46 | let owner: ::Address = generate_address::("owner"); 47 | 48 | let config = NonFungibleTokenConfig:: { 49 | admin: address, 50 | owners: vec![(0, owner)], 51 | }; 52 | 53 | let data = r#" 54 | { 55 | "admin":"sov1335hded4gyzpt00fpz75mms4m7ck02wgw07yhw9grahj4dzg4yvqk63pml", 56 | "owners":[ 57 | [0,"sov1fsgzj6t7udv8zhf6zj32mkqhcjcpv52yph5qsdcl0qt94jgdckqsczjm2y"] 58 | ] 59 | }"#; 60 | 61 | let parsed_config: NonFungibleTokenConfig = 62 | serde_json::from_str(data).unwrap(); 63 | assert_eq!(config, parsed_config) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo/sovereign/simple-nft-module/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![doc = include_str!("../README.md")] 3 | 4 | mod call; 5 | pub use call::CallMessage; 6 | mod genesis; 7 | pub use genesis::*; 8 | #[cfg(feature = "native")] 9 | mod query; 10 | #[cfg(feature = "native")] 11 | pub use query::*; 12 | use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo, WorkingSet}; 13 | 14 | #[cfg_attr(feature = "native", derive(sov_modules_api::ModuleCallJsonSchema))] 15 | #[derive(ModuleInfo, Clone)] 16 | /// Module for non-fungible tokens (NFT). 17 | /// Each token is represented by a unique ID. 18 | pub struct NonFungibleToken { 19 | #[address] 20 | /// The address of the NonFungibleToken module. 21 | address: C::Address, 22 | 23 | #[state] 24 | /// Admin of the NonFungibleToken module. 25 | admin: sov_modules_api::StateValue, 26 | 27 | #[state] 28 | /// Mapping of tokens to their owners 29 | owners: sov_modules_api::StateMap, 30 | } 31 | 32 | impl Module for NonFungibleToken { 33 | type Context = C; 34 | 35 | type Config = NonFungibleTokenConfig; 36 | 37 | type CallMessage = CallMessage; 38 | 39 | type Event = (); 40 | 41 | fn genesis(&self, config: &Self::Config, working_set: &mut WorkingSet) -> Result<(), Error> { 42 | Ok(self.init_module(config, working_set)?) 43 | } 44 | 45 | fn call( 46 | &self, 47 | msg: Self::CallMessage, 48 | context: &Self::Context, 49 | working_set: &mut WorkingSet, 50 | ) -> Result { 51 | let call_result = match msg { 52 | CallMessage::Mint { id } => self.mint(id, context, working_set), 53 | CallMessage::Transfer { to, id } => self.transfer(id, to, context, working_set), 54 | CallMessage::Burn { id } => self.burn(id, context, working_set), 55 | }; 56 | Ok(call_result?) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demo/sovereign/simple-nft-module/src/query.rs: -------------------------------------------------------------------------------- 1 | use jsonrpsee::core::RpcResult; 2 | use sov_modules_api::macros::rpc_gen; 3 | use sov_modules_api::{Context, WorkingSet}; 4 | 5 | use crate::NonFungibleToken; 6 | 7 | #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] 8 | /// Response for `getOwner` method 9 | pub struct OwnerResponse { 10 | /// Optional owner address 11 | pub owner: Option, 12 | } 13 | 14 | #[rpc_gen(client, server, namespace = "nft")] 15 | impl NonFungibleToken { 16 | #[rpc_method(name = "getOwner")] 17 | /// Get the owner of a token 18 | pub fn get_owner( 19 | &self, 20 | token_id: u64, 21 | working_set: &mut WorkingSet, 22 | ) -> RpcResult> { 23 | Ok(OwnerResponse { 24 | owner: self.owners.get(&token_id, working_set), 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/sovereign/simple-nft-module/src/tests.rs: -------------------------------------------------------------------------------- 1 | use sov_modules_api::default_context::DefaultContext; 2 | use sov_modules_api::utils::generate_address as gen_addr_generic; 3 | use sov_modules_api::Spec; 4 | 5 | use crate::NonFungibleTokenConfig; 6 | 7 | #[test] 8 | fn test_config_serialization() { 9 | let address: ::Address = gen_addr_generic::("admin"); 10 | let owner: ::Address = gen_addr_generic::("owner"); 11 | 12 | let config = NonFungibleTokenConfig:: { 13 | admin: address, 14 | owners: vec![(0, owner)], 15 | }; 16 | 17 | let data = r#" 18 | { 19 | "admin":"sov1335hded4gyzpt00fpz75mms4m7ck02wgw07yhw9grahj4dzg4yvqk63pml", 20 | "owners":[ 21 | [0,"sov1fsgzj6t7udv8zhf6zj32mkqhcjcpv52yph5qsdcl0qt94jgdckqsczjm2y"] 22 | ] 23 | }"#; 24 | 25 | let parsed_config: NonFungibleTokenConfig = serde_json::from_str(data).unwrap(); 26 | assert_eq!(config, parsed_config) 27 | } 28 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/accounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "pub_keys": [] 3 | } 4 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/bank.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokens": [ 3 | { 4 | "token_name": "sov-demo-token", 5 | "address_and_balances": [ 6 | [ 7 | "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", 8 | 100000000 9 | ] 10 | ], 11 | "authorized_minters": [ 12 | "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" 13 | ], 14 | "salt": 0 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/chain_state.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial_slot_height": 0, 3 | "current_time": { 4 | "secs": 0, 5 | "nanos": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/evm.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 5 | "balance": "0xffffffffffffffff", 6 | "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 7 | "code": "0x", 8 | "nonce": 0 9 | } 10 | ], 11 | "chain_id": 1, 12 | "limit_contract_code_size": null, 13 | "spec": { 14 | "0": "SHANGHAI" 15 | }, 16 | "coinbase": "0x0000000000000000000000000000000000000000", 17 | "starting_base_fee": 7, 18 | "block_gas_limit": 30000000, 19 | "genesis_timestamp": 0, 20 | "block_timestamp_delta": 1, 21 | "base_fee_params": { 22 | "max_change_denominator": 8, 23 | "elasticity_multiplier": 2 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/nft.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/sequencer_registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "seq_rollup_address": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", 3 | "seq_da_address": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125], 4 | "coins_to_lock": { 5 | "amount": 50, 6 | "token_address": "sov1zsnx7n2wjvtkr0ttscfgt06pjca3v2e6stxeu49qwynavmk7a8xqlxkkjp" 7 | }, 8 | "is_preferred_sequencer": true 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/demo-tests/value_setter.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" 3 | } 4 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/accounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "pub_keys": [] 3 | } 4 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/bank.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokens": [ 3 | { 4 | "token_name": "sov-demo-token", 5 | "address_and_balances": [ 6 | [ 7 | "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", 8 | 100000000 9 | ] 10 | ], 11 | "authorized_minters": [ 12 | "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" 13 | ], 14 | "salt": 0 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/chain_state.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial_slot_height": 0, 3 | "current_time": { 4 | "secs": 0, 5 | "nanos": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/evm.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", 5 | "balance": "0xffffffffffffffff", 6 | "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 7 | "code": "0x", 8 | "nonce": 0 9 | } 10 | ], 11 | "chain_id": 1, 12 | "limit_contract_code_size": null, 13 | "spec": { 14 | "0": "SHANGHAI" 15 | }, 16 | "coinbase": "0x0000000000000000000000000000000000000000", 17 | "starting_base_fee": 7, 18 | "block_gas_limit": 30000000, 19 | "genesis_timestamp": 0, 20 | "block_timestamp_delta": 1, 21 | "base_fee_params": { 22 | "max_change_denominator": 8, 23 | "elasticity_multiplier": 2 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/nft.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/sequencer_registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "seq_rollup_address": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", 3 | "seq_da_address": "0000000000000000000000000000000000000000000000000000000000000000", 4 | "coins_to_lock": { 5 | "amount": 50, 6 | "token_address": "sov1zsnx7n2wjvtkr0ttscfgt06pjca3v2e6stxeu49qwynavmk7a8xqlxkkjp" 7 | }, 8 | "is_preferred_sequencer": true 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/genesis/integration-tests/value_setter.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" 3 | } 4 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/keys/minter_private_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "private_key": { 3 | "key_pair": [ 4 | 35, 110, 128, 203, 34, 44, 78, 208, 67, 27, 9, 59, 58, 197, 62, 106, 167, 5 | 162, 39, 63, 225, 244, 53, 28, 211, 84, 152, 154, 130, 52, 50, 162 6 | ] 7 | }, 8 | "address": "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc" 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/keys/token_deployer_private_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "private_key": { 3 | "key_pair": [ 4 | 117, 251, 248, 217, 135, 70, 194, 105, 46, 80, 41, 66, 185, 56, 200, 35, 5 | 121, 253, 9, 234, 159, 91, 96, 212, 211, 158, 135, 225, 180, 36, 104, 253 6 | ] 7 | }, 8 | "address": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/keys/tx_signer_private_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "private_key": { 3 | "key_pair": [ 4 | 39, 195, 119, 77, 82, 231, 30, 162, 102, 169, 197, 37, 108, 217, 139, 154, 5 | 230, 126, 98, 242, 174, 94, 211, 74, 102, 141, 184, 234, 168, 62, 27, 172 6 | ] 7 | }, 8 | "address": "sov1dnhqk4mdsj2kwv4xymt8a624xuahfx8906j9usdkx7ensfghndkq8p33f7" 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/burn.json: -------------------------------------------------------------------------------- 1 | { 2 | "Burn": { 3 | "coins": { 4 | "amount": 300, 5 | "token_address": "sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/create_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "CreateToken": { 3 | "salt": 11, 4 | "token_name": "sov-test-token", 5 | "initial_balance": 1000, 6 | "minter_address": "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc", 7 | "authorized_minters": [ 8 | "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", 9 | "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/mint.json: -------------------------------------------------------------------------------- 1 | { 2 | "Mint": { 3 | "coins": { 4 | "amount": 3000, 5 | "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" 6 | }, 7 | "minter_address": "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/nft/create_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "CreateCollection": { 3 | "name": "Test Collection", 4 | "collection_uri": "https://foo.bar/test_collection" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/nft/freeze_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "FreezeCollection": { 3 | "collection_name": "Test Collection" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/nft/mint_nft.json: -------------------------------------------------------------------------------- 1 | { 2 | "MintNft": { 3 | "collection_name": "Test Collection", 4 | "token_uri": "https://foo.bar/test_collection/nft/42", 5 | "token_id": 42, 6 | "owner": "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc", 7 | "frozen": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/nft/transfer_nft.json: -------------------------------------------------------------------------------- 1 | { 2 | "TransferNft": { 3 | "collection_address": "sov1j2e3dh76nmuw4gctrqduh0wzqdny8c62z36r2q3883rknw3ky3vsk9g02a", 4 | "token_id": 42, 5 | "to": "sov14fs0gdya9tgwkpglx3gsg3h37r9f5e2pz6qyk87n3ywqch5fqe5s0xyxea" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/nft/update_token_uri.json: -------------------------------------------------------------------------------- 1 | { 2 | "UpdateNft": { 3 | "collection_address": "sov1j2e3dh76nmuw4gctrqduh0wzqdny8c62z36r2q3883rknw3ky3vsk9g02a", 4 | "token_id": 42, 5 | "token_uri": "https://foo.bar/test_collection/nft_new/42", 6 | "frozen": null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/register_sequencer.json: -------------------------------------------------------------------------------- 1 | { 2 | "Register": { 3 | "da_address": [ 4 | 13, 5, 25, 31, 28, 30, 5, 27, 20, 6, 29, 10, 14, 29, 20, 12, 22, 13, 19, 5 | 1, 0, 11, 9, 15, 23, 13, 14, 1, 9, 27, 9, 14 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/sovereign/test-data/requests/transfer.json: -------------------------------------------------------------------------------- 1 | { 2 | "Transfer": { 3 | "to": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqklh0qh", 4 | "coins": { 5 | "amount": 200, 6 | "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # A couple of notes on this file. 2 | # 3 | # docker-compose takes over the control of the context meaning you don't pass it as an argument to 4 | # build. Instead, the context is specified relative to the location of this file. In turn, the 5 | # location of the dockerfile is relative to the context. 6 | 7 | name: ikura 8 | 9 | services: 10 | zombienet: 11 | build: 12 | context: .. 13 | dockerfile: ./ikura/chain/Dockerfile 14 | target: zombienet 15 | ports: 16 | - "9988:9988" 17 | # Mount /zombienet as tmpfs so as to avoid zombienet prompting if it should ignore existing 18 | # directory. 19 | tmpfs: /zombienet 20 | shim: 21 | build: 22 | context: .. 23 | dockerfile: ./ikura/shim/Dockerfile 24 | ports: 25 | - "10995:10995" 26 | # depends_on: 27 | # zombienet: 28 | # condition: service_healthy 29 | environment: 30 | - RUST_LOG=ikura=trace 31 | command: ["serve", "-p", "10995", "--node-url=ws://zombienet:9988", "--submit-dev-alice"] 32 | # Health check. 33 | # 34 | # Note that if JSON-RPC returns an error, the health check will succeed. It's fine for now. 35 | healthcheck: 36 | test: [ 37 | "CMD-SHELL", 38 | "curl -s -XPOST -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"sovereign_getBlock\", \"params\":[1, \"0x00000000000000000000000000000000\"]}' http://localhost:10995/"] 39 | sov: 40 | build: 41 | context: .. 42 | dockerfile: ./demo/sovereign/docker/Dockerfile 43 | depends_on: 44 | shim: 45 | condition: service_healthy 46 | # Don't persist the rollup data directory. 47 | tmpfs: /demo_data 48 | # This unites the Linux network namespace with the one of the `shim` service. That means that 49 | # shim will be available via localhost. 50 | network_mode: "service:shim" 51 | -------------------------------------------------------------------------------- /docker/gha-runner.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # An image that acts as the base image for the GHA runner running in sysbox. 4 | # 5 | # Build: docker build -t ghcr.io/thrumdev/gha-runner -f docker/gha-runner.Dockerfile . 6 | # Push: docker push ghcr.io/thrumdev/gha-runner 7 | 8 | ARG POLKADOT_VERSION=v1.6.0 9 | FROM parity/polkadot:${POLKADOT_VERSION} AS polkadot 10 | 11 | FROM rodnymolina588/gha-sysbox-runner@sha256:d10a36f2da30aa0df71d1ac062cc79fc5114eec7b6ae8a0c42cadf568e6eefa8 12 | 13 | ARG RUSTC_VERSION=stable 14 | 15 | LABEL org.opencontainers.image.source=https://github.com/thrumdev/blobs 16 | 17 | ENV CARGO_INCREMENTAL=0 18 | ENV CARGO_HOME=/cargo 19 | ENV CARGO_TARGET_DIR=/cargo_target 20 | ENV RUSTFLAGS="" 21 | ENV RUSTUP_HOME=/rustup 22 | 23 | RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - 24 | RUN \ 25 | apt-get update && \ 26 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 27 | ca-certificates \ 28 | protobuf-compiler \ 29 | curl \ 30 | git \ 31 | llvm \ 32 | clang \ 33 | cmake \ 34 | make \ 35 | libssl-dev \ 36 | pkg-config \ 37 | docker-compose-plugin \ 38 | nodejs \ 39 | multitail 40 | 41 | RUN \ 42 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain $RUSTC_VERSION 43 | ENV PATH=$CARGO_HOME/bin:$PATH 44 | 45 | RUN rustup target add wasm32-unknown-unknown 46 | RUN rustup component add rust-src 47 | 48 | # Install cargo binstall, using it install cargo-risczero, and using it install risc0 toolchain. 49 | RUN curl \ 50 | -L --proto '=https' --tlsv1.2 -sSf \ 51 | https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh \ 52 | | bash 53 | RUN cargo binstall --no-confirm --no-symlinks cargo-risczero 54 | RUN cargo risczero install 55 | 56 | # Install Zombienet and copy Polkadot binaries, which are all required for xtask tests 57 | RUN npm install -g @zombienet/cli 58 | 59 | COPY --from=polkadot /usr/bin/polkadot /usr/bin/ 60 | COPY --from=polkadot /usr/lib/polkadot/polkadot-prepare-worker /usr/bin/ 61 | COPY --from=polkadot /usr/lib/polkadot/polkadot-execute-worker /usr/bin/ 62 | -------------------------------------------------------------------------------- /docker/risczero.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 as builder 2 | 3 | LABEL org.opencontainers.image.source=https://github.com/thrumdev/blobs 4 | 5 | ARG RUSTC_VERSION=nightly-2023-10-16 6 | 7 | ENV CARGO_INCREMENTAL=0 8 | ENV CARGO_HOME=/cargo 9 | ENV CARGO_TARGET_DIR=/cargo_target 10 | ENV RUSTFLAGS="" 11 | ENV RUSTUP_HOME=/rustup 12 | 13 | RUN mkdir -p /cargo && \ 14 | mkdir -p /cargo_target && \ 15 | mkdir -p /rustup 16 | 17 | RUN \ 18 | apt-get update && \ 19 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 20 | ca-certificates \ 21 | protobuf-compiler \ 22 | curl \ 23 | git \ 24 | llvm \ 25 | clang \ 26 | cmake \ 27 | make \ 28 | libssl-dev \ 29 | pkg-config \ 30 | ninja-build \ 31 | python3 32 | 33 | RUN \ 34 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain $RUSTC_VERSION 35 | 36 | RUN curl \ 37 | -L --proto '=https' --tlsv1.2 -sSf \ 38 | https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh \ 39 | | bash 40 | 41 | # Mount caches because it will build on unsupported platforms. 42 | RUN \ 43 | --mount=type=cache,target=/cargo/git \ 44 | --mount=type=cache,target=/cargo/registry \ 45 | --mount=type=cache,target=/cargo_target \ 46 | $CARGO_HOME/bin/cargo binstall --no-confirm --no-symlinks cargo-risczero 47 | 48 | RUN \ 49 | apt-get update && \ 50 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3 && \ 51 | ln -s /usr/bin/python3 /usr/bin/python 52 | 53 | RUN \ 54 | --mount=type=cache,target=/cargo/git \ 55 | --mount=type=cache,target=/cargo/registry \ 56 | --mount=type=cache,target=/cargo_target \ 57 | --mount=type=cache,target=/root/.risc0/rust \ 58 | $CARGO_HOME/bin/cargo risczero install \ 59 | || ( \ 60 | $CARGO_HOME/bin/cargo risczero build-toolchain \ 61 | && rm /rustup/toolchains/risc0 \ 62 | && cp -r /root/.risc0/rust/build/aarch64-unknown-linux-gnu/stage2 /rustup/toolchains/risc0 \ 63 | ) 64 | -------------------------------------------------------------------------------- /docs-site/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs-site/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | [Yarn](https://yarnpkg.com/getting-started/install) is a pre-requisite. 8 | ``` 9 | $ yarn 10 | ``` 11 | 12 | ### Local Development 13 | 14 | ``` 15 | $ yarn start 16 | ``` 17 | 18 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 19 | 20 | ### Build 21 | 22 | ``` 23 | $ yarn build 24 | ``` 25 | 26 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 27 | 28 | ### Deployment 29 | 30 | Using SSH: 31 | 32 | ``` 33 | $ USE_SSH=true yarn deploy 34 | ``` 35 | 36 | Not using SSH: 37 | 38 | ``` 39 | $ GIT_USER= yarn deploy 40 | ``` 41 | 42 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 43 | -------------------------------------------------------------------------------- /docs-site/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs-site/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | title: Introduction 4 | 5 | --- 6 | 7 | ## Ikura 8 | 9 | Ikura exposes **sequencing** and **data availability** capabilities of [Polkadot](https://polkadot.network) and [Kusama](https://kusama.network) for general use. Use-cases include rollups and inscriptions. 10 | 11 | The Ikura codebase is located at https://github.com/thrumdev/blobs. 12 | 13 | In this documentation site, we'll often use the term Polkadot to refer to the Polkadot Relay Chain - the hub chain which provides security for everything running on Polkadot. Kusama runs on the same technology as Polkadot, so the Kusama version of Ikura (Gondatsu) works identically to the Polkadot version, just with a different network. You can mentally substitute "Polkadot" for "Kusama" when thinking about the Kusama version of Ikura. 14 | 15 | Ikura enables users to submit arbitrary data to the blockchain chain and receive guarantees about the availability of that data, as well as proofs of the order in which data were submitted. Those guarantees are: 16 | 1. The data can be fetched from the Polkadot/Kusama validator set for up to 24 hours after submission and cannot be withheld. 17 | 2. A commitment to the data's availability is stored within the blobchain and used as a proof of guarantee (1) to computer programs, such as smart contracts or Zero-Knowledge circuits. 18 | 19 | Data Availability is a key component of Layer-2 scaling approaches, and is already part of Polkadot and Kusama for use in securing Parachains. Ikura will bring this capability to use-cases beyond parachains with a minimal interface. 20 | 21 | Ikura makes a **relay-chain token utility commitment** now and forever. Submitting blobs will always make use of the DOT token on Polkadot and the KSM token on Kusama, as this is the approach with the least user friction. 22 | 23 | ## Gondatsu 24 | 25 | The version of Ikura targeting the Kusama network is **Gondatsu**. It has Parachain ID 3338 running. We currently do not make any long-term stability commitment for Gondatsu. Gondatsu currently is _centrally controlled_ with a sudo (admin) key. It is experimental. 26 | 27 | ## Integrations 28 | 29 | Ikura supports a variety of rollup SDKs out of the box. 30 | - [x] Rollkit 31 | - [x] Sovereign SDK 32 | - [ ] OP Stack 33 | - [ ] Polygon ZK-EVM 34 | -------------------------------------------------------------------------------- /docs-site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs-site", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "3.0.1", 18 | "@docusaurus/preset-classic": "3.0.1", 19 | "@mdx-js/react": "^3.0.0", 20 | "clsx": "^2.0.0", 21 | "prism-react-renderer": "^2.3.0", 22 | "react": "^18.0.0", 23 | "react-dom": "^18.0.0" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "3.0.1", 27 | "@docusaurus/types": "3.0.1" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.5%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 3 chrome version", 37 | "last 3 firefox version", 38 | "last 5 safari version" 39 | ] 40 | }, 41 | "engines": { 42 | "node": ">=18.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs-site/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | docsSidebar: [ 18 | 'intro', 19 | {type: 'category', label: "Protocol", items: [ 20 | {type: 'autogenerated', dirName: 'protocol'} 21 | ]}, 22 | {type: 'category', label: "Node Operators", items: [ 23 | {type: 'autogenerated', dirName: 'node-operators'} 24 | ]} 25 | ], 26 | }; 27 | 28 | export default sidebars; 29 | -------------------------------------------------------------------------------- /docs-site/src/components/HomepageFeatures/index.js: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Heading from '@theme/Heading'; 3 | import styles from './styles.module.css'; 4 | 5 | const FeatureList = []; 6 | 7 | function Feature({Svg, title, description}) { 8 | return ( 9 |
10 |
11 | 12 |
13 |
14 | {title} 15 |

{description}

16 |
17 |
18 | ); 19 | } 20 | 21 | export default function HomepageFeatures() { 22 | return ( 23 |
24 |
25 |
26 | {FeatureList.map((props, idx) => ( 27 | 28 | ))} 29 |
30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /docs-site/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs-site/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #c96949; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #c96949; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docs-site/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Link from '@docusaurus/Link'; 3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 4 | import Layout from '@theme/Layout'; 5 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 6 | 7 | import Heading from '@theme/Heading'; 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const {siteConfig} = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 | 16 | {siteConfig.title} 17 | 18 |

{siteConfig.tagline}

19 |
20 | 23 | Get Started 24 | 25 |
26 |
27 |
28 | ); 29 | } 30 | 31 | export default function Home() { 32 | const {siteConfig} = useDocusaurusContext(); 33 | return ( 34 | 37 | 38 |
39 | 40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /docs-site/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 2rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs-site/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs-site/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thrumdev/blobs/72c62881fe2955cf7ef9581615697e498e2c7397/docs-site/static/.nojekyll -------------------------------------------------------------------------------- /docs-site/static/img/ikura_can_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thrumdev/blobs/72c62881fe2955cf7ef9581615697e498e2c7397/docs-site/static/img/ikura_can_128.png -------------------------------------------------------------------------------- /ikura/chain/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | **/target/ 3 | **/*.txt 4 | **/*.md 5 | -------------------------------------------------------------------------------- /ikura/chain/.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo compiled files and executables 2 | **/target/ 3 | 4 | # These are backup files generated by rustfmt 5 | **/*.rs.bk 6 | 7 | # Local chain databases (default location) 8 | **/chains/ 9 | 10 | # The cache for chain data in container 11 | .local 12 | 13 | # The cache for docker container dependency 14 | /.cargo/config 15 | 16 | .DS_Store 17 | .idea 18 | .vscode 19 | -------------------------------------------------------------------------------- /ikura/chain/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thrumdev/blobs/72c62881fe2955cf7ef9581615697e498e2c7397/ikura/chain/README.md -------------------------------------------------------------------------------- /ikura/chain/node/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; 2 | 3 | fn main() { 4 | generate_cargo_keys(); 5 | 6 | rerun_if_git_head_changed(); 7 | } 8 | -------------------------------------------------------------------------------- /ikura/chain/node/src/command/export_genesis_metadata.rs: -------------------------------------------------------------------------------- 1 | use crate::command::new_partial; 2 | use sc_client_api::HeaderBackend; 3 | use sc_service::Configuration; 4 | use sp_api::{Metadata, ProvideRuntimeApi}; 5 | use sp_core::hexdisplay::HexDisplay; 6 | use std::fs; 7 | use std::io::{self, Write}; 8 | use std::path::PathBuf; 9 | 10 | /// Export the genesis metadata of the parachain. 11 | /// 12 | /// This will print the metadata into the specified file or stdout if no file is specified. By 13 | /// default, the metadata is printed in hex format. Use the `--raw` flag to print the metadata in 14 | /// binary format. 15 | /// 16 | /// To use it with subxt, you can use the following commands: 17 | /// 18 | /// $ ikura-node export-genesis-metadata --raw > metadata.bin 19 | /// $ subxt codegen --file metadata.bin | rustfmt --edition=2021 --emit=stdout > src/metadata.rs 20 | /// 21 | #[derive(Debug, clap::Parser)] 22 | #[clap(verbatim_doc_comment)] 23 | pub struct ExportGenesisMetadataCmd { 24 | #[allow(missing_docs)] 25 | #[command(flatten)] 26 | pub shared_params: sc_cli::SharedParams, 27 | 28 | /// Output file name or stdout if unspecified. 29 | #[arg()] 30 | pub output: Option, 31 | 32 | /// Write output in binary. Default is to write in hex. 33 | #[arg(short, long)] 34 | pub raw: bool, 35 | } 36 | 37 | impl ExportGenesisMetadataCmd { 38 | /// Exports the metadata for the genesis block. 39 | /// 40 | /// Basically, this returns the metadata returned from the compiled-in runtime. 41 | pub fn run(&self, config: &Configuration) -> sc_cli::Result<()> { 42 | let partials = new_partial(&config)?; 43 | let client = partials.client.clone(); 44 | let hash = client.info().genesis_hash; 45 | let metadata: sp_core::OpaqueMetadata = client 46 | .runtime_api() 47 | .metadata(hash) 48 | .map_err(|e| format!("Failed to fetch metadata from client: {:?}", e))?; 49 | 50 | let output_buf: Vec = if self.raw { 51 | metadata.to_vec() 52 | } else { 53 | format!("0x{:?}", HexDisplay::from(&*metadata)).into_bytes() 54 | }; 55 | 56 | if let Some(output) = &self.output { 57 | fs::write(output, output_buf)?; 58 | } else { 59 | io::stdout().write_all(&output_buf)?; 60 | } 61 | 62 | Ok(()) 63 | } 64 | } 65 | 66 | impl sc_cli::CliConfiguration for ExportGenesisMetadataCmd { 67 | fn shared_params(&self) -> &sc_cli::SharedParams { 68 | &self.shared_params 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ikura/chain/node/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Parachain Node Template CLI 2 | 3 | #![warn(missing_docs)] 4 | 5 | mod chain_spec; 6 | #[macro_use] 7 | mod service; 8 | mod cli; 9 | mod command; 10 | mod proposer; 11 | mod rpc; 12 | 13 | fn main() -> sc_cli::Result<()> { 14 | command::run() 15 | } 16 | -------------------------------------------------------------------------------- /ikura/chain/node/src/rpc.rs: -------------------------------------------------------------------------------- 1 | //! A collection of node-specific RPC methods. 2 | //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer 3 | //! used by Substrate nodes. This file extends those RPC definitions with 4 | //! capabilities that are specific to this project's runtime configuration. 5 | 6 | #![warn(missing_docs)] 7 | 8 | use std::sync::Arc; 9 | 10 | use ikura_primitives::{opaque::Block, AccountId, Balance, Nonce}; 11 | 12 | use sc_client_api::AuxStore; 13 | pub use sc_rpc::DenyUnsafe; 14 | use sc_transaction_pool_api::TransactionPool; 15 | use sp_api::ProvideRuntimeApi; 16 | use sp_block_builder::BlockBuilder; 17 | use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; 18 | 19 | /// A type representing all RPC extensions. 20 | pub type RpcExtension = jsonrpsee::RpcModule<()>; 21 | 22 | /// Full client dependencies 23 | pub struct FullDeps { 24 | /// The client instance to use. 25 | pub client: Arc, 26 | /// Transaction pool instance. 27 | pub pool: Arc

, 28 | /// Whether to deny unsafe calls 29 | pub deny_unsafe: DenyUnsafe, 30 | } 31 | 32 | /// Instantiate all RPC extensions. 33 | pub fn create_full( 34 | deps: FullDeps, 35 | ) -> Result> 36 | where 37 | C: ProvideRuntimeApi 38 | + HeaderBackend 39 | + AuxStore 40 | + HeaderMetadata 41 | + Send 42 | + Sync 43 | + 'static, 44 | C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, 45 | C::Api: substrate_frame_rpc_system::AccountNonceApi, 46 | C::Api: BlockBuilder, 47 | P: TransactionPool + Sync + Send + 'static, 48 | { 49 | use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; 50 | use substrate_frame_rpc_system::{System, SystemApiServer}; 51 | 52 | let mut module = RpcExtension::new(()); 53 | let FullDeps { 54 | client, 55 | pool, 56 | deny_unsafe, 57 | } = deps; 58 | 59 | module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; 60 | module.merge(TransactionPayment::new(client).into_rpc())?; 61 | Ok(module) 62 | } 63 | -------------------------------------------------------------------------------- /ikura/chain/pallets/blobs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pallet-ikura-blobs" 3 | description = "FRAME pallet for submitting blobs" 4 | version = "0.1.0" 5 | authors.workspace = true 6 | homepage.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | 11 | [package.metadata.docs.rs] 12 | targets = ["x86_64-unknown-linux-gnu"] 13 | 14 | [dependencies] 15 | parity-scale-codec = { workspace = true, features = ["derive"] } 16 | scale-info = { workspace = true, features = ["derive"] } 17 | sha2 = { workspace = true } 18 | 19 | # Substrate 20 | frame-benchmarking = { workspace = true, optional = true } 21 | frame-support = { workspace = true } 22 | frame-system = { workspace = true } 23 | sp-std = { workspace = true } 24 | sp-runtime = { workspace = true } 25 | sp-io = { workspace = true } 26 | 27 | # Local 28 | ikura-primitives = { workspace = true } 29 | ikura-nmt = { workspace = true } 30 | 31 | [dev-dependencies] 32 | ikura-nmt = { workspace = true, default-features = true } 33 | 34 | # Substrate 35 | sp-core = { workspace = true } 36 | sp-state-machine = { workspace = true } 37 | sp-trie = { workspace = true } 38 | 39 | [features] 40 | default = ["std"] 41 | runtime-benchmarks = [ 42 | "frame-benchmarking/runtime-benchmarks", 43 | "frame-support/runtime-benchmarks", 44 | "frame-system/runtime-benchmarks", 45 | "sp-runtime/runtime-benchmarks", 46 | ] 47 | std = [ 48 | "parity-scale-codec/std", 49 | "scale-info/std", 50 | "frame-benchmarking/std", 51 | "frame-support/std", 52 | "frame-system/std", 53 | "sp-std/std", 54 | "ikura-nmt/native", 55 | "ikura-primitives/std", 56 | ] 57 | try-runtime = [ "frame-support/try-runtime" ] 58 | -------------------------------------------------------------------------------- /ikura/chain/pallets/blobs/README.md: -------------------------------------------------------------------------------- 1 | License: Unlicense 2 | -------------------------------------------------------------------------------- /ikura/chain/pallets/blobs/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate as pallet_blobs; 2 | use frame_support::{parameter_types, traits::ConstU32}; 3 | use sp_core::H256; 4 | use sp_runtime::{ 5 | traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, 6 | BuildStorage, MultiSignature, 7 | }; 8 | 9 | type Block = frame_system::mocking::MockBlock; 10 | 11 | // Real accounts must be used to test and benchmark on_finalize 12 | pub type AccountId = <::Signer as IdentifyAccount>::AccountId; 13 | 14 | // Configure a mock runtime to test the pallet. 15 | frame_support::construct_runtime!( 16 | pub enum Test { 17 | System: frame_system::{Pallet, Call, Config, Storage, Event}, 18 | Blobs: crate::{Pallet, Call, Storage, Event}, 19 | } 20 | ); 21 | 22 | parameter_types! { 23 | pub const BlockHashCount: u64 = 250; 24 | pub const SS58Prefix: u8 = 42; 25 | } 26 | 27 | impl frame_system::Config for Test { 28 | type BaseCallFilter = frame_support::traits::Everything; 29 | type BlockWeights = (); 30 | type BlockLength = (); 31 | type DbWeight = (); 32 | type RuntimeOrigin = RuntimeOrigin; 33 | type RuntimeCall = RuntimeCall; 34 | type RuntimeTask = RuntimeTask; 35 | type Nonce = u64; 36 | type Hash = H256; 37 | type Hashing = BlakeTwo256; 38 | type AccountId = AccountId; 39 | type Lookup = IdentityLookup; 40 | type Block = Block; 41 | type RuntimeEvent = RuntimeEvent; 42 | type BlockHashCount = BlockHashCount; 43 | type Version = (); 44 | type PalletInfo = PalletInfo; 45 | type AccountData = (); 46 | type OnNewAccount = (); 47 | type OnKilledAccount = (); 48 | type SystemWeightInfo = (); 49 | type SS58Prefix = SS58Prefix; 50 | type OnSetCode = (); 51 | type MaxConsumers = frame_support::traits::ConstU32<16>; 52 | } 53 | 54 | impl pallet_blobs::Config for Test { 55 | type RuntimeEvent = RuntimeEvent; 56 | type MaxBlobs = ConstU32<16>; 57 | type MaxBlobSize = ConstU32<1024>; 58 | type MaxTotalBlobSize = ConstU32<{ 10 * 1024 }>; 59 | type WeightInfo = (); 60 | } 61 | 62 | // Build genesis storage according to the mock runtime. 63 | // TODO: https://github.com/thrumdev/blobs/issues/28 64 | #[allow(unused)] 65 | pub fn new_test_ext() -> sp_io::TestExternalities { 66 | frame_system::GenesisConfig::::default() 67 | .build_storage() 68 | .unwrap() 69 | .into() 70 | } 71 | -------------------------------------------------------------------------------- /ikura/chain/pallets/blobs/src/namespace_param.rs: -------------------------------------------------------------------------------- 1 | //! Namespaces as a parameter. 2 | 3 | use ikura_primitives::namespace; 4 | use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; 5 | use scale_info::TypeInfo; 6 | use sp_runtime::RuntimeDebug; 7 | 8 | /// Type-safe wrapper around an unvalidated blob namespace. 9 | #[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Clone, PartialEq, RuntimeDebug)] 10 | pub struct UnvalidatedNamespace([u8; 16]); 11 | 12 | impl UnvalidatedNamespace { 13 | /// Validate the namespace, extracting the full data. 14 | pub fn validate(&self) -> Result { 15 | namespace::validate(&self.0).map(|()| u128::from_be_bytes(self.0)) 16 | } 17 | } 18 | 19 | impl From<[u8; 16]> for UnvalidatedNamespace { 20 | fn from(x: [u8; 16]) -> Self { 21 | UnvalidatedNamespace(x) 22 | } 23 | } 24 | 25 | impl From for UnvalidatedNamespace { 26 | fn from(x: u128) -> Self { 27 | UnvalidatedNamespace(x.to_be_bytes()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ikura/chain/pallets/length-fee-adjustment/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pallet-ikura-length-fee-adjustment" 3 | version = "0.1.0" 4 | description = "Pallet for fee Adjustment based on block length and weight" 5 | authors.workspace = true 6 | homepage.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | 11 | [package.metadata.docs.rs] 12 | targets = ["x86_64-unknown-linux-gnu"] 13 | 14 | [dependencies] 15 | parity-scale-codec = { workspace = true, features = ["derive"] } 16 | scale-info = { workspace = true, features = ["derive"] } 17 | frame-benchmarking = { workspace = true } 18 | frame-support = { workspace = true } 19 | frame-system = { workspace = true } 20 | pallet-transaction-payment = { workspace = true } 21 | pallet-balances = { workspace = true } 22 | sp-io = { workspace = true, optional = true } 23 | sp-runtime = { workspace = true } 24 | sp-weights = { workspace = true } 25 | sp-arithmetic = { workspace = true } 26 | cumulus-pallet-parachain-system = { workspace = true, features = ["parameterized-consensus-hook",] } 27 | polkadot-primitives = { workspace = true } 28 | 29 | [dev-dependencies] 30 | sp-io = { workspace = true } 31 | sp-core = { workspace = true } 32 | ikura-primitives = { workspace = true, features = ["std"] } 33 | 34 | [features] 35 | default = [ "std" ] 36 | std = [ 37 | "parity-scale-codec/std", 38 | "frame-support/std", 39 | "frame-system/std", 40 | "sp-runtime/std", 41 | "sp-weights/std", 42 | "scale-info/std", 43 | "sp-arithmetic/std", 44 | "pallet-transaction-payment/std", 45 | "pallet-balances/std", 46 | "cumulus-pallet-parachain-system/std", 47 | "polkadot-primitives/std", 48 | ] 49 | 50 | runtime-benchmarks = [ 51 | "frame-benchmarking/runtime-benchmarks", 52 | "frame-support/runtime-benchmarks", 53 | "frame-system/runtime-benchmarks", 54 | "sp-runtime/runtime-benchmarks", 55 | "dep:sp-io" 56 | ] 57 | try-runtime = [ "frame-support/try-runtime" ] 58 | -------------------------------------------------------------------------------- /ikura/chain/primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-primitives" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | parity-scale-codec = { workspace = true, features = ["derive"] } 14 | 15 | sp-runtime = { workspace = true } 16 | sp-core = { workspace = true } 17 | sp-consensus-aura = { workspace = true } 18 | 19 | [dev-dependencies] 20 | quickcheck = { workspace = true } 21 | quickcheck_macros = { workspace = true } 22 | 23 | [features] 24 | default = ["std"] 25 | std = ["parity-scale-codec/std", "sp-runtime/std", "sp-core/std", "sp-consensus-aura/std"] 26 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | fn main() { 3 | substrate_wasm_builder::WasmBuilder::new() 4 | .with_current_project() 5 | .export_heap_base() 6 | .import_memory() 7 | .build() 8 | } 9 | 10 | /// The wasm builder is deactivated when compiling 11 | /// this crate for wasm to speed up the compilation. 12 | #[cfg(not(feature = "std"))] 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/src/weights/block_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, Weight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// Importing a block with 0 Extrinsics. 26 | pub const BlockExecutionWeight: Weight = 27 | Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(5_000_000), 0); 28 | } 29 | 30 | #[cfg(test)] 31 | mod test_weights { 32 | use frame_support::weights::constants; 33 | 34 | /// Checks that the weight exists and is sane. 35 | // NOTE: If this test fails but you are sure that the generated values are fine, 36 | // you can delete it. 37 | #[test] 38 | fn sane() { 39 | let w = super::constants::BlockExecutionWeight::get(); 40 | 41 | // At least 100 µs. 42 | assert!( 43 | w.ref_time() >= 100u64 * constants::WEIGHT_REF_TIME_PER_MICROS, 44 | "Weight should be at least 100 µs." 45 | ); 46 | // At most 50 ms. 47 | assert!( 48 | w.ref_time() <= 50u64 * constants::WEIGHT_REF_TIME_PER_MILLIS, 49 | "Weight should be at most 50 ms." 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/src/weights/extrinsic_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, Weight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// Executing a NO-OP `System::remarks` Extrinsic. 26 | pub const ExtrinsicBaseWeight: Weight = 27 | Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(125_000), 0); 28 | } 29 | 30 | #[cfg(test)] 31 | mod test_weights { 32 | use frame_support::weights::constants; 33 | 34 | /// Checks that the weight exists and is sane. 35 | // NOTE: If this test fails but you are sure that the generated values are fine, 36 | // you can delete it. 37 | #[test] 38 | fn sane() { 39 | let w = super::constants::ExtrinsicBaseWeight::get(); 40 | 41 | // At least 10 µs. 42 | assert!( 43 | w.ref_time() >= 10u64 * constants::WEIGHT_REF_TIME_PER_MICROS, 44 | "Weight should be at least 10 µs." 45 | ); 46 | // At most 1 ms. 47 | assert!( 48 | w.ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 49 | "Weight should be at most 1 ms." 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/src/weights/mod.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | //! Expose the auto generated weight files. 19 | 20 | pub mod block_weights; 21 | pub mod extrinsic_weights; 22 | pub mod paritydb_weights; 23 | pub mod rocksdb_weights; 24 | 25 | pub use block_weights::constants::BlockExecutionWeight; 26 | pub use extrinsic_weights::constants::ExtrinsicBaseWeight; 27 | pub use paritydb_weights::constants::ParityDbWeight; 28 | pub use rocksdb_weights::constants::RocksDbWeight; 29 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/src/weights/paritydb_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, RuntimeDbWeight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights 26 | /// are available for brave runtime engineers who may want to try this out as default. 27 | pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight { 28 | read: 8_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 29 | write: 50_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 30 | }; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test_db_weights { 35 | use super::constants::ParityDbWeight as W; 36 | use frame_support::weights::constants; 37 | 38 | /// Checks that all weights exist and have sane values. 39 | // NOTE: If this test fails but you are sure that the generated values are fine, 40 | // you can delete it. 41 | #[test] 42 | fn sane() { 43 | // At least 1 µs. 44 | assert!( 45 | W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 46 | "Read weight should be at least 1 µs." 47 | ); 48 | assert!( 49 | W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 50 | "Write weight should be at least 1 µs." 51 | ); 52 | // At most 1 ms. 53 | assert!( 54 | W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 55 | "Read weight should be at most 1 ms." 56 | ); 57 | assert!( 58 | W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 59 | "Write weight should be at most 1 ms." 60 | ); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/gondatsu/src/weights/rocksdb_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, RuntimeDbWeight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// By default, Substrate uses `RocksDB`, so this will be the weight used throughout 26 | /// the runtime. 27 | pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight { 28 | read: 25_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 29 | write: 100_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 30 | }; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test_db_weights { 35 | use super::constants::RocksDbWeight as W; 36 | use frame_support::weights::constants; 37 | 38 | /// Checks that all weights exist and have sane values. 39 | // NOTE: If this test fails but you are sure that the generated values are fine, 40 | // you can delete it. 41 | #[test] 42 | fn sane() { 43 | // At least 1 µs. 44 | assert!( 45 | W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 46 | "Read weight should be at least 1 µs." 47 | ); 48 | assert!( 49 | W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 50 | "Write weight should be at least 1 µs." 51 | ); 52 | // At most 1 ms. 53 | assert!( 54 | W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 55 | "Read weight should be at most 1 ms." 56 | ); 57 | assert!( 58 | W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 59 | "Write weight should be at most 1 ms." 60 | ); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | fn main() { 3 | substrate_wasm_builder::WasmBuilder::new() 4 | .with_current_project() 5 | .export_heap_base() 6 | .import_memory() 7 | .build() 8 | } 9 | 10 | /// The wasm builder is deactivated when compiling 11 | /// this crate for wasm to speed up the compilation. 12 | #[cfg(not(feature = "std"))] 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/src/weights/block_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, Weight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// Importing a block with 0 Extrinsics. 26 | pub const BlockExecutionWeight: Weight = 27 | Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(5_000_000), 0); 28 | } 29 | 30 | #[cfg(test)] 31 | mod test_weights { 32 | use frame_support::weights::constants; 33 | 34 | /// Checks that the weight exists and is sane. 35 | // NOTE: If this test fails but you are sure that the generated values are fine, 36 | // you can delete it. 37 | #[test] 38 | fn sane() { 39 | let w = super::constants::BlockExecutionWeight::get(); 40 | 41 | // At least 100 µs. 42 | assert!( 43 | w.ref_time() >= 100u64 * constants::WEIGHT_REF_TIME_PER_MICROS, 44 | "Weight should be at least 100 µs." 45 | ); 46 | // At most 50 ms. 47 | assert!( 48 | w.ref_time() <= 50u64 * constants::WEIGHT_REF_TIME_PER_MILLIS, 49 | "Weight should be at most 50 ms." 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/src/weights/extrinsic_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, Weight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// Executing a NO-OP `System::remarks` Extrinsic. 26 | pub const ExtrinsicBaseWeight: Weight = 27 | Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(125_000), 0); 28 | } 29 | 30 | #[cfg(test)] 31 | mod test_weights { 32 | use frame_support::weights::constants; 33 | 34 | /// Checks that the weight exists and is sane. 35 | // NOTE: If this test fails but you are sure that the generated values are fine, 36 | // you can delete it. 37 | #[test] 38 | fn sane() { 39 | let w = super::constants::ExtrinsicBaseWeight::get(); 40 | 41 | // At least 10 µs. 42 | assert!( 43 | w.ref_time() >= 10u64 * constants::WEIGHT_REF_TIME_PER_MICROS, 44 | "Weight should be at least 10 µs." 45 | ); 46 | // At most 1 ms. 47 | assert!( 48 | w.ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 49 | "Weight should be at most 1 ms." 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/src/weights/mod.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | //! Expose the auto generated weight files. 19 | 20 | pub mod block_weights; 21 | pub mod extrinsic_weights; 22 | pub mod paritydb_weights; 23 | pub mod rocksdb_weights; 24 | 25 | pub use block_weights::constants::BlockExecutionWeight; 26 | pub use extrinsic_weights::constants::ExtrinsicBaseWeight; 27 | pub use paritydb_weights::constants::ParityDbWeight; 28 | pub use rocksdb_weights::constants::RocksDbWeight; 29 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/src/weights/paritydb_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, RuntimeDbWeight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights 26 | /// are available for brave runtime engineers who may want to try this out as default. 27 | pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight { 28 | read: 8_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 29 | write: 50_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 30 | }; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test_db_weights { 35 | use super::constants::ParityDbWeight as W; 36 | use frame_support::weights::constants; 37 | 38 | /// Checks that all weights exist and have sane values. 39 | // NOTE: If this test fails but you are sure that the generated values are fine, 40 | // you can delete it. 41 | #[test] 42 | fn sane() { 43 | // At least 1 µs. 44 | assert!( 45 | W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 46 | "Read weight should be at least 1 µs." 47 | ); 48 | assert!( 49 | W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 50 | "Write weight should be at least 1 µs." 51 | ); 52 | // At most 1 ms. 53 | assert!( 54 | W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 55 | "Read weight should be at most 1 ms." 56 | ); 57 | assert!( 58 | W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 59 | "Write weight should be at most 1 ms." 60 | ); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ikura/chain/runtimes/test/src/weights/rocksdb_weights.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | pub mod constants { 19 | use frame_support::{ 20 | parameter_types, 21 | weights::{constants, RuntimeDbWeight}, 22 | }; 23 | 24 | parameter_types! { 25 | /// By default, Substrate uses `RocksDB`, so this will be the weight used throughout 26 | /// the runtime. 27 | pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight { 28 | read: 25_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 29 | write: 100_000 * constants::WEIGHT_REF_TIME_PER_NANOS, 30 | }; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test_db_weights { 35 | use super::constants::RocksDbWeight as W; 36 | use frame_support::weights::constants; 37 | 38 | /// Checks that all weights exist and have sane values. 39 | // NOTE: If this test fails but you are sure that the generated values are fine, 40 | // you can delete it. 41 | #[test] 42 | fn sane() { 43 | // At least 1 µs. 44 | assert!( 45 | W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 46 | "Read weight should be at least 1 µs." 47 | ); 48 | assert!( 49 | W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS, 50 | "Write weight should be at least 1 µs." 51 | ); 52 | // At most 1 ms. 53 | assert!( 54 | W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 55 | "Read weight should be at most 1 ms." 56 | ); 57 | assert!( 58 | W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS, 59 | "Write weight should be at most 1 ms." 60 | ); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ikura/nmt/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /ikura/nmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-nmt" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | sha2 = { workspace = true } 14 | nmt-rs = { workspace = true } 15 | serde = { workspace = true, optional = true } 16 | ikura-serde-util = { workspace = true, optional = true } 17 | 18 | 19 | [features] 20 | default = ["native"] 21 | native = [] 22 | serde = ["dep:serde", "dep:ikura-serde-util", "nmt-rs/serde"] 23 | -------------------------------------------------------------------------------- /ikura/nmt/src/blob_metadata.rs: -------------------------------------------------------------------------------- 1 | use crate::{leaf::NmtLeaf, ns::Namespace}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct BlobMetadata { 5 | pub namespace: Namespace, 6 | pub leaf: NmtLeaf, 7 | } 8 | -------------------------------------------------------------------------------- /ikura/nmt/src/leaf.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub struct NmtLeaf { 3 | pub extrinsic_index: u32, 4 | pub who: [u8; 32], 5 | pub blob_hash: [u8; 32], 6 | } 7 | 8 | impl NmtLeaf { 9 | /// Read the NMT leaf from the given raw bytes. 10 | pub fn from_raw_bytes(raw: &[u8; 68]) -> Self { 11 | let mut extrinsic_index = [0u8; 4]; 12 | extrinsic_index.copy_from_slice(&raw[0..4]); 13 | let extrinsic_index = u32::from_le_bytes(extrinsic_index); 14 | 15 | let mut who = [0u8; 32]; 16 | who.copy_from_slice(&raw[4..36]); 17 | 18 | let mut blob_hash = [0u8; 32]; 19 | blob_hash.copy_from_slice(&raw[36..68]); 20 | 21 | Self { 22 | extrinsic_index, 23 | who, 24 | blob_hash, 25 | } 26 | } 27 | 28 | /// Convert the NMT leaf to raw bytes. 29 | pub fn to_raw_bytes(&self) -> [u8; 68] { 30 | let mut raw = [0u8; 68]; 31 | raw[0..4].copy_from_slice(&self.extrinsic_index.to_le_bytes()); 32 | raw[4..36].copy_from_slice(&self.who); 33 | raw[36..68].copy_from_slice(&self.blob_hash); 34 | raw 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ikura/nmt/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides utilities for working with the namespaced merkle tree (NMT) used in Ikura. 2 | 3 | #![no_std] 4 | 5 | extern crate alloc; 6 | 7 | pub const NS_ID_SIZE: usize = 16; 8 | 9 | mod blob_metadata; 10 | mod leaf; 11 | mod ns; 12 | mod ns_proof; 13 | mod root; 14 | mod tree; 15 | 16 | #[cfg(test)] 17 | mod tests; 18 | 19 | pub use blob_metadata::BlobMetadata; 20 | pub use leaf::NmtLeaf; 21 | pub use ns::Namespace; 22 | pub use ns_proof::NamespaceProof; 23 | pub use root::TreeRoot; 24 | pub use tree::{PushLeafErr, TreeBuilder}; 25 | 26 | use alloc::vec::Vec; 27 | 28 | /// Creates a namespaced merkle tree from the list of blob metadata. 29 | pub fn tree_from_blobs(mut blob_metadata: Vec) -> TreeBuilder { 30 | blob_metadata.sort_by_key(|blob| blob.namespace); 31 | 32 | let mut tree = TreeBuilder::new(); 33 | for blob in blob_metadata { 34 | match tree.push_leaf(blob.namespace, blob.leaf) { 35 | Ok(()) => (), 36 | Err(PushLeafErr::AscendingOrder) => { 37 | panic!("sorted by namespace, so this should not happen") 38 | } 39 | } 40 | } 41 | tree 42 | } 43 | -------------------------------------------------------------------------------- /ikura/nmt/src/ns_proof.rs: -------------------------------------------------------------------------------- 1 | use crate::{ns::Namespace, NmtLeaf, TreeRoot, NS_ID_SIZE}; 2 | use alloc::vec::Vec; 3 | 4 | #[derive(Debug)] 5 | pub enum VerifyErr { 6 | BlobCountMismatch, 7 | MalformedLeaf(usize), 8 | BlobHashMismatch(usize), 9 | VerifyProof, 10 | } 11 | 12 | #[derive(Debug, Clone)] 13 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 14 | pub struct NamespaceProof { 15 | pub(crate) leaves: Vec>, 16 | pub(crate) proof: nmt_rs::NamespaceProof, NS_ID_SIZE>, 17 | } 18 | 19 | impl NamespaceProof { 20 | pub fn verify( 21 | self, 22 | blob_hashes: &[[u8; 32]], 23 | root: TreeRoot, 24 | namespace: Namespace, 25 | ) -> Result<(), VerifyErr> { 26 | if blob_hashes.len() != self.leaves.len() { 27 | return Err(VerifyErr::BlobCountMismatch); 28 | } 29 | let root = nmt_rs::NamespacedHash::::new( 30 | root.min_ns.nmt_namespace_id(), 31 | root.max_ns.nmt_namespace_id(), 32 | root.root, 33 | ); 34 | self.proof 35 | .verify_complete_namespace(&root, &self.leaves, namespace.nmt_namespace_id()) 36 | .map_err(|_| VerifyErr::VerifyProof)?; 37 | for (i, leaf) in self.leaves.iter().enumerate() { 38 | if leaf.len() != 68 { 39 | return Err(VerifyErr::MalformedLeaf(i)); 40 | } 41 | let leaf = NmtLeaf::from_raw_bytes( 42 | leaf.as_slice().try_into().expect("verified to be 68 bytes"), 43 | ); 44 | if leaf.blob_hash != blob_hashes[i] { 45 | return Err(VerifyErr::BlobHashMismatch(i)); 46 | } 47 | } 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ikura/nmt/src/root.rs: -------------------------------------------------------------------------------- 1 | pub use crate::ns::Namespace; 2 | 3 | #[derive(Clone, Debug, PartialEq, Eq)] 4 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 5 | pub struct TreeRoot { 6 | #[cfg_attr(feature = "serde", serde(with = "ikura_serde_util::bytes32_hex"))] 7 | pub root: [u8; 32], 8 | pub min_ns: Namespace, 9 | pub max_ns: Namespace, 10 | } 11 | 12 | impl TreeRoot { 13 | pub fn from_raw_bytes(raw: &[u8; 68]) -> Self { 14 | let mut root = [0u8; 32]; 15 | root.copy_from_slice(&raw[0..32]); 16 | 17 | let min_ns = Namespace::from_raw_bytes(raw[32..48].try_into().unwrap()); 18 | let max_ns = Namespace::from_raw_bytes(raw[48..64].try_into().unwrap()); 19 | 20 | Self { 21 | root, 22 | min_ns, 23 | max_ns, 24 | } 25 | } 26 | 27 | pub fn to_raw_bytes(&self) -> [u8; 68] { 28 | let mut raw = [0u8; 68]; 29 | raw[0..32].copy_from_slice(&self.root); 30 | raw[32..48].copy_from_slice(&self.min_ns.to_raw_bytes()); 31 | raw[48..64].copy_from_slice(&self.max_ns.to_raw_bytes()); 32 | raw 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ikura/nmt/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{tree_from_blobs, BlobMetadata, Namespace, NmtLeaf, TreeBuilder}; 2 | use alloc::vec::Vec; 3 | 4 | struct MockBuilder { 5 | blobs: Vec, 6 | } 7 | 8 | impl MockBuilder { 9 | fn new() -> Self { 10 | Self { blobs: Vec::new() } 11 | } 12 | 13 | fn push_blob(&mut self, who: [u8; 32], namespace: Namespace, blob_hash: [u8; 32]) { 14 | let index = self.blobs.len(); 15 | self.blobs.push(BlobMetadata { 16 | namespace, 17 | leaf: NmtLeaf { 18 | extrinsic_index: index as u32, 19 | who, 20 | blob_hash, 21 | }, 22 | }); 23 | } 24 | 25 | fn tree(&self) -> TreeBuilder { 26 | tree_from_blobs(self.blobs.clone()) 27 | } 28 | } 29 | 30 | #[test] 31 | fn two_same_blobs() { 32 | let mut b = MockBuilder::new(); 33 | b.push_blob([1u8; 32], Namespace::from_u128_be(1), [2u8; 32]); 34 | b.push_blob([1u8; 32], Namespace::from_u128_be(1), [2u8; 32]); 35 | let mut tree = b.tree(); 36 | let proof = tree.proof(Namespace::from_u128_be(1)); 37 | assert!(proof 38 | .verify( 39 | &[[2u8; 32], [2u8; 32]], 40 | tree.root(), 41 | Namespace::from_u128_be(1) 42 | ) 43 | .is_ok()); 44 | } 45 | 46 | #[test] 47 | fn empty() { 48 | let mut tree = MockBuilder::new().tree(); 49 | let proof = tree.proof(Namespace::from_u128_be(1)); 50 | assert!(proof 51 | .verify(&[], tree.root(), Namespace::from_u128_be(1)) 52 | .is_ok()); 53 | } 54 | 55 | #[test] 56 | fn empty_absent_namespace_id() { 57 | let mut tree = MockBuilder::new().tree(); 58 | let proof = tree.proof(Namespace::from_u128_be(1)); 59 | assert!(proof 60 | .verify(&[], tree.root(), Namespace::from_u128_be(2)) 61 | .is_ok()); 62 | } 63 | 64 | #[test] 65 | fn proof_absent_namespace_id() { 66 | let mut b = MockBuilder::new(); 67 | b.push_blob([1u8; 32], Namespace::from_u128_be(1), [2u8; 32]); 68 | let mut tree = b.tree(); 69 | let proof = tree.proof(Namespace::from_u128_be(2)); 70 | assert!(proof 71 | .verify(&[], tree.root(), Namespace::from_u128_be(2)) 72 | .is_ok()); 73 | } 74 | 75 | #[test] 76 | fn wrong_namespace_id() { 77 | let mut b = MockBuilder::new(); 78 | b.push_blob([1u8; 32], Namespace::from_u128_be(1), [2u8; 32]); 79 | let mut tree = b.tree(); 80 | let proof = tree.proof(Namespace::from_u128_be(2)); 81 | assert!(proof 82 | .verify(&[], tree.root(), Namespace::from_u128_be(1)) 83 | .is_err()); 84 | } 85 | -------------------------------------------------------------------------------- /ikura/nmt/src/tree.rs: -------------------------------------------------------------------------------- 1 | use crate::{leaf::NmtLeaf, ns::Namespace, ns_proof::NamespaceProof, root::TreeRoot, NS_ID_SIZE}; 2 | 3 | use nmt_rs::{simple_merkle::db::MemDb, NamespaceMerkleTree, NamespacedHash, NamespacedSha2Hasher}; 4 | 5 | #[derive(Debug)] 6 | pub enum PushLeafErr { 7 | /// The namespace is not in ascending order. 8 | AscendingOrder, 9 | } 10 | 11 | pub struct TreeBuilder { 12 | tree: NamespaceMerkleTree< 13 | MemDb>, 14 | NamespacedSha2Hasher, 15 | NS_ID_SIZE, 16 | >, 17 | last_namespace: Namespace, 18 | } 19 | 20 | impl TreeBuilder { 21 | pub fn new() -> Self { 22 | Self { 23 | tree: NamespaceMerkleTree::new(), 24 | last_namespace: Namespace::from_u128_be(0), 25 | } 26 | } 27 | 28 | pub fn push_leaf( 29 | &mut self, 30 | namespace: Namespace, 31 | nmt_leaf: NmtLeaf, 32 | ) -> Result<(), PushLeafErr> { 33 | if namespace < self.last_namespace { 34 | return Err(PushLeafErr::AscendingOrder); 35 | } 36 | self.last_namespace = namespace; 37 | let leaf = nmt_leaf.to_raw_bytes(); 38 | self.tree 39 | .push_leaf(&leaf, namespace.nmt_namespace_id()) 40 | .expect("the error is manually checked above"); 41 | 42 | Ok(()) 43 | } 44 | 45 | pub fn root(&mut self) -> TreeRoot { 46 | let root = self.tree.root(); 47 | let min_ns = Namespace::with_nmt_namespace_id(root.min_namespace()); 48 | let max_ns = Namespace::with_nmt_namespace_id(root.max_namespace()); 49 | TreeRoot { 50 | root: root.hash(), 51 | min_ns, 52 | max_ns, 53 | } 54 | } 55 | 56 | pub fn proof(&mut self, namespace: Namespace) -> NamespaceProof { 57 | let (leaves, proof) = self 58 | .tree 59 | .get_namespace_with_proof(namespace.nmt_namespace_id()); 60 | NamespaceProof { leaves, proof } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ikura/serde-util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-serde-util" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | #serde = { workspace = true } 14 | serde = { version = "1.0.130", default-features = false } 15 | hex = { workspace = true } 16 | base64 = { workspace = true } 17 | 18 | [dev-dependencies] 19 | quickcheck = { workspace = true } 20 | quickcheck_macros = { workspace = true } 21 | serde_json = { workspace = true } 22 | serde = { workspace = true, features = ["derive"] } 23 | -------------------------------------------------------------------------------- /ikura/shim/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-shim" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | ikura-nmt = { workspace = true, default-features = true, features = ["serde"] } 14 | ikura-serde-util = { workspace = true } 15 | ikura-subxt = { workspace = true } 16 | ikura-primitives = { workspace = true, default-features = true } 17 | ikura-shim-common-sovereign = { workspace = true, default-features = true, features = ["server"] } 18 | 19 | anyhow = { workspace = true } 20 | clap = { workspace = true, features = ["derive", "env"] } 21 | futures = { workspace = true } 22 | jsonrpsee = { workspace = true, features = ["ws-client", "server"] } 23 | tracing = { workspace = true } 24 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 25 | tokio = { workspace = true, features = ["rt-multi-thread", "macros", "net"] } 26 | async-trait = { workspace = true } 27 | serde = { workspace = true } 28 | serde_json = { workspace = true } 29 | subxt = { workspace = true } 30 | subxt-signer = { workspace = true, features = ["subxt"] } 31 | sha2 = { workspace = true, default-features = true } 32 | url = { workspace = true } 33 | hex = { workspace = true } 34 | tonic = { workspace = true } 35 | prost = { workspace = true } 36 | 37 | [build-dependencies] 38 | tonic-build = { workspace = true } 39 | 40 | [dev-dependencies] 41 | temp-dir = { workspace = true } 42 | -------------------------------------------------------------------------------- /ikura/shim/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 as builder 2 | 3 | LABEL org.opencontainers.image.source=https://github.com/thrumdev/blobs 4 | 5 | ARG RUSTC_VERSION=nightly-2023-10-16 6 | 7 | ENV CARGO_INCREMENTAL=0 8 | ENV CARGO_HOME=/cargo 9 | ENV CARGO_TARGET_DIR=/cargo_target 10 | ENV RUSTFLAGS="" 11 | ENV RUSTUP_HOME=/rustup 12 | ENV WASM_BUILD_WORKSPACE_HINT=/ikura 13 | # Force fetching git deps with git CLI instead of libgit2 to workaround build failures. 14 | ENV CARGO_NET_GIT_FETCH_WITH_CLI=true 15 | 16 | RUN mkdir -p /cargo && \ 17 | mkdir -p /cargo_target && \ 18 | mkdir -p /rustup 19 | 20 | RUN \ 21 | apt-get update && \ 22 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 23 | ca-certificates \ 24 | protobuf-compiler \ 25 | curl \ 26 | git \ 27 | llvm \ 28 | clang \ 29 | cmake \ 30 | make 31 | 32 | RUN \ 33 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain $RUSTC_VERSION 34 | 35 | WORKDIR /ikura 36 | COPY . /ikura 37 | 38 | FROM builder AS builder-release 39 | 40 | RUN --mount=type=cache,id=ikura-shim,target=/cargo/git \ 41 | --mount=type=cache,id=ikura-shim,target=/cargo/registry \ 42 | --mount=type=cache,id=ikura-shim,target=/cargo_target \ 43 | $CARGO_HOME/bin/cargo build --locked --release -p ikura-shim && \ 44 | cp /cargo_target/release/ikura-shim /usr/bin/ikura-shim 45 | 46 | FROM ubuntu:20.04 47 | 48 | ENV TINI_VERSION v0.19.0 49 | ARG TARGETARCH 50 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} /tini 51 | RUN chmod +x /tini 52 | 53 | RUN \ 54 | apt-get update && \ 55 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 56 | ca-certificates \ 57 | curl 58 | 59 | COPY --from=builder-release /usr/bin/ikura-shim /usr/bin/ikura-shim 60 | 61 | EXPOSE 10995 62 | 63 | ENTRYPOINT ["/tini", "--", "/usr/bin/ikura-shim"] 64 | CMD ["serve", "-p", "10995"] 65 | -------------------------------------------------------------------------------- /ikura/shim/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::compile_protos("proto/da.proto")?; 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /ikura/shim/common/sovereign/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-shim-common-sovereign" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = "0.1.74" 10 | jsonrpsee = { version = "0.20.3", features = ["macros"] } 11 | serde = { version = "1.0.192", features = ["derive"] } 12 | ikura-nmt = { path = "../../../nmt", features = ["serde"] } 13 | ikura-serde-util = { path = "../../../serde-util" } 14 | 15 | [features] 16 | default = ["client", "server"] 17 | client = ["jsonrpsee/ws-client"] 18 | server = ["jsonrpsee/server"] 19 | -------------------------------------------------------------------------------- /ikura/shim/common/sovereign/src/lib.rs: -------------------------------------------------------------------------------- 1 | use jsonrpsee::proc_macros::rpc; 2 | 3 | #[cfg(not(any(feature = "server", feature = "client")))] 4 | compile_error!("either feature \"server\" or \"client\" must be enabled"); 5 | 6 | pub type JsonRPCError = jsonrpsee::types::ErrorObjectOwned; 7 | 8 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 9 | pub struct Block { 10 | #[serde(with = "ikura_serde_util::bytes32_hex")] 11 | pub block_hash: [u8; 32], 12 | #[serde(with = "ikura_serde_util::bytes32_hex")] 13 | pub prev_hash: [u8; 32], 14 | pub timestamp: u64, 15 | pub nmt_root: ikura_nmt::TreeRoot, 16 | pub proof: ikura_nmt::NamespaceProof, 17 | pub blobs: Vec, 18 | } 19 | 20 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 21 | pub struct Blob { 22 | #[serde(with = "ikura_serde_util::bytes32_hex")] 23 | pub sender: [u8; 32], 24 | #[serde(with = "ikura_serde_util::bytes_hex")] 25 | pub data: Vec, 26 | } 27 | 28 | #[cfg_attr(all(feature = "client", not(feature = "server")), rpc(client))] 29 | #[cfg_attr(all(feature = "server", not(feature = "client")), rpc(server))] 30 | #[cfg_attr(all(feature = "client", feature = "server"), rpc(client, server))] 31 | pub trait SovereignRPC { 32 | #[method(name = "sovereign_getBlock")] 33 | async fn get_block( 34 | &self, 35 | height: u64, 36 | namespace: ikura_nmt::Namespace, 37 | ) -> Result; 38 | 39 | #[method(name = "sovereign_submitBlob")] 40 | async fn submit_blob( 41 | &self, 42 | blob: Vec, 43 | namespace: ikura_nmt::Namespace, 44 | ) -> Result<(), JsonRPCError>; 45 | } 46 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::{Cli, Commands}; 2 | use crate::key; 3 | use anyhow::Context as _; 4 | use clap::Parser; 5 | 6 | pub mod query; 7 | pub mod serve; 8 | 9 | pub async fn dispatch() -> anyhow::Result<()> { 10 | init_logging()?; 11 | let cli = Cli::parse(); 12 | match cli.command { 13 | Commands::Serve(params) => serve::run(params).await?, 14 | Commands::Simulate => { 15 | anyhow::bail!("simulate subcommand not yet implemented") 16 | } 17 | Commands::Query(params) => query::run(params).await?, 18 | } 19 | Ok(()) 20 | } 21 | 22 | fn init_logging() -> anyhow::Result<()> { 23 | use tracing_subscriber::fmt; 24 | use tracing_subscriber::prelude::*; 25 | let filter = tracing_subscriber::EnvFilter::builder() 26 | .with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into()) 27 | .from_env_lossy(); 28 | tracing_subscriber::registry() 29 | .with(filter) 30 | .with(fmt::layer().with_writer(std::io::stderr)) 31 | .try_init()?; 32 | Ok(()) 33 | } 34 | 35 | fn load_key(params: crate::cli::KeyManagementParams) -> anyhow::Result> { 36 | if params.submit_dev_alice { 37 | Ok(Some(key::alice())) 38 | } else if let Some(path) = params.submit_private_key { 39 | Ok(Some(key::load(path)?)) 40 | } else { 41 | Ok(None) 42 | } 43 | } 44 | 45 | /// Reads the namespace from a given namespace specifier and checks its validity against known schemas. 46 | /// 47 | /// The original namespace format is a 16-byte vector. so we support both the original format and 48 | /// a more human-readable format, which is an unsigned 128-bit integer. To distinguish between the 49 | /// two, the byte vector must be prefixed with `0x`. 50 | /// 51 | /// The integer is interpreted as big-endian. 52 | fn read_namespace(namespace: &str) -> anyhow::Result { 53 | let namespace = match namespace.strip_prefix("0x") { 54 | Some(hex) => { 55 | let namespace = hex::decode(hex)?; 56 | let namespace: [u8; 16] = namespace.try_into().map_err(|e: Vec| { 57 | anyhow::anyhow!("namespace must be 16 bytes long, but was {}", e.len()) 58 | })?; 59 | ikura_nmt::Namespace::from_raw_bytes(namespace) 60 | } 61 | None => { 62 | let namespace = namespace 63 | .parse::() 64 | .with_context(|| format!("cannot parse namespace id '{}'", namespace))?; 65 | ikura_nmt::Namespace::from_u128_be(namespace) 66 | } 67 | }; 68 | 69 | ikura_primitives::namespace::validate(&namespace.to_raw_bytes()) 70 | .map_err(|e| anyhow::anyhow!("cannot validate the namespace, {}", e))?; 71 | Ok(namespace) 72 | } 73 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/query/blob.rs: -------------------------------------------------------------------------------- 1 | use super::{connect_rpc, get_block_at}; 2 | use crate::cli::query::blob::Params; 3 | 4 | use std::io::Write; 5 | 6 | pub async fn run(params: Params) -> anyhow::Result<()> { 7 | let Params { 8 | rpc, 9 | block, 10 | index, 11 | raw, 12 | } = params; 13 | 14 | let client = connect_rpc(rpc).await?; 15 | let block = get_block_at(&client, block).await?; 16 | 17 | let i = block 18 | .blobs 19 | .binary_search_by_key(&index, |b| b.extrinsic_index) 20 | .map_err(|_| anyhow::anyhow!("No blob with extrinsic index {}", index))?; 21 | 22 | let blob = block.blobs.get(i).expect("verified to exist above; qed"); 23 | 24 | if raw { 25 | std::io::stdout().write_all(&blob.data)?; 26 | } else { 27 | println!( 28 | " Blob #{}, Namespace {}, {} bytes", 29 | i, 30 | &blob.namespace, 31 | blob.data.len() 32 | ); 33 | println!("{}", hex::encode(&blob.data)); 34 | } 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/query/block.rs: -------------------------------------------------------------------------------- 1 | use super::{connect_rpc, get_block_at}; 2 | use crate::cli::query::block::Params; 3 | 4 | pub async fn run(params: Params) -> anyhow::Result<()> { 5 | let Params { rpc, block, json } = params; 6 | 7 | let client = connect_rpc(rpc).await?; 8 | let block = get_block_at(&client, block).await?; 9 | 10 | if json { 11 | let json = serde_json::to_string_pretty(&block)?; 12 | println!("{}", json); 13 | } else { 14 | println!("Block: #{}", block.number); 15 | println!(" Hash: 0x{}", hex::encode(&block.hash[..])); 16 | println!(" Parent Hash: 0x{}", hex::encode(&block.parent_hash[..])); 17 | println!(" Blobs Root: 0x{}", hex::encode(&block.tree_root.root[..])); 18 | println!(" Min Namespace: {}", block.tree_root.min_ns); 19 | println!(" Max Namespace: {}", block.tree_root.max_ns); 20 | println!(" Timestamp: {}", block.timestamp); 21 | println!( 22 | " Blob Count: {} ({} bytes)", 23 | block.blobs.len(), 24 | block.blobs.iter().map(|b| b.data.len()).sum::(), 25 | ); 26 | for (i, blob) in block.blobs.into_iter().enumerate() { 27 | println!(" Blob #{}", i); 28 | println!(" Extrinsic Index: {}", blob.extrinsic_index); 29 | println!(" Namespace: {}", &blob.namespace); 30 | println!(" Size: {}", blob.data.len()); 31 | } 32 | } 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/query/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | cli::query::{BlockParams, BlockRef, Commands, Params}, 3 | ikura_rpc, 4 | }; 5 | 6 | mod blob; 7 | mod block; 8 | mod submit; 9 | 10 | pub async fn run(params: Params) -> anyhow::Result<()> { 11 | match params.command { 12 | Commands::Submit(params) => submit::run(params).await?, 13 | Commands::Block(params) => block::run(params).await?, 14 | Commands::Blob(params) => blob::run(params).await?, 15 | } 16 | Ok(()) 17 | } 18 | 19 | /// Given the BlockParams and the client to be used, try to fetch 20 | /// the corresponding block. It will wait until the block is avaiable if specified. 21 | pub async fn get_block_at( 22 | client: &ikura_rpc::Client, 23 | block: BlockParams, 24 | ) -> anyhow::Result { 25 | let maybe_hash = match block.block_ref { 26 | None | Some(BlockRef::Best) => None, 27 | Some(BlockRef::Hash(h)) => Some(h), 28 | Some(BlockRef::Number(n)) => Some(match block.wait { 29 | true => client.await_block_hash(n).await, 30 | false => client 31 | .block_hash(n) 32 | .await? 33 | .ok_or_else(|| anyhow::anyhow!("No block with number {}", n))?, 34 | }), 35 | }; 36 | 37 | match block.wait { 38 | true => client.await_block_at(maybe_hash).await, 39 | false => client.get_block_at(maybe_hash).await, 40 | } 41 | } 42 | 43 | async fn connect_rpc(conn_params: crate::cli::IkuraRpcParams) -> anyhow::Result { 44 | ikura_rpc::Client::new(conn_params.node_url, conn_params.no_retry).await 45 | } 46 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/query/submit.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | 3 | use super::connect_rpc; 4 | use crate::{cli::query::submit::Params, cmd::read_namespace}; 5 | 6 | pub async fn run(params: Params) -> anyhow::Result<()> { 7 | let Params { 8 | blob_path, 9 | namespace, 10 | rpc, 11 | key_management, 12 | } = params; 13 | let blob = read_blob(&blob_path) 14 | .with_context(|| format!("cannot read blob file path '{}'", blob_path))?; 15 | 16 | let key = crate::cmd::load_key(key_management) 17 | .with_context(|| format!("cannot load submission signing key"))? 18 | .ok_or_else(|| anyhow::anyhow!("submission signing key required. Specify the key with --submit-private-key or use the dev key with --submit-dev-alice"))?; 19 | 20 | let namespace = read_namespace(&namespace)?; 21 | let client = connect_rpc(rpc).await?; 22 | tracing::info!("submitting blob to namespace {}", namespace); 23 | let nonce = client.get_last_nonce(&key).await?; 24 | let blob_extrinsic = client 25 | .make_blob_extrinsic(blob, namespace, &key, nonce) 26 | .await?; 27 | let (block_hash, _) = client.submit_blob(&blob_extrinsic).await?; 28 | tracing::info!("submitted blob to block hash 0x{}", hex::encode(block_hash)); 29 | Ok(()) 30 | } 31 | 32 | /// Reads a blob from either a file or stdin. 33 | fn read_blob(path: &str) -> anyhow::Result> { 34 | use std::io::Read; 35 | let mut blob = Vec::new(); 36 | if path == "-" { 37 | tracing::debug!("reading blob contents from stdin"); 38 | std::io::stdin().read_to_end(&mut blob)?; 39 | } else { 40 | std::fs::File::open(path)?.read_to_end(&mut blob)?; 41 | } 42 | Ok(blob) 43 | } 44 | -------------------------------------------------------------------------------- /ikura/shim/src/cmd/serve.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | cli::serve::{self, Dock, Params}, 3 | cmd::read_namespace, 4 | dock, 5 | ikura_rpc::Client, 6 | }; 7 | use tracing::info; 8 | 9 | pub async fn run(Params { dock }: Params) -> anyhow::Result<()> { 10 | match dock { 11 | Dock::Sov(params) => run_sov(params).await, 12 | Dock::Rollkit(params) => run_rollkit(params).await, 13 | } 14 | } 15 | 16 | async fn connect_client(url: &str, no_retry: bool) -> anyhow::Result { 17 | let client = Client::new(url.to_string(), no_retry).await?; 18 | Ok(client) 19 | } 20 | 21 | fn load_submit_key( 22 | params: crate::cli::KeyManagementParams, 23 | ) -> Result, anyhow::Error> { 24 | let submit_key = crate::cmd::load_key(params)?; 25 | if submit_key.is_none() { 26 | tracing::info!( 27 | "no submit key provided, will not be able to submit blobs. \ 28 | Pass --submit-dev-alice or --submit-private-key=<..> to fix." 29 | ); 30 | } 31 | Ok(submit_key) 32 | } 33 | 34 | async fn run_sov(params: serve::sov::Params) -> anyhow::Result<()> { 35 | info!( 36 | "starting Sovereign SDK JSON-RPC ikura-shim server on {}:{}", 37 | params.dock.address, params.dock.port 38 | ); 39 | let submit_key = load_submit_key(params.key_management)?; 40 | let client = connect_client(¶ms.rpc.node_url, params.rpc.no_retry).await?; 41 | let config = dock::sovereign::Config { 42 | client, 43 | submit_key, 44 | address: params.dock.address, 45 | port: params.dock.port, 46 | }; 47 | dock::sovereign::run(config).await?; 48 | Ok(()) 49 | } 50 | 51 | async fn run_rollkit(params: serve::rollkit::Params) -> anyhow::Result<()> { 52 | info!( 53 | "starting Rollkit SDK gRPC ikura-shim server on {}:{}", 54 | params.dock.address, params.dock.port 55 | ); 56 | let submit_key = load_submit_key(params.key_management)?; 57 | let namespace = params.namespace.map(|ns| read_namespace(&ns)).transpose()?; 58 | if namespace.is_none() { 59 | tracing::info!("no namespace provided, will not be able to submit blobs"); 60 | } 61 | let client = connect_client(¶ms.rpc.node_url, params.rpc.no_retry).await?; 62 | let config = dock::rollkit::Config { 63 | client, 64 | submit_key, 65 | address: params.dock.address, 66 | port: params.dock.port, 67 | namespace, 68 | }; 69 | dock::rollkit::run(config).await?; 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /ikura/shim/src/dock/mod.rs: -------------------------------------------------------------------------------- 1 | //! A dock is a component that provides an ad-hoc API consumed by the corresponding adapter in the 2 | //! rollup. 3 | 4 | pub mod rollkit; 5 | mod rpc_error; 6 | pub mod sovereign; 7 | -------------------------------------------------------------------------------- /ikura/shim/src/dock/rpc_error.rs: -------------------------------------------------------------------------------- 1 | use jsonrpsee::types::error::ErrorObjectOwned; 2 | 3 | pub fn no_signing_key() -> ErrorObjectOwned { 4 | ErrorObjectOwned::owned( 5 | jsonrpsee::types::error::INTERNAL_ERROR_CODE, 6 | "Internal Error: no key for signing blobs", 7 | None::<()>, 8 | ) 9 | } 10 | 11 | pub fn nonce_obtain_error(e: anyhow::Error) -> ErrorObjectOwned { 12 | ErrorObjectOwned::owned( 13 | jsonrpsee::types::error::INTERNAL_ERROR_CODE, 14 | format!("Internal Error: failed to obtain nonce: {:?}", e), 15 | None::<()>, 16 | ) 17 | } 18 | 19 | pub fn submit_extrinsic_error(e: anyhow::Error) -> ErrorObjectOwned { 20 | ErrorObjectOwned::owned( 21 | jsonrpsee::types::error::INTERNAL_ERROR_CODE, 22 | format!( 23 | "Internal Error: failed to create a submit blob extrinsic: {:?}", 24 | e 25 | ), 26 | None::<()>, 27 | ) 28 | } 29 | 30 | pub fn submission_error(e: anyhow::Error) -> ErrorObjectOwned { 31 | ErrorObjectOwned::owned( 32 | jsonrpsee::types::error::INTERNAL_ERROR_CODE, 33 | format!("Internal Error: failed to submit blob: {:?}", e), 34 | None::<()>, 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /ikura/shim/src/key.rs: -------------------------------------------------------------------------------- 1 | //! Key management: sr25519 account key used for signing blob submission 2 | //! transactions. 3 | 4 | use std::path::Path; 5 | use subxt_signer::sr25519::Seed; 6 | 7 | pub use subxt_signer::sr25519::Keypair; 8 | 9 | /// Load a key from the provided file path. 10 | /// 11 | /// The file should contain a hex-encoded 32-byte seed used to generate 12 | /// the underlying schnorrkel private key. 13 | pub fn load>(path: P) -> anyhow::Result { 14 | let raw = hex::decode(std::fs::read(path)?)?; 15 | let mut seed: Seed = Seed::default(); 16 | if raw.len() != seed.len() { 17 | anyhow::bail!( 18 | "Keyfile length invalid, expected {} bytes, got {} bytes", 19 | seed.len(), 20 | raw.len() 21 | ); 22 | } 23 | seed.copy_from_slice(&raw[..]); 24 | Ok(Keypair::from_seed(seed)?) 25 | } 26 | 27 | /// The default dev key. 28 | pub fn alice() -> Keypair { 29 | subxt_signer::sr25519::dev::alice() 30 | } 31 | 32 | #[test] 33 | fn load_alice_key() { 34 | use std::fs; 35 | use temp_dir::TempDir; 36 | // `subkey inspect //Alice`: 37 | const ALICE_SECRET_SEED_HEX: &str = 38 | "e5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"; 39 | let dir = TempDir::new().unwrap(); 40 | let alice_key = dir.child("alice.key"); 41 | fs::write(&alice_key, ALICE_SECRET_SEED_HEX).unwrap(); 42 | let actual_alice_pubk = load(&alice_key).unwrap().public_key(); 43 | let expected_alice_pubk = alice().public_key(); 44 | assert_eq!( 45 | hex::encode(actual_alice_pubk.as_ref()), 46 | hex::encode(expected_alice_pubk.as_ref()), 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /ikura/shim/src/main.rs: -------------------------------------------------------------------------------- 1 | mod cli; 2 | mod cmd; 3 | mod dock; 4 | mod ikura_rpc; 5 | mod key; 6 | 7 | #[tokio::main] 8 | async fn main() -> anyhow::Result<()> { 9 | cmd::dispatch().await 10 | } 11 | -------------------------------------------------------------------------------- /ikura/subxt-autogen/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /ikura/subxt-autogen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ikura-subxt" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | subxt = { workspace = true, features = ["jsonrpsee"] } 14 | parity-scale-codec = { workspace = true, default-features = true } 15 | anyhow = { workspace = true } 16 | -------------------------------------------------------------------------------- /ikura/subxt-autogen/README.md: -------------------------------------------------------------------------------- 1 | # ikura-subxt 2 | 3 | ## Update 4 | 5 | This repo includes the metadata for the Ikura chain. You can update the metadata by running the 6 | following command: 7 | 8 | ./gen.sh 9 | 10 | Make sure a local ikura node is accessible at ws://localhost:9988/ and rustfmt is installed. 11 | -------------------------------------------------------------------------------- /ikura/subxt-autogen/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | subxt codegen --url ws://localhost:9988/ | rustfmt --edition=2021 --emit=stdout > src/gen.rs 4 | -------------------------------------------------------------------------------- /ikura/subxt-autogen/src/lib.rs: -------------------------------------------------------------------------------- 1 | use subxt::OnlineClient; 2 | 3 | mod gen; 4 | 5 | pub mod ikura { 6 | pub use super::gen::api::*; 7 | } 8 | 9 | pub type IkuraConfig = subxt::SubstrateConfig; 10 | 11 | pub type Client = OnlineClient; 12 | 13 | pub type Header = ::Header; 14 | 15 | pub type ExtrinsicDetails = subxt::blocks::ExtrinsicDetails; 16 | 17 | // #[derive(Clone, Debug, Default)] 18 | // pub struct IkuraConfig; 19 | 20 | // impl subxt::Config for IkuraConfig { 21 | // type AccountId = ikura_runtime::AccountId; 22 | // type Address = ikura_runtime::Address; 23 | // // type ExtrinsicParams = ikura_runtime::AvailExtrinsicParams; 24 | // type Hash = ikura_runtime::H256; 25 | // type Hasher = sp_runtime::traits::BlakeTwo256; 26 | // type Header = ikura_runtime::Header; 27 | // type Index = ikura_runtime::Index; 28 | // type Signature = ikura_runtime::Signature; 29 | // } 30 | 31 | /// Creates a client and validate the code generation if `validate_codegen == true`. 32 | pub async fn build_client>( 33 | url: impl AsRef, 34 | validate_codegen: bool, 35 | ) -> anyhow::Result { 36 | let api = Client::from_url(url).await?; 37 | if validate_codegen && !ikura::is_codegen_valid_for(&api.metadata()) { 38 | anyhow::bail!("Client metadata not valid") 39 | } 40 | Ok(api) 41 | } 42 | -------------------------------------------------------------------------------- /testnet.toml: -------------------------------------------------------------------------------- 1 | [settings] 2 | timeout = 1000 3 | 4 | [relaychain] 5 | command = "polkadot" 6 | chain = "rococo-local" 7 | 8 | [[relaychain.nodes]] 9 | name = "alice" 10 | ws_port = 9944 11 | rpc_port = 30444 12 | 13 | [[relaychain.nodes]] 14 | name = "bob" 15 | ws_port = 9955 16 | rpc_port = 30555 17 | 18 | [[parachains]] 19 | id = 100 20 | 21 | [parachains.collator] 22 | name = "collator01" 23 | command = "ikura-node" 24 | ws_port = 9988 25 | rpc_port = 31200 26 | args = ["--state-pruning=archive"] 27 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | edition.workspace = true 8 | license.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | xshell = { workspace = true } 14 | nix = { workspace = true, features = ["signal", "process"] } 15 | tokio = { workspace = true, features = ["rt", "macros", "rt-multi-thread", "time", "process", "sync", "signal"] } 16 | clap = { workspace = true, features = ["derive"] } 17 | anyhow = { workspace = true } 18 | tracing = { workspace = true } 19 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 20 | serde_json = { workspace = true } 21 | -------------------------------------------------------------------------------- /xtask/src/build.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | cli::BuildParams, 3 | logging::{create_log_file, WithLogs}, 4 | }; 5 | use xshell::cmd; 6 | 7 | // TODO: https://github.com/thrumdev/blobs/issues/225 8 | 9 | pub async fn build(project_path: &std::path::Path, params: BuildParams) -> anyhow::Result<()> { 10 | if params.skip { 11 | return Ok(()); 12 | } 13 | 14 | let sh = xshell::Shell::new()?; 15 | 16 | tracing::info!("Building logs redirected {}", params.log_path); 17 | let log_path = create_log_file(project_path, ¶ms.log_path); 18 | 19 | // `it is advisable to use CARGO environmental variable to get the right cargo` 20 | // quoted by xtask readme 21 | let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 22 | 23 | cmd!(sh, "{cargo} build -p ikura-node --release") 24 | .run_with_logs("Building ikura-node", &log_path) 25 | .await??; 26 | 27 | cmd!(sh, "{cargo} build -p ikura-shim --release") 28 | .run_with_logs("Building ikura-shim", &log_path) 29 | .await??; 30 | 31 | sh.change_dir(project_path.join("demo/sovereign/demo-rollup/")); 32 | cmd!(sh, "{cargo} build --release") 33 | .run_with_logs("Building sovereign demo-rollup", &log_path) 34 | .await??; 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /xtask/src/shim.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | check_binary, 3 | cli::test::ShimParams, 4 | logging::{create_log_file, WithLogs}, 5 | }; 6 | 7 | pub struct Shim(tokio::process::Child); 8 | 9 | impl Shim { 10 | // Try launching the shim, it requires an up an running ikura-node 11 | pub async fn try_new( 12 | project_path: &std::path::Path, 13 | params: ShimParams, 14 | ) -> anyhow::Result { 15 | check_binary( 16 | "ikura-shim", 17 | "'ikura-shim' is not found in PATH. \n \ 18 | cd to 'ikura/shim' and run 'cargo build --release' and add the result into your PATH.", 19 | )?; 20 | 21 | tracing::info!("Shim logs redirected to {}", params.log_path); 22 | let log_path = create_log_file(project_path, ¶ms.log_path); 23 | 24 | let sh = xshell::Shell::new()?; 25 | 26 | // Wait for the shim to be connected, which indicates that the network is ready 27 | xshell::cmd!(sh, "ikura-shim query block --wait 1") 28 | .run_with_logs("Wait for the network to be ready", &log_path) 29 | .await??; 30 | 31 | let shim_process = xshell::cmd!(sh, "ikura-shim serve sov --submit-dev-alice") 32 | .spawn_with_logs("Launching Shim", &log_path)?; 33 | 34 | Ok(Self(shim_process)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /xtask/src/zombienet.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | check_binary, 3 | cli::ZombienetParams, 4 | logging::{create_log_file, WithLogs}, 5 | ProcessGroupId, 6 | }; 7 | use std::path::Path; 8 | use tracing::info; 9 | use xshell::cmd; 10 | 11 | pub struct Zombienet(tokio::process::Child); 12 | 13 | impl Zombienet { 14 | // Try launching the network using zombienet 15 | // 16 | // The binaries for zombienet and polkadot are expected to be in the PATH, 17 | // while polkadot-execute-worker and polkadot-prepare-worker 18 | // need to be in the same directory as the polkadot binary. 19 | pub async fn try_new(project_path: &Path, params: ZombienetParams) -> anyhow::Result { 20 | let sh = xshell::Shell::new()?; 21 | 22 | info!("Deleting the zombienet folder if it already exists"); 23 | let zombienet_folder = project_path.join("zombienet"); 24 | if zombienet_folder.as_path().exists() { 25 | cmd!(sh, "rm -r {zombienet_folder}") 26 | .quiet() 27 | .ignore_status() 28 | .run()?; 29 | } 30 | 31 | info!("Checking binaries availability"); 32 | check_binary( 33 | "zombienet", 34 | "'zombienet' is not found in PATH. Install zombienet. \n \ 35 | Available at https://github.com/paritytech/zombienet", 36 | )?; 37 | 38 | check_binary( 39 | "polkadot", 40 | "'polkadot' is not found in PATH. Install polkadot \n \ 41 | To obtain, refer to https://github.com/paritytech/polkadot-sdk/tree/master/polkadot#polkadot" 42 | )?; 43 | 44 | check_binary( 45 | "ikura-node", 46 | "'ikura-node' is not found in PATH. \n \ 47 | cd to 'ikura/chain' and run 'cargo build --release' and add the result into your PATH." 48 | )?; 49 | 50 | tracing::info!("Zombienet logs redirected to {}", params.log_path); 51 | let log_path = create_log_file(project_path, ¶ms.log_path); 52 | 53 | sh.change_dir(project_path); 54 | 55 | let zombienet_process = cmd!(sh, "zombienet spawn -p native --dir zombienet testnet.toml") 56 | .process_group(0) 57 | .spawn_with_logs("Launching zombienet", &log_path)?; 58 | 59 | Ok(Self(zombienet_process)) 60 | } 61 | } 62 | 63 | impl Drop for Zombienet { 64 | fn drop(&mut self) { 65 | use nix::{sys::signal, unistd::Pid}; 66 | let Some(id) = self.0.id() else { return }; 67 | signal::killpg(Pid::from_raw(id as i32), Some(signal::Signal::SIGKILL)) 68 | .expect("Failed kill zombienet process"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /zombienet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if the zombienet binary is available. 4 | if ! [ -x "$(command -v zombienet)" ]; then 5 | echo "\ 6 | zombienet is not found in PATH. Install zombienet. 7 | 8 | Available at https://github.com/paritytech/zombienet" 9 | exit 1 10 | fi 11 | 12 | if ! [ -x "$(command -v polkadot)" ]; then 13 | echo "\ 14 | 'polkadot' is not found in PATH. Install polkadot. 15 | 16 | To obtain, refer to https://github.com/paritytech/polkadot-sdk/tree/master/polkadot#polkadot" 17 | exit 1 18 | fi 19 | 20 | if ! [ -x "$(command -v ikura-node)" ]; then 21 | echo "\ 22 | 'ikura-node' is not found in PATH. cd to 'ikura/chain' and run 'cargo build --release' 23 | and add the result into your PATH." 24 | exit 1 25 | fi 26 | 27 | zombienet spawn -p native --dir zombienet testnet.toml 28 | --------------------------------------------------------------------------------