From 75a3dd7b8657c825be23202ef45d01acafaf4d98 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 4 Apr 2023 16:48:53 +0200 Subject: [PATCH 01/28] feat: basic DIP structure (#489) Fixes https://github.com/KILTprotocol/ticket/issues/2550. **This PR will merge the changes into the `aa/dip` branch, which will be the base branch for all the future DIP improvements until we reach a version that we can merge into `develop` and deploy on some testnets in the Peregrine runtime.** ## Base structure for DIP This PR contains the following components: * A pallet to send identity information to one or more destinations (DIP sender) * A pallet to receive such identity information from a source (DIP receiver) * A support crate that contains common (versioned) types, in future also traits and auxiliary functions, if needed * A template project consisting of 1. a template runtime for the dip-sender, with its node counterpart, 2. a template runtime for the dip-receiver, with its node counterpart, 3. an example XCM integration test using [XCM emulator](https://github.com/shaunxw/xcm-simulator) (not simulator) which connects the two template runtimes with a local Rococo relay chain. ### Design The current version assumes that anyone on the sender chain can dispatch identity information on some target chain (with which there exists an open HRMP channel). Ultimately, all the fees for dispatching the message and process it on the receiver side will be paid by the extrinsic submitter on the target chain. Right now, the submitter only pays for the extrinsic dispatch on the source chain, and the chain itself uses its own balance (liked to its sovereign account) on the target chain to pay for the `Transact` execution. The `Transact` operation that interacts with the `dip-receiver` pallet on the target chain has an `OriginKind::Native`, so that there is no way for any user on the source chain to dispatch such messages. Right now, the basic structure and the templates use defaults for everything, from proof generation to proof verification. One step at the time, all these defaults will be replaced with the actual features. Nevertheless, for two chains that have the same concept of `Identifier` and `AccountId`, it is already possible to demo the complete e2e flow. For the sake of our demonstration, the flow would go as follow, **assuming that both the source and target chains are registered as parachain on the same relaychain**: 1. The source chain requests to open an HRMP channel with the target chain 2. The target chain accepts the request and the channel is open 3. The target chain issues some funds (enough to cover the demo use case) to the sender chain's sovereign account 4. A user on the source chain creates a DID with a single authentication key 5. The same user (but could be anyone else), calls the extrinsic on the `dip-sender` pallet to send the merkle root of the DID to the target chain, paying for the dispatch fee 6. The source chain sends the information to the target chain 7. The user calls the `dispatch_as` extrinsic of the `dip-receiver` pallet which is deployed on the target chain, providing a merkle proof of their DID document + details on the source chain. For now this check always returns true. 8. Inside, the `dispatch_as` has a nested call which is dispatched with the new `DipOrigin`, which pallets can now use if they expect extrinsics to be DID-authorized. ## What's coming I created tickets for each of the next steps, under the DIP milestone. The tickets will take care of: * Adding benchmarks and weights * Introducing proper error handling * Introducing proper DID signature verificaiton on the target template chain (right now it's just a default behaviour) * Introducing proper merkle proof generation (I tested that it works, but removed those pieces from this PR for the ease of understanding) * Introducing proper merkle proof verification (same as above) * Adding an emergency brake in the sender pallet to stop dispatching messages to a specific destination, e.g., if the sender sovereign account on the destination chain does not have any more or if the `Transact` encoding would fail * Adding support for metadata beyond the simple DID details, e.g., proof expiration time, etc. * Replacing user-provided weights and assets with per-chain configurations stored in the sender pallet * More comments (I can work on this first if the code as it is it's too hard to follow) * Adding support for version negotiation. There is a basic concept of versioning right now, but it can and should be expanded and improved. * More unit and integration tests. Probably more tickets will come out of this work, and will be tracked separately in the same or in future milestones. ## How to test A basic integration test of the two template runtimes is present in the `dip-templates-xcm-tests` crate, inside the `dip-template/runtimes/xcm-tests`. It can be run as usual with `cargo test -p dip-templates-xcm-tests`. --- .gitignore | 3 - Cargo.lock | 303 +++++++++- Cargo.toml | 16 +- crates/assets/Cargo.toml | 2 +- crates/dip/Cargo.toml | 29 + crates/dip/src/lib.rs | 57 ++ crates/dip/src/v1.rs | 31 + dip-template/nodes/dip-receiver/Cargo.toml | 67 +++ dip-template/nodes/dip-receiver/build.rs | 25 + .../nodes/dip-receiver/src/chain_spec.rs | 144 +++++ dip-template/nodes/dip-receiver/src/cli.rs | 89 +++ .../nodes/dip-receiver/src/command.rs | 400 +++++++++++++ dip-template/nodes/dip-receiver/src/main.rs | 32 ++ dip-template/nodes/dip-receiver/src/rpc.rs | 70 +++ .../nodes/dip-receiver/src/service.rs | 412 ++++++++++++++ dip-template/nodes/dip-sender/Cargo.toml | 67 +++ dip-template/nodes/dip-sender/build.rs | 25 + .../nodes/dip-sender/src/chain_spec.rs | 143 +++++ dip-template/nodes/dip-sender/src/cli.rs | 89 +++ dip-template/nodes/dip-sender/src/command.rs | 400 +++++++++++++ dip-template/nodes/dip-sender/src/main.rs | 32 ++ dip-template/nodes/dip-sender/src/rpc.rs | 70 +++ dip-template/nodes/dip-sender/src/service.rs | 412 ++++++++++++++ dip-template/runtimes/dip-receiver/Cargo.toml | 123 ++++ dip-template/runtimes/dip-receiver/build.rs | 27 + dip-template/runtimes/dip-receiver/src/dip.rs | 36 ++ dip-template/runtimes/dip-receiver/src/lib.rs | 501 +++++++++++++++++ .../runtimes/dip-receiver/src/xcm_config.rs | 149 +++++ dip-template/runtimes/dip-sender/Cargo.toml | 123 ++++ dip-template/runtimes/dip-sender/build.rs | 27 + dip-template/runtimes/dip-sender/src/dip.rs | 70 +++ dip-template/runtimes/dip-sender/src/lib.rs | 528 ++++++++++++++++++ .../runtimes/dip-sender/src/xcm_config.rs | 130 +++++ dip-template/runtimes/xcm-tests/Cargo.toml | 38 ++ dip-template/runtimes/xcm-tests/src/lib.rs | 59 ++ dip-template/runtimes/xcm-tests/src/para.rs | 107 ++++ dip-template/runtimes/xcm-tests/src/relay.rs | 91 +++ dip-template/runtimes/xcm-tests/src/tests.rs | 75 +++ nodes/parachain/Cargo.toml | 1 - pallets/attestation/Cargo.toml | 2 +- pallets/ctype/Cargo.toml | 2 +- pallets/delegation/Cargo.toml | 2 +- pallets/did/Cargo.toml | 4 +- pallets/pallet-did-lookup/Cargo.toml | 4 +- pallets/pallet-dip-receiver/Cargo.toml | 41 ++ pallets/pallet-dip-receiver/src/lib.rs | 143 +++++ pallets/pallet-dip-receiver/src/origin.rs | 79 +++ pallets/pallet-dip-receiver/src/traits.rs | 52 ++ pallets/pallet-dip-sender/Cargo.toml | 39 ++ pallets/pallet-dip-sender/src/lib.rs | 129 +++++ pallets/pallet-dip-sender/src/traits.rs | 192 +++++++ pallets/pallet-inflation/Cargo.toml | 2 +- pallets/pallet-web3-names/Cargo.toml | 2 +- pallets/parachain-staking/Cargo.toml | 2 +- runtimes/common/Cargo.toml | 2 +- runtimes/peregrine/Cargo.toml | 2 +- runtimes/spiritnet/Cargo.toml | 2 +- runtimes/standalone/Cargo.toml | 2 +- support/Cargo.toml | 2 +- 59 files changed, 5684 insertions(+), 24 deletions(-) create mode 100644 crates/dip/Cargo.toml create mode 100644 crates/dip/src/lib.rs create mode 100644 crates/dip/src/v1.rs create mode 100644 dip-template/nodes/dip-receiver/Cargo.toml create mode 100644 dip-template/nodes/dip-receiver/build.rs create mode 100644 dip-template/nodes/dip-receiver/src/chain_spec.rs create mode 100644 dip-template/nodes/dip-receiver/src/cli.rs create mode 100644 dip-template/nodes/dip-receiver/src/command.rs create mode 100644 dip-template/nodes/dip-receiver/src/main.rs create mode 100644 dip-template/nodes/dip-receiver/src/rpc.rs create mode 100644 dip-template/nodes/dip-receiver/src/service.rs create mode 100644 dip-template/nodes/dip-sender/Cargo.toml create mode 100644 dip-template/nodes/dip-sender/build.rs create mode 100644 dip-template/nodes/dip-sender/src/chain_spec.rs create mode 100644 dip-template/nodes/dip-sender/src/cli.rs create mode 100644 dip-template/nodes/dip-sender/src/command.rs create mode 100644 dip-template/nodes/dip-sender/src/main.rs create mode 100644 dip-template/nodes/dip-sender/src/rpc.rs create mode 100644 dip-template/nodes/dip-sender/src/service.rs create mode 100644 dip-template/runtimes/dip-receiver/Cargo.toml create mode 100644 dip-template/runtimes/dip-receiver/build.rs create mode 100644 dip-template/runtimes/dip-receiver/src/dip.rs create mode 100644 dip-template/runtimes/dip-receiver/src/lib.rs create mode 100644 dip-template/runtimes/dip-receiver/src/xcm_config.rs create mode 100644 dip-template/runtimes/dip-sender/Cargo.toml create mode 100644 dip-template/runtimes/dip-sender/build.rs create mode 100644 dip-template/runtimes/dip-sender/src/dip.rs create mode 100644 dip-template/runtimes/dip-sender/src/lib.rs create mode 100644 dip-template/runtimes/dip-sender/src/xcm_config.rs create mode 100644 dip-template/runtimes/xcm-tests/Cargo.toml create mode 100644 dip-template/runtimes/xcm-tests/src/lib.rs create mode 100644 dip-template/runtimes/xcm-tests/src/para.rs create mode 100644 dip-template/runtimes/xcm-tests/src/relay.rs create mode 100644 dip-template/runtimes/xcm-tests/src/tests.rs create mode 100644 pallets/pallet-dip-receiver/Cargo.toml create mode 100644 pallets/pallet-dip-receiver/src/lib.rs create mode 100644 pallets/pallet-dip-receiver/src/origin.rs create mode 100644 pallets/pallet-dip-receiver/src/traits.rs create mode 100644 pallets/pallet-dip-sender/Cargo.toml create mode 100644 pallets/pallet-dip-sender/src/lib.rs create mode 100644 pallets/pallet-dip-sender/src/traits.rs diff --git a/.gitignore b/.gitignore index 058330f49e..6892a1980e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,4 @@ # ignore vscode settings .vscode -# Custom Polkadot-js apps types -pallets/did/src/custom_types.json - **/__pycache__ diff --git a/Cargo.lock b/Cargo.lock index ea8941a93e..4ea5562ee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2279,6 +2279,236 @@ dependencies = [ "subtle", ] +[[package]] +name = "dip-receiver-node-template" +version = "1.11.0-dev" +dependencies = [ + "clap", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dip-receiver-runtime-template", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-rpc-api", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "dip-receiver-runtime-template" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-did-lookup", + "pallet-dip-receiver", + "pallet-session", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "dip-sender-node-template" +version = "1.11.0-dev" +dependencies = [ + "clap", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dip-sender-runtime-template", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-rpc-api", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "dip-sender-runtime-template" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "did", + "dip-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-dip-sender", + "pallet-session", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "dip-support" +version = "1.11.0-dev" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "dip-templates-xmc-tests" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-xcmp-queue", + "dip-receiver-runtime-template", + "dip-sender-runtime-template", + "frame-support", + "frame-system", + "pallet-balances", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rococo-runtime", + "scale-info", + "sp-io", + "sp-runtime", + "xcm", + "xcm-emulator", + "xcm-executor", +] + [[package]] name = "directories" version = "4.0.1" @@ -3977,7 +4207,6 @@ dependencies = [ "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", - "cumulus-client-network", "cumulus-client-service", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", @@ -5894,6 +6123,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-collator-selection" +version = "3.0.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.39#d6eef144421ef5c3f339f681484d06bb729dfa82" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", +] + [[package]] name = "pallet-collective" version = "4.0.0-dev" @@ -5973,6 +6221,33 @@ dependencies = [ "test-log", ] +[[package]] +name = "pallet-dip-receiver" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-xcm", + "dip-support", + "frame-support", + "frame-system", + "kilt-support", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "pallet-dip-sender" +version = "1.11.0-dev" +dependencies = [ + "dip-support", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-std", + "xcm", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" @@ -13787,6 +14062,32 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "xcm-emulator" +version = "0.1.0" +source = "git+https://github.com/shaunxw/xcm-simulator?branch=master#aa13dce47596e150806dfc3af99096dae6ffc65e" +dependencies = [ + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "parachain-info", + "parity-scale-codec", + "paste", + "polkadot-primitives", + "polkadot-runtime-parachains", + "quote", + "sp-arithmetic", + "sp-io", + "sp-std", + "xcm", + "xcm-executor", +] + [[package]] name = "xcm-executor" version = "0.9.39-1" diff --git a/Cargo.toml b/Cargo.toml index a08152f9c2..95442c91b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,14 @@ version = "1.11.0-dev" [workspace] members = [ + "crates/*", + "dip-template/nodes/*", + "dip-template/runtimes/*", "nodes/*", "pallets/*", "runtime-api/*", "runtimes/*", "support", - "crates/*", ] [profile.release] @@ -30,7 +32,6 @@ substrate-wasm-builder = {git = "https://github.com/paritytech/substrate", branc base58 = {version = "0.2.0", default-features = false} bitflags = {version = "1.3.2", default-features = false} clap = "4.1.6" -parity-scale-codec = {version = "3.1.5", default-features = false} env_logger = "0.10.0" futures = {version = "0.3.21", default-features = false} hex = {version = "0.4.0", default-features = false} @@ -38,6 +39,7 @@ hex-literal = "0.3.4" jsonrpsee = "0.16.2" libsecp256k1 = {version = "0.7", default-features = false} log = "0.4.17" +parity-scale-codec = {version = "3.1.5", default-features = false} scale-info = {version = "2.1.1", default-features = false} serde = "1.0.144" serde_json = "1.0.85" @@ -49,6 +51,8 @@ attestation = {path = "pallets/attestation", default-features = false} ctype = {path = "pallets/ctype", default-features = false} delegation = {path = "pallets/delegation", default-features = false} did = {path = "pallets/did", default-features = false} +pallet-dip-receiver = {path = "pallets/pallet-dip-receiver", default-features = false} +pallet-dip-sender = {path = "pallets/pallet-dip-sender", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "pallets/pallet-inflation", default-features = false} pallet-web3-names = {path = "pallets/pallet-web3-names", default-features = false} @@ -56,10 +60,15 @@ parachain-staking = {path = "pallets/parachain-staking", default-features = fals public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) +dip-support = {path = "crates/dip", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} +# Templates +dip-receiver-runtime-template = {path = "dip-template/runtimes/dip-receiver", default-features = false} +dip-sender-runtime-template = {path = "dip-template/runtimes/dip-sender", default-features = false} + # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} kilt-runtime-api-public-credentials = {path = "runtime-api/public-credentials", default-features = false} @@ -134,6 +143,8 @@ try-runtime-cli = {git = "https://github.com/paritytech/substrate", default-feat # Polkadot (with default disabled) pallet-xcm = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} polkadot-parachain = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} +rococo-runtime = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} +polkadot-runtime-parachains = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} xcm = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} xcm-builder = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} xcm-executor = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.39"} @@ -142,7 +153,6 @@ xcm-executor = {git = "https://github.com/paritytech/polkadot", default-features cumulus-client-cli = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} cumulus-client-consensus-aura = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} cumulus-client-consensus-common = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} -cumulus-client-network = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} cumulus-client-service = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} cumulus-primitives-parachain-inherent = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} cumulus-relay-chain-inprocess-interface = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39"} diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml index c3bd616dc7..7b4e60c486 100644 --- a/crates/assets/Cargo.toml +++ b/crates/assets/Cargo.toml @@ -29,9 +29,9 @@ sp-std.workspace = true [features] default = ["std"] std = [ - "parity-scale-codec/std", "hex/std", "log/std", + "parity-scale-codec/std", "scale-info/std", "frame-support/std", "sp-core/std", diff --git a/crates/dip/Cargo.toml b/crates/dip/Cargo.toml new file mode 100644 index 0000000000..0b0a7af1e1 --- /dev/null +++ b/crates/dip/Cargo.toml @@ -0,0 +1,29 @@ +[package] +authors.workspace = true +description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-support" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +# Parity dependencies +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true + +# Substrate dependencies +frame-support.workspace = true + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "sp-std/std", + "frame-support/std" +] diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs new file mode 100644 index 0000000000..f681976fac --- /dev/null +++ b/crates/dip/src/lib.rs @@ -0,0 +1,57 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Crate documentation + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::RuntimeDebug; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +// Export v1 behind a namespace and also as the latest +pub mod v1; +pub mod latest { + pub use crate::v1::*; +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum VersionedIdentityProofAction { + #[codec(index = 1)] + V1(v1::IdentityProofAction), +} + +impl From> + for VersionedIdentityProofAction +{ + fn from(value: v1::IdentityProofAction) -> Self { + Self::V1(value) + } +} + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] +pub enum VersionedIdentityProof { + #[codec(index = 1)] + V1(v1::Proof), +} + +impl From> for VersionedIdentityProof { + fn from(value: v1::Proof) -> Self { + Self::V1(value) + } +} diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs new file mode 100644 index 0000000000..f72e02881f --- /dev/null +++ b/crates/dip/src/v1.rs @@ -0,0 +1,31 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::RuntimeDebug; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum IdentityProofAction { + Updated(Identifier, Proof, Details), + Deleted(Identifier), +} + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] +pub struct Proof(Vec<(LeafKey, LeafValue)>); diff --git a/dip-template/nodes/dip-receiver/Cargo.toml b/dip-template/nodes/dip-receiver/Cargo.toml new file mode 100644 index 0000000000..1f217b7d01 --- /dev/null +++ b/dip-template/nodes/dip-receiver/Cargo.toml @@ -0,0 +1,67 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "dip-receiver-node-template" +description = "Node template for the KILT Decentralized Identity Provider (DIP) receiver." +build = "build.rs" + +[dependencies] +clap = {workspace = true, features = ["std", "derive"]} +log = { workspace = true, features = ["std"] } +parity-scale-codec = { workspace = true, features = ["std"] } +serde = {workspace = true, features = ["std", "derive"]} +jsonrpsee = {workspace = true, features = ["server"]} + +# Internal dependencies +dip-receiver-runtime-template = { workspace = true, features = ["std"] } + +# Substrate +frame-benchmarking.workspace = true +frame-benchmarking-cli.workspace = true +pallet-transaction-payment-rpc.workspace = true +sc-basic-authorship.workspace = true +sc-chain-spec.workspace = true +sc-cli.workspace = true +sc-client-api.workspace = true +sc-consensus.workspace = true +sc-executor.workspace = true +sc-network.workspace = true +sc-rpc-api.workspace = true +sc-service.workspace = true +sc-sysinfo.workspace = true +sc-telemetry.workspace = true +sc-tracing.workspace = true +sc-transaction-pool.workspace = true +sc-transaction-pool-api.workspace = true +sp-api = { workspace = true, features = ["std"] } +sp-block-builder = { workspace = true, features = ["std"] } +sp-blockchain.workspace = true +sp-consensus-aura = { workspace = true, features = ["std"] } +sp-core = { workspace = true, features = ["std"] } +sp-keystore = { workspace = true, features = ["std"] } +sp-runtime = { workspace = true, features = ["std"] } +sp-timestamp = { workspace = true, features = ["std"] } +substrate-frame-rpc-system.workspace = true +substrate-prometheus-endpoint.workspace = true + +# Polkadot +polkadot-cli.workspace = true +polkadot-primitives = { workspace = true, features = ["std"] } + +# Cumulus +cumulus-client-cli.workspace = true +cumulus-client-consensus-aura.workspace = true +cumulus-client-consensus-common.workspace = true +cumulus-client-service.workspace = true +cumulus-primitives-core = { workspace = true, features = ["std"] } +cumulus-primitives-parachain-inherent = { workspace = true, features = ["std"] } +cumulus-relay-chain-interface.workspace = true + +[build-dependencies] +substrate-build-script-utils.workspace = true diff --git a/dip-template/nodes/dip-receiver/build.rs b/dip-template/nodes/dip-receiver/build.rs new file mode 100644 index 0000000000..961231c478 --- /dev/null +++ b/dip-template/nodes/dip-receiver/build.rs @@ -0,0 +1,25 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/dip-template/nodes/dip-receiver/src/chain_spec.rs b/dip-template/nodes/dip-receiver/src/chain_spec.rs new file mode 100644 index 0000000000..50daa7a567 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/chain_spec.rs @@ -0,0 +1,144 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_primitives_core::ParaId; +use dip_receiver_runtime_template::{ + AccountId, AuraId, BalancesConfig, CollatorSelectionConfig, GenesisConfig, ParachainInfoConfig, SessionConfig, + SessionKeys, Signature, SudoConfig, SystemConfig, EXISTENTIAL_DEPOSIT, SS58_PREFIX, WASM_BINARY, +}; +use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, Properties}; +use sc_service::{ChainType, GenericChainSpec}; +use serde::{Deserialize, Serialize}; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +const PARA_ID: u32 = 2_001; + +pub type ChainSpec = GenericChainSpec; +type AccountPublic = ::Signer; + +pub(crate) fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + pub relay_chain: String, + pub para_id: u32, +} + +impl Extensions { + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { + get_from_seed::(seed) +} + +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +pub fn template_session_keys(keys: AuraId) -> SessionKeys { + SessionKeys { aura: keys } +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + parachain_system: Default::default(), + parachain_info: ParachainInfoConfig { parachain_id: id }, + sudo: SudoConfig { + key: Some(endowed_accounts.first().unwrap().clone()), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + transaction_payment: Default::default(), + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: EXISTENTIAL_DEPOSIT * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| (acc.clone(), acc, template_session_keys(aura))) + .collect(), + }, + aura: Default::default(), + aura_ext: Default::default(), + polkadot_xcm: Default::default(), + did_lookup: Default::default(), + } +} + +pub fn development_config() -> ChainSpec { + let mut properties = Properties::new(); + properties.insert("tokenSymbol".into(), "REILT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), SS58_PREFIX.into()); + + ChainSpec::from_genesis( + "DIP receiver dev", + "dip-receiver-dev", + ChainType::Development, + move || { + testnet_genesis( + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Charlie//stash"), + ], + PARA_ID.into(), + ) + }, + Vec::new(), + None, + "dip-receiver-dev".into(), + None, + None, + Extensions { + relay_chain: "rococo-local".into(), + para_id: PARA_ID, + }, + ) +} diff --git a/dip-template/nodes/dip-receiver/src/cli.rs b/dip-template/nodes/dip-receiver/src/cli.rs new file mode 100644 index 0000000000..3cec4a2550 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/cli.rs @@ -0,0 +1,89 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::path::PathBuf; + +use cumulus_client_cli::{ExportGenesisStateCommand, ExportGenesisWasmCommand, PurgeChainCmd}; +use polkadot_cli::RunCmd; +use sc_cli::{BuildSpecCmd, CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; +use sc_service::Configuration; + +use crate::chain_spec::Extensions; + +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + BuildSpec(BuildSpecCmd), + + CheckBlock(CheckBlockCmd), + + ExportBlocks(ExportBlocksCmd), + + ExportState(ExportStateCmd), + + ImportBlocks(ImportBlocksCmd), + + Revert(RevertCmd), + + PurgeChain(PurgeChainCmd), + + ExportGenesisState(ExportGenesisStateCommand), + + ExportGenesisWasm(ExportGenesisWasmCommand), +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: cumulus_client_cli::RunCmd, + + #[arg(long)] + pub no_hardware_benchmarks: bool, + + #[arg(raw = true)] + pub relay_chain_args: Vec, +} + +#[derive(Debug)] +pub struct RelayChainCli { + pub base: RunCmd, + + pub chain_id: Option, + + pub base_path: Option, +} + +impl RelayChainCli { + pub fn new<'a>(para_config: &Configuration, relay_chain_args: impl Iterator) -> Self { + let extension = Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.as_ref().map(|x| x.path().join("polkadot")); + Self { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + } +} diff --git a/dip-template/nodes/dip-receiver/src/command.rs b/dip-template/nodes/dip-receiver/src/command.rs new file mode 100644 index 0000000000..0e9db24e47 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/command.rs @@ -0,0 +1,400 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::{fs::create_dir_all, net::SocketAddr}; + +use cumulus_client_cli::generate_genesis_block; +use cumulus_primitives_core::ParaId; +use dip_receiver_runtime_template::Block; +use log::{info, warn}; +use parity_scale_codec::Encode; +use sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, LoggerBuilder, + NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, +}; +use sc_service::{ + config::{BasePath, PrometheusConfig}, + Configuration, Role, RpcMethods, TransactionPoolOptions, +}; +use sc_sysinfo::gather_hwbench; +use sc_telemetry::TelemetryEndpoints; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; + +use crate::{ + chain_spec::{development_config, Extensions}, + cli::{Cli, RelayChainCli, Subcommand}, + service::{new_partial, start_parachain_node}, +}; + +fn load_spec(id: &str) -> std::result::Result, String> { + match id { + "dev" => Ok(Box::new(development_config())), + _ => Err("Unrecognized spec ID.".into()), + } +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "DIP Receiver Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Receiver Node Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/kiltprotocol/kilt-node/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2023 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id) + } + + fn native_runtime_version(_spec: &Box) -> &'static RuntimeVersion { + &dip_receiver_runtime_template::VERSION + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "DIP Receiver Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Receiver Node Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/kiltprotocol/kilt-node/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2023 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } + + fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion { + polkadot_cli::Cli::native_runtime_version(chain_spec) + } +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = new_partial(&$config)?; + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) + }} +} + +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + } + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.database))) + } + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.chain_spec))) + } + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + } + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.backend, None)) + }) + } + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cli.relay_chain_args.iter()), + ); + + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, config.tokio_handle.clone()) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + } + Some(Subcommand::ExportGenesisState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + let state_version = Cli::native_runtime_version(&spec).state_version(); + cmd.run::(&*spec, state_version) + }) + } + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + } + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let hwbench = (!cli.no_hardware_benchmarks).then_some( + config.database.path().map(|database_path| { + let _ = create_dir_all(database_path); + gather_hwbench(Some(database_path)) + })).flatten(); + + let para_id = Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain ID in chain-spec.")?; + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()), + ); + + let id = ParaId::from(para_id); + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + let state_version = Cli::native_runtime_version(&config.chain_spec).state_version(); + let block: Block = generate_genesis_block(&*config.chain_spec, state_version) + .map_err(|e| format!("{:?}", e))?; + let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state: {}", genesis_state); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + if !collator_options.relay_chain_rpc_urls.is_empty() && cli.relay_chain_args.is_empty() { + warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options."); + } + + start_parachain_node( + config, + polkadot_config, + collator_options, + id, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + } + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_ws_listen_port() -> u16 { + 9945 + } + + fn rpc_http_listen_port() -> u16 { + 9934 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &Configuration, + ) -> Result<()> + where + F: FnOnce(&mut LoggerBuilder, &Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints(&self, chain_spec: &Box) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/dip-template/nodes/dip-receiver/src/main.rs b/dip-template/nodes/dip-receiver/src/main.rs new file mode 100644 index 0000000000..e0250cebc8 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/main.rs @@ -0,0 +1,32 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! KILT Decentralized Identity Provider (DIP) receiver CLI. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; +mod rpc; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/dip-template/nodes/dip-receiver/src/rpc.rs b/dip-template/nodes/dip-receiver/src/rpc.rs new file mode 100644 index 0000000000..30a8bd3b09 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/rpc.rs @@ -0,0 +1,70 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![warn(missing_docs)] + +pub use sc_rpc_api::DenyUnsafe; +use substrate_frame_rpc_system::AccountNonceApi; + +use std::{error::Error, sync::Arc}; + +use dip_receiver_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; +use sc_client_api::AuxStore; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; + +pub type RpcExtension = RpcModule<()>; + +pub struct FullDeps { + pub client: Arc, + pub pool: Arc

, + pub deny_unsafe: DenyUnsafe, +} + +pub fn create_full(deps: FullDeps) -> Result> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + HeaderMetadata + + Send + + Sync + + 'static, + C::Api: TransactionPaymentRuntimeApi, + C::Api: AccountNonceApi, + C::Api: BlockBuilder, + P: TransactionPool + Sync + Send + 'static, +{ + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut module = RpcExtension::new(()); + let FullDeps { + client, + pool, + deny_unsafe, + } = deps; + + module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(TransactionPayment::new(client).into_rpc())?; + Ok(module) +} diff --git a/dip-template/nodes/dip-receiver/src/service.rs b/dip-template/nodes/dip-receiver/src/service.rs new file mode 100644 index 0000000000..82c07368a7 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/service.rs @@ -0,0 +1,412 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::{error::Error, sync::Arc, time::Duration}; + +use cumulus_client_cli::CollatorOptions; +use cumulus_client_consensus_aura::{ + import_queue, slot_duration, AuraConsensus, BuildAuraConsensusParams, ImportQueueParams, SlotProportion, +}; +use cumulus_client_consensus_common::{ParachainBlockImport as TParachainBlockImport, ParachainConsensus}; +use cumulus_client_service::{ + build_network, build_relay_chain_interface, prepare_node_config, start_collator, start_full_node, + BuildNetworkParams, StartCollatorParams, StartFullNodeParams, +}; +use cumulus_primitives_core::ParaId; +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use cumulus_relay_chain_interface::RelayChainInterface; +use dip_receiver_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; +use frame_benchmarking::benchmarking::HostFunctions; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; +use sc_basic_authorship::ProposerFactory; +use sc_consensus::{DefaultImportQueue, ImportQueue}; +use sc_executor::NativeElseWasmExecutor; +use sc_network::{NetworkBlock, NetworkService}; +use sc_service::{ + build_offchain_workers, new_full_parts, spawn_tasks, Configuration, PartialComponents, SpawnTasksParams, + TFullBackend, TFullClient, TaskManager, +}; +use sc_sysinfo::{initialize_hwbench_telemetry, print_hwbench, HwBench}; +use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; +use sc_transaction_pool::{BasicPool, FullPool}; +use sp_keystore::SyncCryptoStorePtr; +use substrate_prometheus_endpoint::Registry; + +use crate::rpc::{create_full, FullDeps}; + +pub struct ParachainNativeExecutor; + +impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { + type ExtendHostFunctions = HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + native_version() + } +} + +type ParachainExecutor = NativeElseWasmExecutor; +type ParachainClient = TFullClient; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = TParachainBlockImport, ParachainBackend>; + +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, +) -> Result< + PartialComponents< + ParachainClient, + ParachainBackend, + (), + DefaultImportQueue, + FullPool, + (ParachainBlockImport, Option, Option), + >, + sc_service::Error, +> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = ParachainExecutor::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + config.runtime_cache_size, + ); + + let (client, backend, keystore_container, task_manager) = new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + + let import_queue = build_import_queue( + client.clone(), + block_import.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + Ok(PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (block_import, telemetry, telemetry_worker_handle), + }) +} + +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node_impl( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + let parachain_config = prepare_node_config(parachain_config); + + let params = new_partial(¶chain_config)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let client = params.client.clone(); + let backend = params.backend.clone(); + let mut task_manager = params.task_manager; + + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options.clone(), + hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let force_authoring = parachain_config.force_authoring; + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + + let (network, system_rpc_tx, tx_handler_controller, start_network) = build_network(BuildNetworkParams { + parachain_config: ¶chain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + para_id, + spawn_handle: task_manager.spawn_handle(), + relay_chain_interface: relay_chain_interface.clone(), + import_queue: params.import_queue, + }) + .await?; + + if parachain_config.offchain_worker.enabled { + build_offchain_workers( + ¶chain_config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + } + + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + }; + + create_full(deps).map_err(Into::into) + }) + }; + + spawn_tasks(SpawnTasksParams { + rpc_builder, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.sync_keystore(), + backend, + network: network.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + print_hwbench(&hwbench); + if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && validator { + log::warn!("⚠️ The hardware does not meet the minimal requirements for role 'Authority'."); + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let announce_block = { + let network = network.clone(); + Arc::new(move |hash, data| network.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + if validator { + let parachain_consensus = build_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + network, + params.keystore_container.sync_keystore(), + force_authoring, + para_id, + )?; + + let spawner = task_manager.spawn_handle(); + let params = StartCollatorParams { + para_id, + block_status: client.clone(), + announce_block, + client: client.clone(), + task_manager: &mut task_manager, + relay_chain_interface, + spawner, + parachain_consensus, + import_queue: import_queue_service, + collator_key: collator_key.expect("Command line arguments do not allow this. qed"), + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle), + }; + + start_collator(params).await?; + } else { + let params = StartFullNodeParams { + client: client.clone(), + announce_block, + task_manager: &mut task_manager, + para_id, + relay_chain_interface, + relay_chain_slot_duration, + import_queue: import_queue_service, + recovery_handle: Box::new(overseer_handle), + }; + + start_full_node(params)?; + } + + start_network.start_network(); + + Ok((task_manager, client)) +} + +fn build_import_queue( + client: Arc, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + let slot_duration = slot_duration(&*client)?; + + import_queue::(ImportQueueParams { + block_import, + client, + create_inherent_data_providers: move |_, _| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +#[allow(clippy::too_many_arguments)] +fn build_consensus( + client: Arc, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>, + sync_oracle: Arc>, + keystore: SyncCryptoStorePtr, + force_authoring: bool, + para_id: ParaId, +) -> Result>, sc_service::Error> { + let slot_duration = slot_duration(&*client)?; + + let proposer_factory = ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let params = BuildAuraConsensusParams { + proposer_factory, + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + ParachainInherentData::create_at(relay_parent, &relay_chain_interface, &validation_data, para_id) + .await; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + let parachain_inherent = parachain_inherent + .ok_or_else(|| Box::::from("Failed to create parachain inherent"))?; + Ok((slot, timestamp, parachain_inherent)) + } + }, + block_import, + para_client: client, + backoff_authoring_blocks: Option::<()>::None, + sync_oracle, + keystore, + force_authoring, + slot_duration, + block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), + max_block_proposal_slot_portion: Some(SlotProportion::new(1f32 / 16f32)), + telemetry, + }; + + Ok(AuraConsensus::build::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + _, + >(params)) +} + +pub async fn start_parachain_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + start_node_impl(parachain_config, polkadot_config, collator_options, para_id, hwbench).await +} diff --git a/dip-template/nodes/dip-sender/Cargo.toml b/dip-template/nodes/dip-sender/Cargo.toml new file mode 100644 index 0000000000..c77a4b72ec --- /dev/null +++ b/dip-template/nodes/dip-sender/Cargo.toml @@ -0,0 +1,67 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "dip-sender-node-template" +description = "Node template for the KILT Decentralized Identity Provider (DIP) sender." +build = "build.rs" + +[dependencies] +clap = {workspace = true, features = ["std", "derive"]} +log = { workspace = true, features = ["std"] } +parity-scale-codec = { workspace = true, features = ["std"] } +serde = {workspace = true, features = ["std", "derive"]} +jsonrpsee = {workspace = true, features = ["server"]} + +# Internal dependencies +dip-sender-runtime-template = { workspace = true, features = ["std"] } + +# Substrate +frame-benchmarking.workspace = true +frame-benchmarking-cli.workspace = true +pallet-transaction-payment-rpc.workspace = true +sc-basic-authorship.workspace = true +sc-chain-spec.workspace = true +sc-cli.workspace = true +sc-client-api.workspace = true +sc-consensus.workspace = true +sc-executor.workspace = true +sc-network.workspace = true +sc-rpc-api.workspace = true +sc-service.workspace = true +sc-sysinfo.workspace = true +sc-telemetry.workspace = true +sc-tracing.workspace = true +sc-transaction-pool.workspace = true +sc-transaction-pool-api.workspace = true +sp-api = { workspace = true, features = ["std"] } +sp-block-builder = { workspace = true, features = ["std"] } +sp-blockchain.workspace = true +sp-consensus-aura = { workspace = true, features = ["std"] } +sp-core = { workspace = true, features = ["std"] } +sp-keystore = { workspace = true, features = ["std"] } +sp-runtime = { workspace = true, features = ["std"] } +sp-timestamp = { workspace = true, features = ["std"] } +substrate-frame-rpc-system.workspace = true +substrate-prometheus-endpoint.workspace = true + +# Polkadot +polkadot-cli.workspace = true +polkadot-primitives = { workspace = true, features = ["std"] } + +# Cumulus +cumulus-client-cli.workspace = true +cumulus-client-consensus-aura.workspace = true +cumulus-client-consensus-common.workspace = true +cumulus-client-service.workspace = true +cumulus-primitives-core = { workspace = true, features = ["std"] } +cumulus-primitives-parachain-inherent = { workspace = true, features = ["std"] } +cumulus-relay-chain-interface.workspace = true + +[build-dependencies] +substrate-build-script-utils.workspace = true diff --git a/dip-template/nodes/dip-sender/build.rs b/dip-template/nodes/dip-sender/build.rs new file mode 100644 index 0000000000..961231c478 --- /dev/null +++ b/dip-template/nodes/dip-sender/build.rs @@ -0,0 +1,25 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/dip-template/nodes/dip-sender/src/chain_spec.rs b/dip-template/nodes/dip-sender/src/chain_spec.rs new file mode 100644 index 0000000000..8fe5269ada --- /dev/null +++ b/dip-template/nodes/dip-sender/src/chain_spec.rs @@ -0,0 +1,143 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_primitives_core::ParaId; +use dip_sender_runtime_template::{ + AccountId, AuraId, BalancesConfig, CollatorSelectionConfig, GenesisConfig, ParachainInfoConfig, SessionConfig, + SessionKeys, Signature, SudoConfig, SystemConfig, EXISTENTIAL_DEPOSIT, SS58_PREFIX, WASM_BINARY, +}; +use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, Properties}; +use sc_service::{ChainType, GenericChainSpec}; +use serde::{Deserialize, Serialize}; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +const PARA_ID: u32 = 2_000; + +pub type ChainSpec = GenericChainSpec; +type AccountPublic = ::Signer; + +pub(crate) fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + pub relay_chain: String, + pub para_id: u32, +} + +impl Extensions { + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { + get_from_seed::(seed) +} + +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +pub fn template_session_keys(keys: AuraId) -> SessionKeys { + SessionKeys { aura: keys } +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + parachain_system: Default::default(), + parachain_info: ParachainInfoConfig { parachain_id: id }, + sudo: SudoConfig { + key: Some(endowed_accounts.first().unwrap().clone()), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + transaction_payment: Default::default(), + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: EXISTENTIAL_DEPOSIT * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| (acc.clone(), acc, template_session_keys(aura))) + .collect(), + }, + aura: Default::default(), + aura_ext: Default::default(), + polkadot_xcm: Default::default(), + } +} + +pub fn development_config() -> ChainSpec { + let mut properties = Properties::new(); + properties.insert("tokenSymbol".into(), "SEILT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), SS58_PREFIX.into()); + + ChainSpec::from_genesis( + "DIP sender dev", + "dip-sender-dev", + ChainType::Development, + move || { + testnet_genesis( + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Charlie//stash"), + ], + PARA_ID.into(), + ) + }, + Vec::new(), + None, + "dip-sender-dev".into(), + None, + None, + Extensions { + relay_chain: "rococo-local".into(), + para_id: PARA_ID, + }, + ) +} diff --git a/dip-template/nodes/dip-sender/src/cli.rs b/dip-template/nodes/dip-sender/src/cli.rs new file mode 100644 index 0000000000..3cec4a2550 --- /dev/null +++ b/dip-template/nodes/dip-sender/src/cli.rs @@ -0,0 +1,89 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::path::PathBuf; + +use cumulus_client_cli::{ExportGenesisStateCommand, ExportGenesisWasmCommand, PurgeChainCmd}; +use polkadot_cli::RunCmd; +use sc_cli::{BuildSpecCmd, CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; +use sc_service::Configuration; + +use crate::chain_spec::Extensions; + +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + BuildSpec(BuildSpecCmd), + + CheckBlock(CheckBlockCmd), + + ExportBlocks(ExportBlocksCmd), + + ExportState(ExportStateCmd), + + ImportBlocks(ImportBlocksCmd), + + Revert(RevertCmd), + + PurgeChain(PurgeChainCmd), + + ExportGenesisState(ExportGenesisStateCommand), + + ExportGenesisWasm(ExportGenesisWasmCommand), +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: cumulus_client_cli::RunCmd, + + #[arg(long)] + pub no_hardware_benchmarks: bool, + + #[arg(raw = true)] + pub relay_chain_args: Vec, +} + +#[derive(Debug)] +pub struct RelayChainCli { + pub base: RunCmd, + + pub chain_id: Option, + + pub base_path: Option, +} + +impl RelayChainCli { + pub fn new<'a>(para_config: &Configuration, relay_chain_args: impl Iterator) -> Self { + let extension = Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.as_ref().map(|x| x.path().join("polkadot")); + Self { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + } +} diff --git a/dip-template/nodes/dip-sender/src/command.rs b/dip-template/nodes/dip-sender/src/command.rs new file mode 100644 index 0000000000..da2116679a --- /dev/null +++ b/dip-template/nodes/dip-sender/src/command.rs @@ -0,0 +1,400 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::{fs::create_dir_all, net::SocketAddr}; + +use cumulus_client_cli::generate_genesis_block; +use cumulus_primitives_core::ParaId; +use dip_sender_runtime_template::Block; +use log::{info, warn}; +use parity_scale_codec::Encode; +use sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, LoggerBuilder, + NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, +}; +use sc_service::{ + config::{BasePath, PrometheusConfig}, + Configuration, Role, RpcMethods, TransactionPoolOptions, +}; +use sc_sysinfo::gather_hwbench; +use sc_telemetry::TelemetryEndpoints; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; + +use crate::{ + chain_spec::{development_config, Extensions}, + cli::{Cli, RelayChainCli, Subcommand}, + service::{new_partial, start_parachain_node}, +}; + +fn load_spec(id: &str) -> std::result::Result, String> { + match id { + "dev" => Ok(Box::new(development_config())), + _ => Err("Unrecognized spec ID.".into()), + } +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "DIP Sender Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Sender Node Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/kiltprotocol/kilt-node/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2023 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id) + } + + fn native_runtime_version(_spec: &Box) -> &'static RuntimeVersion { + &dip_sender_runtime_template::VERSION + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "DIP Sender Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Sender Node Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relay chain node.\n\n\ + {} -- ", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/kiltprotocol/kilt-node/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2023 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } + + fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion { + polkadot_cli::Cli::native_runtime_version(chain_spec) + } +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = new_partial(&$config)?; + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) + }} +} + +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + } + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + } + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.database))) + } + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.chain_spec))) + } + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + } + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.backend, None)) + }) + } + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cli.relay_chain_args.iter()), + ); + + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, config.tokio_handle.clone()) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + } + Some(Subcommand::ExportGenesisState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + let state_version = Cli::native_runtime_version(&spec).state_version(); + cmd.run::(&*spec, state_version) + }) + } + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + } + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let hwbench = (!cli.no_hardware_benchmarks).then_some( + config.database.path().map(|database_path| { + let _ = create_dir_all(database_path); + gather_hwbench(Some(database_path)) + })).flatten(); + + let para_id = Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain ID in chain-spec.")?; + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()), + ); + + let id = ParaId::from(para_id); + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + let state_version = Cli::native_runtime_version(&config.chain_spec).state_version(); + let block: Block = generate_genesis_block(&*config.chain_spec, state_version) + .map_err(|e| format!("{:?}", e))?; + let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state: {}", genesis_state); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + if !collator_options.relay_chain_rpc_urls.is_empty() && cli.relay_chain_args.is_empty() { + warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options."); + } + + start_parachain_node( + config, + polkadot_config, + collator_options, + id, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + } + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_ws_listen_port() -> u16 { + 9945 + } + + fn rpc_http_listen_port() -> u16 { + 9934 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &Configuration, + ) -> Result<()> + where + F: FnOnce(&mut LoggerBuilder, &Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints(&self, chain_spec: &Box) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/dip-template/nodes/dip-sender/src/main.rs b/dip-template/nodes/dip-sender/src/main.rs new file mode 100644 index 0000000000..a3e57e48c6 --- /dev/null +++ b/dip-template/nodes/dip-sender/src/main.rs @@ -0,0 +1,32 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! KILT Decentralized Identity Provider (DIP) sender CLI. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; +mod rpc; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/dip-template/nodes/dip-sender/src/rpc.rs b/dip-template/nodes/dip-sender/src/rpc.rs new file mode 100644 index 0000000000..79365ebb0e --- /dev/null +++ b/dip-template/nodes/dip-sender/src/rpc.rs @@ -0,0 +1,70 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![warn(missing_docs)] + +pub use sc_rpc_api::DenyUnsafe; +use substrate_frame_rpc_system::AccountNonceApi; + +use std::{error::Error, sync::Arc}; + +use dip_sender_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; +use sc_client_api::AuxStore; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; + +pub type RpcExtension = RpcModule<()>; + +pub struct FullDeps { + pub client: Arc, + pub pool: Arc

, + pub deny_unsafe: DenyUnsafe, +} + +pub fn create_full(deps: FullDeps) -> Result> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + HeaderMetadata + + Send + + Sync + + 'static, + C::Api: TransactionPaymentRuntimeApi, + C::Api: AccountNonceApi, + C::Api: BlockBuilder, + P: TransactionPool + Sync + Send + 'static, +{ + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut module = RpcExtension::new(()); + let FullDeps { + client, + pool, + deny_unsafe, + } = deps; + + module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(TransactionPayment::new(client).into_rpc())?; + Ok(module) +} diff --git a/dip-template/nodes/dip-sender/src/service.rs b/dip-template/nodes/dip-sender/src/service.rs new file mode 100644 index 0000000000..9421403248 --- /dev/null +++ b/dip-template/nodes/dip-sender/src/service.rs @@ -0,0 +1,412 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::{error::Error, sync::Arc, time::Duration}; + +use cumulus_client_cli::CollatorOptions; +use cumulus_client_consensus_aura::{ + import_queue, slot_duration, AuraConsensus, BuildAuraConsensusParams, ImportQueueParams, SlotProportion, +}; +use cumulus_client_consensus_common::{ParachainBlockImport as TParachainBlockImport, ParachainConsensus}; +use cumulus_client_service::{ + build_network, build_relay_chain_interface, prepare_node_config, start_collator, start_full_node, + BuildNetworkParams, StartCollatorParams, StartFullNodeParams, +}; +use cumulus_primitives_core::ParaId; +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use cumulus_relay_chain_interface::RelayChainInterface; +use dip_sender_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; +use frame_benchmarking::benchmarking::HostFunctions; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; +use sc_basic_authorship::ProposerFactory; +use sc_consensus::{DefaultImportQueue, ImportQueue}; +use sc_executor::NativeElseWasmExecutor; +use sc_network::{NetworkBlock, NetworkService}; +use sc_service::{ + build_offchain_workers, new_full_parts, spawn_tasks, Configuration, PartialComponents, SpawnTasksParams, + TFullBackend, TFullClient, TaskManager, +}; +use sc_sysinfo::{initialize_hwbench_telemetry, print_hwbench, HwBench}; +use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; +use sc_transaction_pool::{BasicPool, FullPool}; +use sp_keystore::SyncCryptoStorePtr; +use substrate_prometheus_endpoint::Registry; + +use crate::rpc::{create_full, FullDeps}; + +pub struct ParachainNativeExecutor; + +impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { + type ExtendHostFunctions = HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + native_version() + } +} + +type ParachainExecutor = NativeElseWasmExecutor; +type ParachainClient = TFullClient; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = TParachainBlockImport, ParachainBackend>; + +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, +) -> Result< + PartialComponents< + ParachainClient, + ParachainBackend, + (), + DefaultImportQueue, + FullPool, + (ParachainBlockImport, Option, Option), + >, + sc_service::Error, +> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = ParachainExecutor::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + config.runtime_cache_size, + ); + + let (client, backend, keystore_container, task_manager) = new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + + let import_queue = build_import_queue( + client.clone(), + block_import.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + Ok(PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (block_import, telemetry, telemetry_worker_handle), + }) +} + +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node_impl( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + let parachain_config = prepare_node_config(parachain_config); + + let params = new_partial(¶chain_config)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let client = params.client.clone(); + let backend = params.backend.clone(); + let mut task_manager = params.task_manager; + + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options.clone(), + hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let force_authoring = parachain_config.force_authoring; + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + + let (network, system_rpc_tx, tx_handler_controller, start_network) = build_network(BuildNetworkParams { + parachain_config: ¶chain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + para_id, + spawn_handle: task_manager.spawn_handle(), + relay_chain_interface: relay_chain_interface.clone(), + import_queue: params.import_queue, + }) + .await?; + + if parachain_config.offchain_worker.enabled { + build_offchain_workers( + ¶chain_config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + } + + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + + Box::new(move |deny_unsafe, _| { + let deps = FullDeps { + client: client.clone(), + pool: transaction_pool.clone(), + deny_unsafe, + }; + + create_full(deps).map_err(Into::into) + }) + }; + + spawn_tasks(SpawnTasksParams { + rpc_builder, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.sync_keystore(), + backend, + network: network.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + print_hwbench(&hwbench); + if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && validator { + log::warn!("⚠️ The hardware does not meet the minimal requirements for role 'Authority'."); + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let announce_block = { + let network = network.clone(); + Arc::new(move |hash, data| network.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + if validator { + let parachain_consensus = build_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + network, + params.keystore_container.sync_keystore(), + force_authoring, + para_id, + )?; + + let spawner = task_manager.spawn_handle(); + let params = StartCollatorParams { + para_id, + block_status: client.clone(), + announce_block, + client: client.clone(), + task_manager: &mut task_manager, + relay_chain_interface, + spawner, + parachain_consensus, + import_queue: import_queue_service, + collator_key: collator_key.expect("Command line arguments do not allow this. qed"), + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle), + }; + + start_collator(params).await?; + } else { + let params = StartFullNodeParams { + client: client.clone(), + announce_block, + task_manager: &mut task_manager, + para_id, + relay_chain_interface, + relay_chain_slot_duration, + import_queue: import_queue_service, + recovery_handle: Box::new(overseer_handle), + }; + + start_full_node(params)?; + } + + start_network.start_network(); + + Ok((task_manager, client)) +} + +fn build_import_queue( + client: Arc, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + let slot_duration = slot_duration(&*client)?; + + import_queue::(ImportQueueParams { + block_import, + client, + create_inherent_data_providers: move |_, _| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +#[allow(clippy::too_many_arguments)] +fn build_consensus( + client: Arc, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>, + sync_oracle: Arc>, + keystore: SyncCryptoStorePtr, + force_authoring: bool, + para_id: ParaId, +) -> Result>, sc_service::Error> { + let slot_duration = slot_duration(&*client)?; + + let proposer_factory = ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let params = BuildAuraConsensusParams { + proposer_factory, + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + ParachainInherentData::create_at(relay_parent, &relay_chain_interface, &validation_data, para_id) + .await; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + let parachain_inherent = parachain_inherent + .ok_or_else(|| Box::::from("Failed to create parachain inherent"))?; + Ok((slot, timestamp, parachain_inherent)) + } + }, + block_import, + para_client: client, + backoff_authoring_blocks: Option::<()>::None, + sync_oracle, + keystore, + force_authoring, + slot_duration, + block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), + max_block_proposal_slot_portion: Some(SlotProportion::new(1f32 / 16f32)), + telemetry, + }; + + Ok(AuraConsensus::build::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + _, + >(params)) +} + +pub async fn start_parachain_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc)> { + start_node_impl(parachain_config, polkadot_config, collator_options, para_id, hwbench).await +} diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml new file mode 100644 index 0000000000..d41f73dbab --- /dev/null +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -0,0 +1,123 @@ +[package] +authors.workspace = true +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) receiver." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-receiver-runtime-template" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[build-dependencies] +substrate-wasm-builder.workspace = true + +[dependencies] +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# DIP +pallet-did-lookup.workspace = true +pallet-dip-receiver.workspace = true + +# Substrate +frame-executive.workspace = true +frame-support.workspace = true +frame-system.workspace = true +frame-system-rpc-runtime-api.workspace = true +pallet-aura.workspace = true +pallet-authorship.workspace = true +pallet-balances.workspace = true +pallet-session.workspace = true +pallet-sudo.workspace = true +pallet-timestamp.workspace = true +pallet-transaction-payment.workspace = true +pallet-transaction-payment-rpc-runtime-api.workspace = true +polkadot-parachain.workspace = true +sp-api.workspace = true +sp-block-builder.workspace = true +sp-consensus-aura.workspace = true +sp-core.workspace = true +sp-inherents.workspace = true +sp-offchain.workspace = true +sp-runtime.workspace = true +sp-session.workspace = true +sp-std.workspace = true +sp-transaction-pool.workspace = true +sp-version.workspace = true + +# Polkadot +pallet-xcm.workspace = true +xcm.workspace = true +xcm-builder.workspace = true +xcm-executor.workspace = true + +# Cumulus +cumulus-pallet-aura-ext.workspace = true +cumulus-pallet-dmp-queue.workspace = true +cumulus-pallet-parachain-system.workspace = true +cumulus-pallet-xcm.workspace = true +cumulus-pallet-xcmp-queue.workspace = true +cumulus-primitives-core.workspace = true +cumulus-primitives-timestamp.workspace = true +cumulus-primitives-utility.workspace = true +pallet-collator-selection.workspace = true +parachain-info.workspace = true + +[features] +default = [ + "std", +] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "pallet-did-lookup/std", + "pallet-dip-receiver/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-system-rpc-runtime-api/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "polkadot-parachain/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "pallet-xcm/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "pallet-collator-selection/std", + "parachain-info/std" +] + +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-dip-receiver/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", +] diff --git a/dip-template/runtimes/dip-receiver/build.rs b/dip-template/runtimes/dip-receiver/build.rs new file mode 100644 index 0000000000..a394382833 --- /dev/null +++ b/dip-template/runtimes/dip-receiver/build.rs @@ -0,0 +1,27 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs new file mode 100644 index 0000000000..b36d425abd --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/dip.rs @@ -0,0 +1,36 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use pallet_dip_receiver::traits::SuccessfulProofVerifier; + +use crate::{DidIdentifier, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; + +impl pallet_dip_receiver::Config for Runtime { + type Identifier = DidIdentifier; + // TODO: Change with right one + type ProofDigest = [u8; 32]; + // TODO: Change with right one + type ProofLeafKey = [u8; 4]; + // TODO: Change with right one + type ProofLeafValue = [u8; 4]; + // TODO: Change with right one + type ProofVerifier = SuccessfulProofVerifier; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; +} diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs new file mode 100644 index 0000000000..6dd9a52193 --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -0,0 +1,501 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +use cumulus_pallet_parachain_system::{ + register_validate_block, ParachainSetCode, RelayChainStateProof, RelayNumberStrictlyIncreases, +}; +use cumulus_primitives_core::CollationInfo; +use cumulus_primitives_timestamp::InherentDataProvider; +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8, Everything}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + IdentityFee, Weight, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + ChainContext, EnsureRoot, +}; +use pallet_balances::AccountData; +use pallet_collator_selection::IdentityCollator; +use pallet_dip_receiver::{DipOrigin, EnsureDipOrigin}; +use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; +use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::SlotDuration; +use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; +use sp_inherents::{CheckInherentsResult, InherentData}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::{prelude::*, time::Duration}; +use sp_version::RuntimeVersion; + +mod dip; +mod xcm_config; +pub use crate::{dip::*, xcm_config::*}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type Address = MultiAddress; +pub type Balance = u128; +pub type Block = generic::Block; +pub type BlockNumber = u32; +pub type DidIdentifier = AccountId; +pub type Hash = sp_core::H256; +pub type Header = generic::Header; +pub type Index = u32; +pub type Signature = MultiSignature; + +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type Executive = frame_executive::Executive, Runtime, AllPalletsWithSystem>; +pub type NodeBlock = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; + +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = UNIT / 1_000; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = NodeBlock, + UncheckedExtrinsic = UncheckedExtrinsic, + { + // System + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + + // Money + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Collators + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + DmpQueue: cumulus_pallet_dmp_queue = 31, + PolkadotXcm: pallet_xcm = 32, + CumulusXcm: cumulus_pallet_xcm = 33, + + // DID lookup + DidLookup: pallet_did_lookup = 40, + + // DIP + DipReceiver: pallet_dip_receiver = 50, + } +); + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("dip-receiver-runtime-template"), + impl_name: create_runtime_str!("dip-receiver-runtime-template"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents(block: &Block, relay_state_proof: &RelayChainStateProof) -> CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + InherentDataProvider::from_relay_chain_slot_and_duration(relay_chain_slot, Duration::from_secs(6)) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} + +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +pub const SS58_PREFIX: u16 = 101; + +impl frame_system::Config for Runtime { + type AccountData = AccountData; + type AccountId = AccountId; + type BaseCallFilter = Everything; + type BlockHashCount = ConstU32<256>; + type BlockLength = RuntimeBlockLength; + type BlockNumber = BlockNumber; + type BlockWeights = RuntimeBlockWeights; + type DbWeight = RocksDbWeight; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Header = generic::Header; + type Index = Index; + type Lookup = AccountIdLookup; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = ParachainSetCode; + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16; + type SystemWeightInfo = (); + type Version = Version; +} + +parameter_types! { + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; + type DmpMessageHandler = DmpQueue; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = XcmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type ReservedXcmpWeight = ReservedXcmpWeight; + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainInfo; + type XcmpMessageHandler = XcmpQueue; +} + +impl pallet_timestamp::Config for Runtime { + type MinimumPeriod = ConstU64<{ MILLISECS_PER_BLOCK / 2 }>; + type Moment = u64; + type OnTimestampSet = Aura; + type WeightInfo = (); +} + +impl parachain_info::Config for Runtime {} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ConstU128; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = CurrencyAdapter; + type FeeMultiplierUpdate = (); + type LengthToFee = IdentityFee; + type OperationalFeeMultiplier = ConstU8<1>; + type RuntimeEvent = RuntimeEvent; + type WeightToFee = IdentityFee; +} + +impl pallet_authorship::Config for Runtime { + type EventHandler = (CollatorSelection,); + type FindAuthor = FindAccountFromAuthorIndex; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); +} + +impl pallet_collator_selection::Config for Runtime { + type Currency = Balances; + type PotId = PotId; + type KickThreshold = ConstU32<{ 6 * HOURS }>; + type MaxCandidates = ConstU32<1_000>; + type MaxInvulnerables = ConstU32<100>; + type MinCandidates = ConstU32<5>; + type RuntimeEvent = RuntimeEvent; + type UpdateOrigin = EnsureRoot; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +impl pallet_session::Config for Runtime { + type Keys = SessionKeys; + type NextSessionRotation = PeriodicSessions, ConstU32<0>>; + type RuntimeEvent = RuntimeEvent; + type SessionHandler = ::KeyTypeIdProviders; + type SessionManager = CollatorSelection; + type ShouldEndSession = PeriodicSessions, ConstU32<0>>; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +parameter_types! { + pub const LinkDeposit: Balance = UNIT; +} + +impl pallet_did_lookup::Config for Runtime { + type Currency = Balances; + type Deposit = ConstU128; + type DidIdentifier = DidIdentifier; + type EnsureOrigin = EnsureDipOrigin; + type OriginSuccess = DipOrigin; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> SlotDuration { + SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } +} diff --git a/dip-template/runtimes/dip-receiver/src/xcm_config.rs b/dip-template/runtimes/dip-receiver/src/xcm_config.rs new file mode 100644 index 0000000000..cc0ee09fcc --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/xcm_config.rs @@ -0,0 +1,149 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_primitives_utility::ParentAsUmp; +use frame_support::{ + parameter_types, + traits::{ConstU32, Contains, Everything, Nothing}, + weights::{IdentityFee, Weight}, +}; +use frame_system::EnsureRoot; +use pallet_xcm::TestWeightInfo; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedToAccountId32, UsingComponents, +}; +use xcm_executor::XcmExecutor; + +use crate::{ + AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, XcmpQueue, +}; + +parameter_types! { + pub HereLocation: MultiLocation = MultiLocation::here(); + pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +pub type Barrier = AllowTopLevelPaidExecutionFrom; +pub type AssetTransactorLocationConverter = SiblingParachainConvertsVia; +pub type LocalAssetTransactor = + CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; +pub type XcmRouter = (ParentAsUmp, XcmpQueue); + +pub struct DipTransactSafeCalls; + +impl Contains for DipTransactSafeCalls { + fn contains(t: &RuntimeCall) -> bool { + matches!( + t, + RuntimeCall::DipReceiver(pallet_dip_receiver::Call::process_identity_action { .. }) + ) + } +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = (); + type AssetTransactor = LocalAssetTransactor; + type AssetTrap = (); + type Barrier = Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = ConstU32<64>; + type MessageExporter = (); + type OriginConverter = SiblingParachainAsNative; + type PalletInstancesInfo = AllPalletsWithSystem; + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = DipTransactSafeCalls; + type SubscriptionService = (); + type UniversalAliases = Nothing; + type UniversalLocation = UniversalLocation; + type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; + type Weigher = FixedWeightBounds>; + type XcmSender = XcmRouter; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type ChannelInfo = ParachainSystem; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = (); + type ExecuteOverweightOrigin = EnsureRoot; + type PriceForSiblingDelivery = (); + type RuntimeEvent = RuntimeEvent; + type VersionWrapper = (); + type WeightInfo = (); + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type ExecuteOverweightOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +const MAX_INSTRUCTIONS: u32 = 100; + +parameter_types! { + pub RelayNetwork: Option = None; +} +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = (); + type TrustedLockers = (); + type UniversalLocation = UniversalLocation; + type Weigher = FixedWeightBounds>; + type WeightInfo = TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Nothing; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml new file mode 100644 index 0000000000..d9972fd45a --- /dev/null +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -0,0 +1,123 @@ +[package] +authors.workspace = true +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) sender." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-sender-runtime-template" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[build-dependencies] +substrate-wasm-builder.workspace = true + +[dependencies] +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# DIP +did.workspace = true +dip-support.workspace = true +pallet-dip-sender.workspace = true + +# Substrate +frame-executive.workspace = true +frame-support.workspace = true +frame-system.workspace = true +frame-system-rpc-runtime-api.workspace = true +pallet-aura.workspace = true +pallet-authorship.workspace = true +pallet-balances.workspace = true +pallet-session.workspace = true +pallet-sudo.workspace = true +pallet-timestamp.workspace = true +pallet-transaction-payment.workspace = true +pallet-transaction-payment-rpc-runtime-api.workspace = true +sp-api.workspace = true +sp-block-builder.workspace = true +sp-consensus-aura.workspace = true +sp-core.workspace = true +sp-inherents.workspace = true +sp-offchain.workspace = true +sp-runtime.workspace = true +sp-session.workspace = true +sp-std.workspace = true +sp-transaction-pool.workspace = true +sp-version.workspace = true + +# Polkadot +pallet-xcm.workspace = true +xcm.workspace = true +xcm-builder.workspace = true +xcm-executor.workspace = true + +# Cumulus +cumulus-pallet-aura-ext.workspace = true +cumulus-pallet-dmp-queue.workspace = true +cumulus-pallet-parachain-system.workspace = true +cumulus-pallet-xcm.workspace = true +cumulus-pallet-xcmp-queue.workspace = true +cumulus-primitives-core.workspace = true +cumulus-primitives-timestamp.workspace = true +cumulus-primitives-utility.workspace = true +pallet-collator-selection.workspace = true +parachain-info.workspace = true + +[features] +default = [ + "std", +] +std = [ + "parity-scale-codec/std", + "scale-info/std", + "did/std", + "dip-support/std", + "pallet-dip-sender/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-system-rpc-runtime-api/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "pallet-xcm/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "pallet-collator-selection/std", + "parachain-info/std" +] +runtime-benchmarks = [ + "did/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-dip-sender/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks" +] diff --git a/dip-template/runtimes/dip-sender/build.rs b/dip-template/runtimes/dip-sender/build.rs new file mode 100644 index 0000000000..a394382833 --- /dev/null +++ b/dip-template/runtimes/dip-sender/build.rs @@ -0,0 +1,27 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/dip-template/runtimes/dip-sender/src/dip.rs b/dip-template/runtimes/dip-sender/src/dip.rs new file mode 100644 index 0000000000..a31ed8b39f --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/dip.rs @@ -0,0 +1,70 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use dip_support::VersionedIdentityProofAction; +use pallet_dip_sender::traits::{ + DefaultIdentityProofGenerator, DefaultIdentityProvider, TxBuilder, XcmRouterDispatcher, +}; +use parity_scale_codec::{Decode, Encode}; +use xcm::{latest::MultiLocation, DoubleEncoded}; + +use crate::{DidIdentifier, Runtime, RuntimeEvent, XcmRouter}; + +#[derive(Encode, Decode)] +enum ReceiverParachainCalls { + #[codec(index = 50)] + DipReceiver(ReceiverParachainDipReceiverCalls), +} + +#[derive(Encode, Decode)] +enum ReceiverParachainDipReceiverCalls { + #[codec(index = 0)] + ProcessIdentityAction(VersionedIdentityProofAction), +} + +pub struct ReceiverParachainTxBuilder; +impl TxBuilder for ReceiverParachainTxBuilder { + type Error = (); + + fn build( + _dest: MultiLocation, + action: VersionedIdentityProofAction, + ) -> Result, Self::Error> { + let double_encoded: DoubleEncoded<()> = + ReceiverParachainCalls::DipReceiver(ReceiverParachainDipReceiverCalls::ProcessIdentityAction(action)) + .encode() + .into(); + Ok(double_encoded) + } +} + +impl pallet_dip_sender::Config for Runtime { + type Identifier = DidIdentifier; + // TODO: Change with right one + type Identity = u32; + // TODO: Change with right one + type IdentityProofDispatcher = XcmRouterDispatcher; + // TODO: Change with right one + type IdentityProofGenerator = DefaultIdentityProofGenerator; + // TODO: Change with right one + type IdentityProvider = DefaultIdentityProvider; + // TODO: Change with right one + type ProofOutput = [u8; 32]; + type RuntimeEvent = RuntimeEvent; + type TxBuilder = ReceiverParachainTxBuilder; +} diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs new file mode 100644 index 0000000000..d81684c1e7 --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -0,0 +1,528 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +use cumulus_pallet_parachain_system::{ + register_validate_block, ParachainSetCode, RelayChainStateProof, RelayNumberStrictlyIncreases, +}; +use cumulus_primitives_core::CollationInfo; +use cumulus_primitives_timestamp::InherentDataProvider; +use did::{DidRawOrigin, EnsureDidOrigin}; +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8, Everything}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + IdentityFee, Weight, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + ChainContext, EnsureRoot, +}; +use pallet_balances::AccountData; +use pallet_collator_selection::IdentityCollator; +use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; +use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::SlotDuration; +use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; +use sp_inherents::{CheckInherentsResult, InherentData}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::{prelude::*, time::Duration}; +use sp_version::RuntimeVersion; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +mod dip; +mod xcm_config; +pub use crate::{dip::*, xcm_config::*}; + +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type Address = MultiAddress; +pub type Balance = u128; +pub type Block = generic::Block; +pub type BlockNumber = u32; +pub type DidIdentifier = AccountId; +pub type Hash = sp_core::H256; +pub type Header = generic::Header; +pub type Index = u32; +pub type Signature = MultiSignature; + +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type Executive = frame_executive::Executive, Runtime, AllPalletsWithSystem>; +pub type NodeBlock = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; + +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = UNIT / 1_000; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = NodeBlock, + UncheckedExtrinsic = UncheckedExtrinsic, + { + // System + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + + // Money + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Collators + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + DmpQueue: cumulus_pallet_dmp_queue = 31, + PolkadotXcm: pallet_xcm = 32, + CumulusXcm: cumulus_pallet_xcm = 33, + + // DID + Did: did = 40, + + // DIP + DipSender: pallet_dip_sender = 50, + } +); + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("dip-sender-runtime-template"), + impl_name: create_runtime_str!("dip-sender-runtime-template"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents(block: &Block, relay_state_proof: &RelayChainStateProof) -> CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + InherentDataProvider::from_relay_chain_slot_and_duration(relay_chain_slot, Duration::from_secs(6)) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} + +pub const SS58_PREFIX: u16 = 100; +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +impl frame_system::Config for Runtime { + type AccountData = AccountData; + type AccountId = AccountId; + type BaseCallFilter = Everything; + type BlockHashCount = ConstU32<256>; + type BlockLength = RuntimeBlockLength; + type BlockNumber = BlockNumber; + type BlockWeights = RuntimeBlockWeights; + type DbWeight = RocksDbWeight; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Header = generic::Header; + type Index = Index; + type Lookup = AccountIdLookup; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = ParachainSetCode; + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16; + type SystemWeightInfo = (); + type Version = Version; +} + +parameter_types! { + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; + type DmpMessageHandler = DmpQueue; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = XcmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type ReservedXcmpWeight = ReservedXcmpWeight; + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainInfo; + type XcmpMessageHandler = XcmpQueue; +} + +impl pallet_timestamp::Config for Runtime { + type MinimumPeriod = ConstU64<{ MILLISECS_PER_BLOCK / 2 }>; + type Moment = u64; + type OnTimestampSet = Aura; + type WeightInfo = (); +} + +impl parachain_info::Config for Runtime {} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ConstU128; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = CurrencyAdapter; + type FeeMultiplierUpdate = (); + type LengthToFee = IdentityFee; + type OperationalFeeMultiplier = ConstU8<1>; + type RuntimeEvent = RuntimeEvent; + type WeightToFee = IdentityFee; +} + +impl pallet_authorship::Config for Runtime { + type EventHandler = (CollatorSelection,); + type FindAuthor = FindAccountFromAuthorIndex; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); +} + +impl pallet_collator_selection::Config for Runtime { + type Currency = Balances; + type PotId = PotId; + type KickThreshold = ConstU32<{ 6 * HOURS }>; + type MaxCandidates = ConstU32<1_000>; + type MaxInvulnerables = ConstU32<100>; + type MinCandidates = ConstU32<5>; + type RuntimeEvent = RuntimeEvent; + type UpdateOrigin = EnsureRoot; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +impl pallet_session::Config for Runtime { + type Keys = SessionKeys; + type NextSessionRotation = PeriodicSessions, ConstU32<0>>; + type RuntimeEvent = RuntimeEvent; + type SessionHandler = ::KeyTypeIdProviders; + type SessionManager = CollatorSelection; + type ShouldEndSession = PeriodicSessions, ConstU32<0>>; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { + fn derive_verification_key_relationship(&self) -> did::DeriveDidCallKeyRelationshipResult { + Ok(did::DidVerificationKeyRelationship::Authentication) + } + + #[cfg(feature = "runtime-benchmarks")] + fn get_call_for_did_call_benchmark() -> Self { + RuntimeCall::System(frame_system::Call::remark { + remark: b"test-remark".to_vec(), + }) + } +} + +parameter_types! { + #[derive(Debug, Clone, Eq, PartialEq)] + pub const MaxTotalKeyAgreementKeys: u32 = 1; +} + +impl did::Config for Runtime { + type Currency = Balances; + type Deposit = ConstU128; + type DidIdentifier = DidIdentifier; + type EnsureOrigin = EnsureDidOrigin; + type Fee = ConstU128; + type FeeCollector = (); + type MaxBlocksTxValidity = ConstU32; + type MaxNewKeyAgreementKeys = ConstU32<1>; + type MaxNumberOfServicesPerDid = ConstU32<1>; + type MaxNumberOfTypesPerService = ConstU32<1>; + type MaxNumberOfUrlsPerService = ConstU32<1>; + type MaxPublicKeysPerDid = ConstU32<4>; + type MaxServiceIdLength = ConstU32<100>; + type MaxServiceTypeLength = ConstU32<100>; + type MaxServiceUrlLength = ConstU32<100>; + type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; + type OriginSuccess = DidRawOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type WeightInfo = (); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> SlotDuration { + SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } +} diff --git a/dip-template/runtimes/dip-sender/src/xcm_config.rs b/dip-template/runtimes/dip-sender/src/xcm_config.rs new file mode 100644 index 0000000000..d73c126189 --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/xcm_config.rs @@ -0,0 +1,130 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_primitives_utility::ParentAsUmp; +use frame_support::{ + parameter_types, + traits::{ConstU32, Nothing}, + weights::{IdentityFee, Weight}, +}; +use frame_system::EnsureRoot; +use pallet_xcm::TestWeightInfo; +use xcm::latest::prelude::*; +use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; +use xcm_executor::XcmExecutor; + +use crate::{ + AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, XcmpQueue, +}; + +parameter_types! { + pub HereLocation: MultiLocation = Junctions::Here.into(); + pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +pub type XcmRouter = (ParentAsUmp, XcmpQueue); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = (); + type AssetTransactor = (); + type AssetTrap = (); + type Barrier = (); + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = ConstU32<64>; + type MessageExporter = (); + type OriginConverter = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Nothing; + type SubscriptionService = (); + type UniversalAliases = Nothing; + type UniversalLocation = UniversalLocation; + type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; + type Weigher = FixedWeightBounds>; + type XcmSender = XcmRouter; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type ChannelInfo = ParachainSystem; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = (); + type ExecuteOverweightOrigin = EnsureRoot; + type PriceForSiblingDelivery = (); + type RuntimeEvent = RuntimeEvent; + type VersionWrapper = (); + type WeightInfo = (); + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type ExecuteOverweightOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +const MAX_INSTRUCTIONS: u32 = 100; + +parameter_types! { + pub RelayNetwork: Option = None; +} +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = (); + type TrustedLockers = (); + type UniversalLocation = UniversalLocation; + type Weigher = FixedWeightBounds>; + type WeightInfo = TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Nothing; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml new file mode 100644 index 0000000000..26c770a855 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -0,0 +1,38 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "dip-templates-xmc-tests" +description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) sender and receiver templates." + +[dev-dependencies] +cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } + +[dependencies] +parity-scale-codec = {workspace = true, features = ["std", "derive"]} +dip-receiver-runtime-template = { workspace = true, features = ["std"] } +dip-sender-runtime-template = { workspace = true, features = ["std"] } +frame-support = { workspace = true, features = ["std"] } +frame-system = { workspace = true, features = ["std"] } +pallet-balances = { workspace = true, features = ["std"] } +parachain-info = { workspace = true, features = ["std"] } +polkadot-parachain = { workspace = true, features = ["std"] } +polkadot-primitives = { workspace = true, features = ["std"] } +polkadot-runtime-parachains = { workspace = true, features = ["std"] } +rococo-runtime = { workspace = true, features = ["std"] } +scale-info = { workspace = true, features = ["std"] } +sp-io = { workspace = true, features = ["std"] } +sp-runtime = { workspace = true, features = ["std"] } +xcm = { workspace = true, features = ["std"] } +xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } +xcm-executor = { workspace = true, features = ["std"] } + +[features] +runtime-benchmarks = [ + "rococo-runtime/runtime-benchmarks" +] diff --git a/dip-template/runtimes/xcm-tests/src/lib.rs b/dip-template/runtimes/xcm-tests/src/lib.rs new file mode 100644 index 0000000000..9eb8ed5de7 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/lib.rs @@ -0,0 +1,59 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use xcm_emulator::{decl_test_network, decl_test_parachain}; + +use crate::relay::RococoChain; + +mod para; +mod relay; + +#[cfg(test)] +mod tests; + +decl_test_parachain! { + pub struct SenderParachain { + Runtime = para::sender::Runtime, + RuntimeOrigin = para::sender::RuntimeOrigin, + XcmpMessageHandler = para::sender::XcmpQueue, + DmpMessageHandler = para::sender::DmpQueue, + new_ext = para::sender::para_ext(), + } +} + +decl_test_parachain! { + pub struct ReceiverParachain { + Runtime = para::receiver::Runtime, + RuntimeOrigin = para::receiver::RuntimeOrigin, + XcmpMessageHandler = para::receiver::XcmpQueue, + DmpMessageHandler = para::receiver::DmpQueue, + new_ext = para::receiver::para_ext(), + } +} + +decl_test_network! { + pub struct Network { + relay_chain = RococoChain, + parachains = vec![ + // TODO: Change when and if the macro will allow arbitrary expressions. + // Until then, these have to match the PARA_ID consts in the para submodules. + (2_000, SenderParachain), + (2_001, ReceiverParachain), + ], + } +} diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs new file mode 100644 index 0000000000..ca30e62ba9 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -0,0 +1,107 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{_Messenger, _hrmp_channel_parachain_inherent_data, _process_messages}; +use frame_support::traits::GenesisBuild; +use sp_io::TestExternalities; +use xcm_emulator::decl_test_parachain; + +pub(super) mod sender { + pub(crate) use dip_sender_runtime_template::{DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + + use super::*; + + pub const PARA_ID: u32 = 2_000; + + pub(crate) fn para_ext() -> TestExternalities { + use dip_sender_runtime_template::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_info_config = parachain_info::GenesisConfig { + parachain_id: PARA_ID.into(), + }; + + >::assimilate_storage(¶chain_info_config, &mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } + + decl_test_parachain! { + pub struct SenderParachain { + Runtime = Runtime, + RuntimeOrigin = RuntimeOrigin, + XcmpMessageHandler = XcmpQueue, + DmpMessageHandler = DmpQueue, + new_ext = para_ext(), + } + } +} + +pub(super) mod receiver { + pub(crate) use dip_receiver_runtime_template::{ + AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, + }; + + use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; + use xcm_executor::traits::Convert; + + use super::*; + + pub const PARA_ID: u32 = 2_001; + const INITIAL_BALANCE: Balance = 1_000_000_000; + + pub(crate) fn sender_parachain_account() -> AccountId { + AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(sender::PARA_ID))).into()) + .expect("Conversion of account from sender parachain to receiver parachain should not fail.") + } + + pub(crate) fn para_ext() -> TestExternalities { + use dip_receiver_runtime_template::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_info_config = parachain_info::GenesisConfig { + parachain_id: PARA_ID.into(), + }; + + >::assimilate_storage(¶chain_info_config, &mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(sender_parachain_account(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } +} diff --git a/dip-template/runtimes/xcm-tests/src/relay.rs b/dip-template/runtimes/xcm-tests/src/relay.rs new file mode 100644 index 0000000000..2f60171149 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/relay.rs @@ -0,0 +1,91 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{_Messenger, _para_ids, _process_messages}; +use frame_support::{traits::GenesisBuild, weights::Weight}; +use polkadot_primitives::{runtime_api::runtime_decl_for_ParachainHost::ParachainHostV4, BlockNumber}; +use polkadot_runtime_parachains::configuration::HostConfiguration; +use rococo_runtime::{xcm_config::XcmConfig, Runtime}; +use sp_io::TestExternalities; +use xcm_emulator::decl_test_relay_chain; + +fn default_parachains_host_configuration() -> HostConfiguration { + use polkadot_primitives::v2::{MAX_CODE_SIZE, MAX_POV_SIZE}; + + HostConfiguration { + minimum_validation_upgrade_delay: 5, + validation_upgrade_cooldown: 10u32, + validation_upgrade_delay: 10, + code_retention_period: 1200, + max_code_size: MAX_CODE_SIZE, + max_pov_size: MAX_POV_SIZE, + max_head_data_size: 32 * 1024, + group_rotation_frequency: 20, + chain_availability_period: 4, + thread_availability_period: 4, + max_upward_queue_count: 8, + max_upward_queue_size: 1024 * 1024, + max_downward_message_size: 1024, + ump_service_total_weight: Weight::from_ref_time(4 * 1_000_000_000), + max_upward_message_size: 50 * 1024, + max_upward_message_num_per_candidate: 5, + hrmp_sender_deposit: 0, + hrmp_recipient_deposit: 0, + hrmp_channel_max_capacity: 8, + hrmp_channel_max_total_size: 8 * 1024, + hrmp_max_parachain_inbound_channels: 4, + hrmp_max_parathread_inbound_channels: 4, + hrmp_channel_max_message_size: 1024 * 1024, + hrmp_max_parachain_outbound_channels: 4, + hrmp_max_parathread_outbound_channels: 4, + hrmp_max_message_num_per_candidate: 5, + dispute_period: 6, + no_show_slots: 2, + n_delay_tranches: 25, + needed_approvals: 2, + relay_vrf_modulo_samples: 2, + zeroth_delay_tranche_width: 0, + ..Default::default() + } +} + +fn relay_ext() -> TestExternalities { + use rococo_runtime::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + polkadot_runtime_parachains::configuration::GenesisConfig:: { + config: default_parachains_host_configuration(), + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +decl_test_relay_chain! { + pub struct RococoChain { + Runtime = Runtime, + XcmConfig = XcmConfig, + new_ext = relay_ext(), + } +} diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs new file mode 100644 index 0000000000..037561708c --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -0,0 +1,75 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use super::*; + +use dip_receiver_runtime_template::{ + AccountId as ReceiverAccountId, DidIdentifier as ReceiverDidIdentifier, DipReceiver, +}; +use dip_sender_runtime_template::DipSender; + +use frame_support::{assert_ok, weights::Weight}; +use frame_system::RawOrigin; +use xcm::latest::{ + Junction::Parachain, + Junctions::{Here, X1}, + ParentThen, +}; +use xcm_emulator::TestExt; + +#[test] +fn commit_identity() { + Network::reset(); + + ReceiverParachain::execute_with(|| { + use dip_receiver_runtime_template::Balances; + use para::receiver::sender_parachain_account; + + let sender_balance = Balances::free_balance(sender_parachain_account()); + println!("Sender balance: {:?}", sender_balance); + }); + + // 1. Send identity proof from DIP sender to DIP receiver. + SenderParachain::execute_with(|| { + assert_ok!(DipSender::commit_identity( + RawOrigin::Signed(ReceiverAccountId::from([0u8; 32])).into(), + ReceiverDidIdentifier::from([0u8; 32]), + Box::new(ParentThen(X1(Parachain(para::receiver::PARA_ID))).into()), + Box::new((Here, 1_000_000_000).into()), + Weight::from_ref_time(4_000), + )); + }); + // 2. Verify that the proof has made it to the DIP receiver. + ReceiverParachain::execute_with(|| { + use cumulus_pallet_xcmp_queue::Event as XcmpEvent; + use dip_receiver_runtime_template::{RuntimeEvent, System}; + + // 2.1 Verify that there was no XCM error. + assert!(!System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XcmpQueue(XcmpEvent::Fail { + error: _, + message_hash: _, + weight: _ + }) + ))); + // 2.2 Verify the proof digest is the same that was sent. + let details = DipReceiver::identity_proofs(dip_sender_runtime_template::AccountId::from([0u8; 32])); + assert_eq!(details, Some([0u8; 32])); + }); +} diff --git a/nodes/parachain/Cargo.toml b/nodes/parachain/Cargo.toml index b64cb66bac..dff28e2833 100644 --- a/nodes/parachain/Cargo.toml +++ b/nodes/parachain/Cargo.toml @@ -70,7 +70,6 @@ substrate-frame-rpc-system.workspace = true cumulus-client-cli.workspace = true cumulus-client-consensus-aura.workspace = true cumulus-client-consensus-common.workspace = true -cumulus-client-network.workspace = true cumulus-client-service.workspace = true cumulus-primitives-core = {workspace = true, features = ["std"]} cumulus-primitives-parachain-inherent = {workspace = true, features = ["std"]} diff --git a/pallets/attestation/Cargo.toml b/pallets/attestation/Cargo.toml index 9b83cca7b1..d32dce404b 100644 --- a/pallets/attestation/Cargo.toml +++ b/pallets/attestation/Cargo.toml @@ -61,7 +61,6 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks" ] std = [ - "parity-scale-codec/std", "ctype/std", "frame-benchmarking?/std", "frame-support/std", @@ -69,6 +68,7 @@ std = [ "kilt-support/std", "log/std", "pallet-balances/std", + "parity-scale-codec/std", "scale-info/std", "sp-core/std", "sp-io/std", diff --git a/pallets/ctype/Cargo.toml b/pallets/ctype/Cargo.toml index 555640af68..e1f53ca326 100644 --- a/pallets/ctype/Cargo.toml +++ b/pallets/ctype/Cargo.toml @@ -55,13 +55,13 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "kilt-support/std", "log/std", "pallet-balances/std", + "parity-scale-codec/std", "scale-info/std", "sp-core/std", "sp-io/std", diff --git a/pallets/delegation/Cargo.toml b/pallets/delegation/Cargo.toml index a2df28e947..073e88f451 100644 --- a/pallets/delegation/Cargo.toml +++ b/pallets/delegation/Cargo.toml @@ -68,7 +68,6 @@ runtime-benchmarks = [ ] std = [ "attestation/std", - "parity-scale-codec/std", "ctype/std", "frame-benchmarking?/std", "frame-support/std", @@ -76,6 +75,7 @@ std = [ "kilt-support/std", "log/std", "pallet-balances/std", + "parity-scale-codec/std", "public-credentials/std", "scale-info/std", "sp-core/std", diff --git a/pallets/did/Cargo.toml b/pallets/did/Cargo.toml index 2ec102139f..dc1193ffd7 100644 --- a/pallets/did/Cargo.toml +++ b/pallets/did/Cargo.toml @@ -33,9 +33,9 @@ kilt-support.workspace = true ctype = {workspace = true, optional = true} # Substrate dependencies -parity-scale-codec = {workspace = true, features = ["derive"]} frame-support.workspace = true frame-system.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sp-core.workspace = true sp-io.workspace = true @@ -64,13 +64,13 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "ctype/std", "frame-support/std", "frame-system/std", "kilt-support/std", "log/std", "pallet-balances/std", + "parity-scale-codec/std", "scale-info/std", "sp-core/std", "sp-io/std", diff --git a/pallets/pallet-did-lookup/Cargo.toml b/pallets/pallet-did-lookup/Cargo.toml index 323b369d1b..b2ea3a32a7 100644 --- a/pallets/pallet-did-lookup/Cargo.toml +++ b/pallets/pallet-did-lookup/Cargo.toml @@ -27,10 +27,10 @@ test-log = "0.2.11" # External dependencies base58.workspace = true blake2 = {version = "0.10.6", default-features = false} -parity-scale-codec = {workspace = true, features = ["derive"]} hex.workspace = true libsecp256k1 = {workspace = true, features = ["hmac"]} log.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sha3.workspace = true @@ -62,7 +62,6 @@ runtime-benchmarks = [ ] std = [ "blake2/std", - "parity-scale-codec/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", @@ -70,6 +69,7 @@ std = [ "kilt-support/std", "log/std", "libsecp256k1/std", + "parity-scale-codec/std", "scale-info/std", "sha3/std", "serde", diff --git a/pallets/pallet-dip-receiver/Cargo.toml b/pallets/pallet-dip-receiver/Cargo.toml new file mode 100644 index 0000000000..8e7e5a1ac5 --- /dev/null +++ b/pallets/pallet-dip-receiver/Cargo.toml @@ -0,0 +1,41 @@ +[package] +authors.workspace = true +description = "Pallet enabling receiving identity information from providers via the pallet-dip-provider pallet." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-dip-receiver" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +cumulus-pallet-xcm.workspace = true +dip-support.workspace = true +frame-support.workspace = true +frame-system.workspace = true +kilt-support.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "cumulus-pallet-xcm/std", + "dip-support/std", + "frame-support/std", + "frame-system/std", + "kilt-support/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs new file mode 100644 index 0000000000..551b6bc80a --- /dev/null +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -0,0 +1,143 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +mod origin; +pub mod traits; + +pub use crate::{origin::*, pallet::*}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use cumulus_pallet_xcm::ensure_sibling_para; + use frame_support::{dispatch::Dispatchable, pallet_prelude::*, Twox64Concat}; + use frame_system::pallet_prelude::*; + use sp_std::boxed::Box; + + use dip_support::{latest::IdentityProofAction, VersionedIdentityProof, VersionedIdentityProofAction}; + + use crate::traits::IdentityProofVerifier; + + pub type VersionedIdentityProofOf = + VersionedIdentityProof<::ProofLeafKey, ::ProofLeafValue>; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + // TODO: Store also additional details received by the provider. + #[pallet::storage] + #[pallet::getter(fn identity_proofs)] + pub(crate) type IdentityProofs = + StorageMap<_, Twox64Concat, ::Identifier, ::ProofDigest>; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Identifier: Parameter + MaxEncodedLen; + type ProofLeafKey: Parameter; + type ProofLeafValue: Parameter; + type ProofDigest: Parameter + MaxEncodedLen; + type ProofVerifier: IdentityProofVerifier< + ProofDigest = Self::ProofDigest, + LeafKey = Self::ProofLeafKey, + LeafValue = Self::ProofLeafValue, + >; + type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeOrigin: From> + + From<::RuntimeOrigin> + + Into::RuntimeOrigin>>; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + IdentityInfoDeleted(T::Identifier), + IdentityInfoUpdated(T::Identifier, T::ProofDigest), + } + + #[pallet::error] + pub enum Error { + Dispatch, + IdentityNotFound, + InvalidProof, + } + + // The new origin other pallets can use. + #[pallet::origin] + pub type Origin = DipOrigin<::Identifier, ::AccountId>; + + // TODO: Benchmarking + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn process_identity_action( + origin: OriginFor, + action: VersionedIdentityProofAction, + ) -> DispatchResult { + ensure_sibling_para(::RuntimeOrigin::from(origin))?; + + let event = match action { + VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { + IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); + Event::::IdentityInfoUpdated(identifier, proof) + } + VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { + IdentityProofs::::remove(&identifier); + Event::::IdentityInfoDeleted(identifier) + } + }; + + Self::deposit_event(event); + + Ok(()) + } + + // TODO: Replace with a SignedExtra. + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn dispatch_as( + origin: OriginFor, + identifier: T::Identifier, + proof: VersionedIdentityProofOf, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let submitter = ensure_signed(origin)?; + let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let _ = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) + .map_err(|_| Error::::InvalidProof)?; + // TODO: Proper DID signature verification (and cross-chain replay protection) + let did_origin = DipOrigin { + identifier, + account_address: submitter, + }; + // TODO: Use dispatch info for weight calculation + let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; + Ok(()) + } + } +} diff --git a/pallets/pallet-dip-receiver/src/origin.rs b/pallets/pallet-dip-receiver/src/origin.rs new file mode 100644 index 0000000000..e24ed1aedb --- /dev/null +++ b/pallets/pallet-dip-receiver/src/origin.rs @@ -0,0 +1,79 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{traits::EnsureOrigin, RuntimeDebug}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::marker::PhantomData; + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct DipOrigin { + pub identifier: Identifier, + pub account_address: AccountId, +} + +pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId)>); + +#[cfg(not(feature = "runtime-benchmarks"))] +impl EnsureOrigin for EnsureDipOrigin +where + OuterOrigin: From> + Into, OuterOrigin>>, +{ + type Success = DipOrigin; + + fn try_origin(o: OuterOrigin) -> Result { + o.into() + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl EnsureOrigin for EnsureDipOrigin +where + OuterOrigin: From> + Into, OuterOrigin>>, + // Additional trait bounds only valid when benchmarking + Identifier: From<[u8; 32]>, + AccountId: From<[u8; 32]>, +{ + type Success = DipOrigin; + + fn try_origin(o: OuterOrigin) -> Result { + o.into() + } + + fn try_successful_origin() -> Result { + Ok(OuterOrigin::from(DipOrigin { + identifier: Identifier::from([0u8; 32]), + account_address: AccountId::from([0u8; 32]), + })) + } +} + +impl kilt_support::traits::CallSources + for DipOrigin +where + Identifier: Clone, + AccountId: Clone, +{ + fn sender(&self) -> AccountId { + self.account_address.clone() + } + + fn subject(&self) -> Identifier { + self.identifier.clone() + } +} diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs new file mode 100644 index 0000000000..460dc48b0b --- /dev/null +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -0,0 +1,52 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use dip_support::VersionedIdentityProof; +use sp_std::marker::PhantomData; + +pub trait IdentityProofVerifier { + type ProofDigest; + type LeafKey; + type LeafValue; + type VerificationResult; + type Error; + + fn verify_proof_against_digest( + proof: VersionedIdentityProof, + digest: Self::ProofDigest, + ) -> Result; +} + +// Always returns success. +pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, LeafKey, LeafValue)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier +{ + type ProofDigest = ProofDigest; + type Error = (); + type LeafKey = LeafKey; + type LeafValue = LeafValue; + type VerificationResult = (); + + fn verify_proof_against_digest( + _proof: VersionedIdentityProof, + _digest: Self::ProofDigest, + ) -> Result { + Ok(()) + } +} diff --git a/pallets/pallet-dip-sender/Cargo.toml b/pallets/pallet-dip-sender/Cargo.toml new file mode 100644 index 0000000000..a5d54cf134 --- /dev/null +++ b/pallets/pallet-dip-sender/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors.workspace = true +description = "Pallet enabling to send some form of identity information to a specified destination." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-dip-sender" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +dip-support.workspace = true +frame-support.workspace = true +frame-system.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true +xcm.workspace = true + +[features] +default = ["std"] +std = [ + "dip-support/std", + "frame-support/std", + "frame-system/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-std/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] diff --git a/pallets/pallet-dip-sender/src/lib.rs b/pallets/pallet-dip-sender/src/lib.rs new file mode 100644 index 0000000000..57d3c38fa0 --- /dev/null +++ b/pallets/pallet-dip-sender/src/lib.rs @@ -0,0 +1,129 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod traits; + +pub use crate::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use frame_support::{pallet_prelude::*, weights::Weight}; + use frame_system::pallet_prelude::*; + use sp_std::{boxed::Box, fmt::Debug}; + use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; + + use dip_support::{v1::IdentityProofAction, VersionedIdentityProofAction}; + + use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; + + pub type IdentityProofActionOf = IdentityProofAction<::Identifier, ::ProofOutput>; + pub type VersionedIdentityProofActionOf = + VersionedIdentityProofAction<::Identifier, ::ProofOutput>; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::config] + pub trait Config: frame_system::Config { + type Identifier: Parameter; + type Identity; + type ProofOutput: Clone + Eq + Debug; + type IdentityProofGenerator: IdentityProofGenerator; + type IdentityProofDispatcher: IdentityProofDispatcher; + type IdentityProvider: IdentityProvider; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type TxBuilder: TxBuilder; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + // #[pallet::storage] + // #[pallet::getter(fn destination_info)] + // pub type DestinationInfos = StorageMap<_, Blake2_128Concat, NetworkId, + // ()>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + IdentityInfoDispatched(VersionedIdentityProofActionOf, Box), + } + + #[pallet::error] + pub enum Error { + BadVersion, + Dispatch, + IdentityNotFound, + IdentityProofGeneration, + Predispatch, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + // TODO: Update weight + #[pallet::weight(0)] + pub fn commit_identity( + origin: OriginFor, + identifier: T::Identifier, + destination: Box, + asset: Box, + weight: Weight, + ) -> DispatchResult { + // TODO: Charge the dispatcher based on the destination weight configuration + ensure_signed(origin)?; + + let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; + let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { + Ok(Some((identity, _))) => { + let identity_proof = T::IdentityProofGenerator::generate_proof(&identifier, &identity) + .map_err(|_| Error::::IdentityProofGeneration)?; + Ok(IdentityProofAction::Updated(identifier, identity_proof, ())) + } + Ok(None) => Ok(IdentityProofAction::Deleted(identifier)), + Err(_) => Err(Error::::IdentityNotFound), + }?; + // TODO: Add correct version creation based on lookup (?) + let versioned_action = VersionedIdentityProofAction::V1(action); + + let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; + + let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( + versioned_action.clone(), + asset, + weight, + destination, + ) + .map_err(|_| Error::::Predispatch)?; + + // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the + // fee, in addition to the cost on the target chain. + T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; + + Self::deposit_event(Event::IdentityInfoDispatched(versioned_action, Box::new(destination))); + Ok(()) + } + } +} diff --git a/pallets/pallet-dip-sender/src/traits.rs b/pallets/pallet-dip-sender/src/traits.rs new file mode 100644 index 0000000000..097bd6a37f --- /dev/null +++ b/pallets/pallet-dip-sender/src/traits.rs @@ -0,0 +1,192 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use dip_support::VersionedIdentityProofAction; +use xcm::{latest::prelude::*, DoubleEncoded}; + +pub use identity_generation::*; +pub mod identity_generation { + + pub trait IdentityProofGenerator { + type Error; + + fn generate_proof(identifier: &Identifier, identity: &Identity) -> Result; + } + + // Implement the `IdentityProofGenerator` by returning the `Default` value for + // the `Output` type. + pub struct DefaultIdentityProofGenerator; + + impl IdentityProofGenerator + for DefaultIdentityProofGenerator + where + Output: Default, + { + type Error = (); + + fn generate_proof(_identifier: &Identifier, _identity: &Identity) -> Result { + Ok(Output::default()) + } + } +} + +pub use identity_dispatch::*; +pub mod identity_dispatch { + use super::*; + + use frame_support::weights::Weight; + use parity_scale_codec::Encode; + use sp_std::{marker::PhantomData, vec}; + + pub trait IdentityProofDispatcher { + type PreDispatchOutput; + type Error; + + fn pre_dispatch>( + action: VersionedIdentityProofAction, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error>; + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error>; + } + + // Returns `Ok` without doing anything. + pub struct NullIdentityProofDispatcher; + + impl IdentityProofDispatcher + for NullIdentityProofDispatcher + { + type PreDispatchOutput = (); + type Error = (); + + fn pre_dispatch<_B>( + _action: VersionedIdentityProofAction, + _asset: MultiAsset, + _weight: Weight, + _destination: MultiLocation, + ) -> Result<((), MultiAssets), Self::Error> { + Ok(((), MultiAssets::default())) + } + + fn dispatch(_pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Ok(()) + } + } + + // Dispatcher using a type implementing the `SendXcm` trait. + // It properly encodes the `Transact` operation, then delegates everything else + // to the sender, similarly to what the XCM pallet's `send` extrinsic does. + pub struct XcmRouterDispatcher( + PhantomData<(Router, Identifier, ProofOutput, Details)>, + ); + + impl IdentityProofDispatcher + for XcmRouterDispatcher + where + Router: SendXcm, + Identifier: Encode, + ProofOutput: Encode, + { + type PreDispatchOutput = Router::Ticket; + type Error = SendError; + + fn pre_dispatch>( + action: VersionedIdentityProofAction, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { + // TODO: Replace with proper error handling + let dest_tx = Builder::build(destination, action) + .map_err(|_| ()) + .expect("Failed to build call"); + + // TODO: Set an error handler and an appendix to refund any leftover funds to + // the sender parachain sovereign account. + let operation = [vec![ + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + // TODO: Configurable weight limit? + weight_limit: Unlimited, + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + ]] + .concat(); + let op = Xcm(operation); + Router::validate(&mut Some(destination), &mut Some(op)) + } + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Router::deliver(pre_output).map(|_| ()) + } + } +} + +pub use identity_provision::*; +pub mod identity_provision { + + pub trait IdentityProvider { + type Error; + + fn retrieve(identifier: &Identifier) -> Result, Self::Error>; + } + + // Return the `Default` value if `Identity` adn `Details` both implement it. + pub struct DefaultIdentityProvider; + + impl IdentityProvider for DefaultIdentityProvider + where + Identity: Default, + Details: Default, + { + type Error = (); + + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + Ok(Some((Identity::default(), Details::default()))) + } + } + + // Always return `None`. Might be useful for tests. + pub struct NoneIdentityProvider; + + impl IdentityProvider for NoneIdentityProvider { + type Error = (); + + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + Ok(None) + } + } +} + +// Given a destination and an identity action, creates and encodes the proper +// `Transact` call. +pub trait TxBuilder { + type Error; + + fn build( + dest: MultiLocation, + action: VersionedIdentityProofAction, + ) -> Result, Self::Error>; +} diff --git a/pallets/pallet-inflation/Cargo.toml b/pallets/pallet-inflation/Cargo.toml index 59dcc2b0d8..8664de6b4a 100644 --- a/pallets/pallet-inflation/Cargo.toml +++ b/pallets/pallet-inflation/Cargo.toml @@ -41,10 +41,10 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "parity-scale-codec/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/pallets/pallet-web3-names/Cargo.toml b/pallets/pallet-web3-names/Cargo.toml index 844bd1fc29..23c5e26408 100644 --- a/pallets/pallet-web3-names/Cargo.toml +++ b/pallets/pallet-web3-names/Cargo.toml @@ -50,10 +50,10 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "parity-scale-codec/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/pallets/parachain-staking/Cargo.toml b/pallets/parachain-staking/Cargo.toml index 7eea424f05..2835f3e0ba 100644 --- a/pallets/parachain-staking/Cargo.toml +++ b/pallets/parachain-staking/Cargo.toml @@ -53,7 +53,6 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", @@ -62,6 +61,7 @@ std = [ "pallet-authorship/std", "pallet-balances/std", "pallet-session/std", + "parity-scale-codec/std", "scale-info/std", "serde", "sp-runtime/std", diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 8ae80c73f0..f6c49d6463 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -75,7 +75,6 @@ runtime-benchmarks = [ ] std = [ "attestation/std", - "parity-scale-codec/std", "ctype/std", "cumulus-primitives-core/std", "frame-support/std", @@ -88,6 +87,7 @@ std = [ "pallet-membership/std", "pallet-transaction-payment/std", "parachain-staking/std", + "parity-scale-codec/std", "polkadot-parachain/std", "public-credentials/std", "scale-info/std", diff --git a/runtimes/peregrine/Cargo.toml b/runtimes/peregrine/Cargo.toml index e08459be90..7ee2773686 100644 --- a/runtimes/peregrine/Cargo.toml +++ b/runtimes/peregrine/Cargo.toml @@ -150,7 +150,6 @@ runtime-benchmarks = [ ] std = [ "attestation/std", - "parity-scale-codec/std", "ctype/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -202,6 +201,7 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachain-staking/std", + "parity-scale-codec/std", "public-credentials/std", "runtime-common/std", "scale-info/std", diff --git a/runtimes/spiritnet/Cargo.toml b/runtimes/spiritnet/Cargo.toml index 4a5d0fe473..7ef754b7eb 100644 --- a/runtimes/spiritnet/Cargo.toml +++ b/runtimes/spiritnet/Cargo.toml @@ -149,7 +149,6 @@ runtime-benchmarks = [ ] std = [ "attestation/std", - "parity-scale-codec/std", "ctype/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -199,6 +198,7 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachain-staking/std", + "parity-scale-codec/std", "public-credentials/std", "runtime-common/std", "scale-info/std", diff --git a/runtimes/standalone/Cargo.toml b/runtimes/standalone/Cargo.toml index 2577c17521..bc6e9b42e2 100644 --- a/runtimes/standalone/Cargo.toml +++ b/runtimes/standalone/Cargo.toml @@ -102,7 +102,6 @@ runtime-benchmarks = [ std = [ "frame-benchmarking?/std", "attestation/std", - "parity-scale-codec/std", "ctype/std", "delegation/std", "did/std", @@ -131,6 +130,7 @@ std = [ "pallet-transaction-payment-rpc-runtime-api/std", "pallet-utility/std", "pallet-web3-names/std", + "parity-scale-codec/std", "public-credentials/std", "runtime-common/std", "scale-info/std", diff --git a/support/Cargo.toml b/support/Cargo.toml index 5a606fa9fb..2a2d566095 100644 --- a/support/Cargo.toml +++ b/support/Cargo.toml @@ -38,9 +38,9 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", ] std = [ - "parity-scale-codec/std", "frame-support/std", "frame-system/std", + "parity-scale-codec/std", "scale-info/std", "serde", "sp-core/std", From 95e18f43b5a10729cc0ccc579e4aed7464834523 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 6 Apr 2023 15:01:12 +0200 Subject: [PATCH 02/28] feat: add merkleization of DID documents (#492) Fixes https://github.com/KILTprotocol/ticket/issues/2557 and fixes https://github.com/KILTprotocol/ticket/issues/2556. This PR builds on top of the shell PR, and adds support for Merkle proof for DID documents. ## Merkle proof structure A DID merkle proof is, at the core, an order set of (key, value) pairs, on which proof-of-inclusion and proof-of-non-inclusion can be performed. This PR generates and validates merkle proofs where leaves are of two types: - a DID key reference leave, whose key is the tuple (key ID, key relationship) and the value is an empty tuple - a DID key details leave, whose key is the key ID and the value is the key details For each key reference leaf with a given key ID, the proof also has to contain a key details leaf whose key is the key ID. Multiple reference leaves can reference the same key details leaf, optimising the storage size. ## New runtime APIs There is a new runtime API which the DIP sender would expose, and that allows anyone to generate a merkle proof for a given DID identifier and set of key IDs. The result contains the merkle root (which must match what other chains have stored in their `pallet-dip-receiver` map), and a merkle proof, which includes blinded values and a set of key reference and key details leaves for the keys identified by the provided key IDs. ## How to test The setup flow is similar to that of https://github.com/KILTprotocol/kilt-node/pull/489. Specifically: - Set up the local Rococo network onboarding the sender and receiver chains with para IDs 2_000 and 2_001 respectively - Open an HRMP channel from sender 2_000 to receiver 2_001 - Create a DID on the sender chain, e.g., using the [kilt-did-utilities](https://github.com/KILTprotocol/kilt-did-utilities) tool ![Screenshot 2023-03-27 at 10 00 48](https://user-images.githubusercontent.com/6704504/227900994-41f0f355-84bd-4b8a-a2a8-3a9c74447e59.png) - Push the identity of the DID to the receiver chain via the `pallet-dip-sender` extrinsic ![Screenshot 2023-03-27 at 10 01 13](https://user-images.githubusercontent.com/6704504/227901150-7e8c9c9d-8aac-4739-8ad3-fad4ba6ff5f8.png) - Call the runtime API to generate a proof for the created DID with some keys revealed ![Screenshot 2023-03-27 at 10 01 40](https://user-images.githubusercontent.com/6704504/227901309-94b4dbc9-ca83-4541-820d-d1bec6adc6f0.png) - Use the generated proof to dispatch an extrinsic on the receiving chain ### How to use the runtime API with polkadot apps There is currently no support for the new runtime API in the public polkadot apps instance. To use the runtime APIs from UI, please use [our fork from the aa/dip-sender-template branch](https://github.com/KILTprotocol/polkadot-apps/tree/aa/dip-sender-template), by running `yarn && yarn build && yarn start`, then connecting to the sender node WS socket. For runtime augmentation within a Node script, please use our [SDK repo from the aa/dip-merkle-proof branch](https://github.com/KILTprotocol/sdk-js/tree/aa/dip-merkle-proof). --- Cargo.lock | 24 +- Cargo.toml | 2 + crates/dip/src/lib.rs | 24 +- crates/dip/src/v1.rs | 6 +- dip-template/runtimes/dip-receiver/Cargo.toml | 3 + dip-template/runtimes/dip-receiver/src/dip.rs | 17 +- dip-template/runtimes/dip-receiver/src/lib.rs | 15 +- dip-template/runtimes/dip-sender/Cargo.toml | 7 +- dip-template/runtimes/dip-sender/src/dip.rs | 29 +- dip-template/runtimes/dip-sender/src/lib.rs | 24 +- dip-template/runtimes/xcm-tests/Cargo.toml | 15 +- dip-template/runtimes/xcm-tests/src/para.rs | 51 ++- dip-template/runtimes/xcm-tests/src/tests.rs | 64 ++-- pallets/did/src/did_details.rs | 6 +- pallets/pallet-dip-receiver/Cargo.toml | 2 +- pallets/pallet-dip-receiver/src/lib.rs | 18 +- pallets/pallet-dip-receiver/src/traits.rs | 22 +- pallets/pallet-dip-sender/src/lib.rs | 8 +- pallets/pallet-dip-sender/src/traits.rs | 15 +- runtime-api/did/src/did_details.rs | 2 +- runtime-api/did/src/lib.rs | 2 +- runtime-api/dip-sender/Cargo.toml | 27 ++ runtime-api/dip-sender/src/lib.rs | 33 ++ runtimes/common/Cargo.toml | 14 +- runtimes/common/src/dip/mod.rs | 82 +++++ runtimes/common/src/dip/receiver.rs | 118 +++++++ runtimes/common/src/dip/sender.rs | 221 ++++++++++++ runtimes/common/src/dip/tests.rs | 315 ++++++++++++++++++ runtimes/common/src/lib.rs | 1 + 29 files changed, 1052 insertions(+), 115 deletions(-) create mode 100644 runtime-api/dip-sender/Cargo.toml create mode 100644 runtime-api/dip-sender/src/lib.rs create mode 100644 runtimes/common/src/dip/mod.rs create mode 100644 runtimes/common/src/dip/receiver.rs create mode 100644 runtimes/common/src/dip/sender.rs create mode 100644 runtimes/common/src/dip/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 4ea5562ee3..69a1fae44d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2344,6 +2344,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-runtime-api-dip-sender", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -2359,6 +2360,7 @@ dependencies = [ "parachain-info", "parity-scale-codec", "polkadot-parachain", + "runtime-common", "scale-info", "sp-api", "sp-block-builder", @@ -2457,6 +2459,7 @@ dependencies = [ "pallet-xcm", "parachain-info", "parity-scale-codec", + "runtime-common", "scale-info", "sp-api", "sp-block-builder", @@ -2486,24 +2489,31 @@ dependencies = [ ] [[package]] -name = "dip-templates-xmc-tests" +name = "dip-templates-xcm-tests" version = "1.11.0-dev" dependencies = [ "cumulus-pallet-xcmp-queue", + "did", "dip-receiver-runtime-template", "dip-sender-runtime-template", + "dip-support", "frame-support", "frame-system", + "kilt-support", "pallet-balances", + "pallet-did-lookup", "parachain-info", "parity-scale-codec", "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-parachains", "rococo-runtime", + "runtime-common", "scale-info", + "sp-core", "sp-io", "sp-runtime", + "sp-std", "xcm", "xcm-emulator", "xcm-executor", @@ -4273,6 +4283,14 @@ dependencies = [ "sp-std", ] +[[package]] +name = "kilt-runtime-api-dip-sender" +version = "1.11.0-dev" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + [[package]] name = "kilt-runtime-api-public-credentials" version = "1.11.0-dev" @@ -9380,6 +9398,7 @@ dependencies = [ "cumulus-primitives-core", "delegation", "did", + "dip-support", "frame-support", "frame-system", "kilt-asset-dids", @@ -9388,6 +9407,8 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-did-lookup", + "pallet-dip-receiver", + "pallet-dip-sender", "pallet-inflation", "pallet-membership", "pallet-transaction-payment", @@ -9403,6 +9424,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "sp-trie", "xcm", "xcm-builder", "xcm-executor", diff --git a/Cargo.toml b/Cargo.toml index 95442c91b4..292ea62c90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ dip-sender-runtime-template = {path = "dip-template/runtimes/dip-sender", defaul # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} +kilt-runtime-api-dip-sender = {path = "runtime-api/dip-sender", default-features = false} kilt-runtime-api-public-credentials = {path = "runtime-api/public-credentials", default-features = false} kilt-runtime-api-staking = {path = "runtime-api/staking", default-features = false} @@ -136,6 +137,7 @@ sp-session = {git = "https://github.com/paritytech/substrate", default-features sp-staking = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} sp-std = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} sp-transaction-pool = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} +sp-trie = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} sp-version = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} sp-weights = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} try-runtime-cli = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.39"} diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs index f681976fac..a795d7c855 100644 --- a/crates/dip/src/lib.rs +++ b/crates/dip/src/lib.rs @@ -31,6 +31,7 @@ pub mod latest { } #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[non_exhaustive] pub enum VersionedIdentityProofAction { #[codec(index = 1)] V1(v1::IdentityProofAction), @@ -45,13 +46,28 @@ impl From { +#[non_exhaustive] +pub enum VersionedIdentityProof { #[codec(index = 1)] - V1(v1::Proof), + V1(v1::Proof), } -impl From> for VersionedIdentityProof { - fn from(value: v1::Proof) -> Self { +impl From> for VersionedIdentityProof { + fn from(value: v1::Proof) -> Self { Self::V1(value) } } + +impl TryFrom> for v1::Proof { + // Proper error handling + type Error = (); + + fn try_from(value: VersionedIdentityProof) -> Result { + #[allow(irrefutable_let_patterns)] + if let VersionedIdentityProof::V1(v1::Proof { blinded, revealed }) = value { + Ok(Self { blinded, revealed }) + } else { + Err(()) + } + } +} diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs index f72e02881f..bdbda97bf6 100644 --- a/crates/dip/src/v1.rs +++ b/crates/dip/src/v1.rs @@ -28,4 +28,8 @@ pub enum IdentityProofAction { } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct Proof(Vec<(LeafKey, LeafValue)>); +pub struct Proof { + pub blinded: BlindedValue, + // TODO: Probably replace with a different data structure for better lookup capabilities + pub revealed: Vec, +} diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml index d41f73dbab..1737cf1525 100644 --- a/dip-template/runtimes/dip-receiver/Cargo.toml +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP pallet-did-lookup.workspace = true pallet-dip-receiver.workspace = true +runtime-common.workspace = true # Substrate frame-executive.workspace = true @@ -74,6 +75,7 @@ std = [ "scale-info/std", "pallet-did-lookup/std", "pallet-dip-receiver/std", + "runtime-common/std", "frame-executive/std", "frame-support/std", "frame-system/std", @@ -118,6 +120,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-dip-receiver/runtime-benchmarks", + "runtime-common/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs index b36d425abd..18a5ff1b05 100644 --- a/dip-template/runtimes/dip-receiver/src/dip.rs +++ b/dip-template/runtimes/dip-receiver/src/dip.rs @@ -16,20 +16,17 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use pallet_dip_receiver::traits::SuccessfulProofVerifier; +use runtime_common::dip::{receiver::DidMerkleProofVerifier, ProofLeaf}; +use sp_std::vec::Vec; -use crate::{DidIdentifier, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_receiver::Config for Runtime { + type BlindedValue = Vec>; type Identifier = DidIdentifier; - // TODO: Change with right one - type ProofDigest = [u8; 32]; - // TODO: Change with right one - type ProofLeafKey = [u8; 4]; - // TODO: Change with right one - type ProofLeafValue = [u8; 4]; - // TODO: Change with right one - type ProofVerifier = SuccessfulProofVerifier; + type ProofLeaf = ProofLeaf; + type ProofDigest = Hash; + type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs index 6dd9a52193..c678bf0c2c 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -56,9 +56,9 @@ use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, + AccountId32, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; @@ -73,14 +73,15 @@ pub use sp_runtime::BuildStorage; #[cfg(feature = "std")] use sp_version::NativeVersion; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type AccountId = AccountId32; pub type Address = MultiAddress; pub type Balance = u128; pub type Block = generic::Block; pub type BlockNumber = u32; pub type DidIdentifier = AccountId; +pub type Hasher = BlakeTwo256; pub type Hash = sp_core::H256; -pub type Header = generic::Header; +pub type Header = generic::Header; pub type Index = u32; pub type Signature = MultiSignature; @@ -149,7 +150,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("dip-receiver-runtime-template"), impl_name: create_runtime_str!("dip-receiver-runtime-template"), authoring_version: 1, - spec_version: 1, + spec_version: 11100, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -228,8 +229,8 @@ impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type DbWeight = RocksDbWeight; type Hash = Hash; - type Hashing = BlakeTwo256; - type Header = generic::Header; + type Hashing = Hasher; + type Header = generic::Header; type Index = Index; type Lookup = AccountIdLookup; type MaxConsumers = ConstU32<16>; diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml index d9972fd45a..3d638bb4dd 100644 --- a/dip-template/runtimes/dip-sender/Cargo.toml +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -20,7 +20,9 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true dip-support.workspace = true +kilt-runtime-api-dip-sender.workspace = true pallet-dip-sender.workspace = true +runtime-common.workspace = true # Substrate frame-executive.workspace = true @@ -74,7 +76,9 @@ std = [ "scale-info/std", "did/std", "dip-support/std", + "kilt-runtime-api-dip-sender/std", "pallet-dip-sender/std", + "runtime-common/std", "frame-executive/std", "frame-support/std", "frame-system/std", @@ -115,9 +119,10 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", + "pallet-dip-sender/runtime-benchmarks", + "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", - "pallet-dip-sender/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "xcm-builder/runtime-benchmarks" ] diff --git a/dip-template/runtimes/dip-sender/src/dip.rs b/dip-template/runtimes/dip-sender/src/dip.rs index a31ed8b39f..a8afc90aca 100644 --- a/dip-template/runtimes/dip-sender/src/dip.rs +++ b/dip-template/runtimes/dip-sender/src/dip.rs @@ -16,14 +16,14 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use did::did_details::DidDetails; use dip_support::VersionedIdentityProofAction; -use pallet_dip_sender::traits::{ - DefaultIdentityProofGenerator, DefaultIdentityProvider, TxBuilder, XcmRouterDispatcher, -}; +use pallet_dip_sender::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; +use runtime_common::dip::sender::{DidIdentityProvider, DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{DidIdentifier, Runtime, RuntimeEvent, XcmRouter}; +use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; #[derive(Encode, Decode)] enum ReceiverParachainCalls { @@ -34,16 +34,16 @@ enum ReceiverParachainCalls { #[derive(Encode, Decode)] enum ReceiverParachainDipReceiverCalls { #[codec(index = 0)] - ProcessIdentityAction(VersionedIdentityProofAction), + ProcessIdentityAction(VersionedIdentityProofAction), } pub struct ReceiverParachainTxBuilder; -impl TxBuilder for ReceiverParachainTxBuilder { +impl TxBuilder for ReceiverParachainTxBuilder { type Error = (); fn build( _dest: MultiLocation, - action: VersionedIdentityProofAction, + action: VersionedIdentityProofAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = ReceiverParachainCalls::DipReceiver(ReceiverParachainDipReceiverCalls::ProcessIdentityAction(action)) @@ -55,16 +55,11 @@ impl TxBuilder for ReceiverParachainTxBuilder { impl pallet_dip_sender::Config for Runtime { type Identifier = DidIdentifier; - // TODO: Change with right one - type Identity = u32; - // TODO: Change with right one - type IdentityProofDispatcher = XcmRouterDispatcher; - // TODO: Change with right one - type IdentityProofGenerator = DefaultIdentityProofGenerator; - // TODO: Change with right one - type IdentityProvider = DefaultIdentityProvider; - // TODO: Change with right one - type ProofOutput = [u8; 32]; + type Identity = DidDetails; + type IdentityProofDispatcher = XcmRouterDispatcher; + type IdentityProofGenerator = DidMerkleRootGenerator; + type IdentityProvider = DidIdentityProvider; + type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; type TxBuilder = ReceiverParachainTxBuilder; } diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index d81684c1e7..d13871665f 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -30,7 +30,7 @@ use cumulus_pallet_parachain_system::{ }; use cumulus_primitives_core::CollationInfo; use cumulus_primitives_timestamp::InherentDataProvider; -use did::{DidRawOrigin, EnsureDidOrigin}; +use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; use frame_support::{ construct_runtime, dispatch::DispatchClass, @@ -48,17 +48,19 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; +use pallet_dip_sender::traits::IdentityProvider; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; use sp_api::impl_runtime_apis; use sp_consensus_aura::SlotDuration; use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, + AccountId32, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; @@ -73,7 +75,7 @@ mod dip; mod xcm_config; pub use crate::{dip::*, xcm_config::*}; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type AccountId = AccountId32; pub type Address = MultiAddress; pub type Balance = u128; pub type Block = generic::Block; @@ -149,7 +151,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("dip-sender-runtime-template"), impl_name: create_runtime_str!("dip-sender-runtime-template"), authoring_version: 1, - spec_version: 1, + spec_version: 11100, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -525,4 +527,16 @@ impl_runtime_apis! { ParachainSystem::collect_collation_info(header) } } + + // TODO: `keys` could and should be a BTreeSet, but it makes it more complicated for clients to build the type. So we use a Vec, since the keys are deduplicated anyway at proof creation time. + // TODO: Support generating different versions of the proof, based on the provided parameter + impl kilt_runtime_api_dip_sender::DipSender, Vec>, CompleteMerkleProof>, ()> for Runtime { + fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { + if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { + DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) + } else { + Err(()) + } + } + } } diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 26c770a855..4c032a3e9a 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -7,27 +7,32 @@ license-file.workspace = true readme.workspace = true repository.workspace = true version.workspace = true -name = "dip-templates-xmc-tests" +name = "dip-templates-xcm-tests" description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) sender and receiver templates." -[dev-dependencies] -cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } - [dependencies] -parity-scale-codec = {workspace = true, features = ["std", "derive"]} +cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } +did = { workspace = true, features = ["std"] } dip-receiver-runtime-template = { workspace = true, features = ["std"] } dip-sender-runtime-template = { workspace = true, features = ["std"] } +dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } +kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } +pallet-did-lookup = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } +parity-scale-codec = {workspace = true, features = ["std", "derive"]} polkadot-parachain = { workspace = true, features = ["std"] } polkadot-primitives = { workspace = true, features = ["std"] } polkadot-runtime-parachains = { workspace = true, features = ["std"] } rococo-runtime = { workspace = true, features = ["std"] } +runtime-common = { workspace = true, features = ["std"] } scale-info = { workspace = true, features = ["std"] } +sp-core = { workspace = true, features = ["std"] } sp-io = { workspace = true, features = ["std"] } sp-runtime = { workspace = true, features = ["std"] } +sp-std = { workspace = true, features = ["std"] } xcm = { workspace = true, features = ["std"] } xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } xcm-executor = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index ca30e62ba9..15fc36c469 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -22,15 +22,44 @@ use sp_io::TestExternalities; use xcm_emulator::decl_test_parachain; pub(super) mod sender { - pub(crate) use dip_sender_runtime_template::{DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + + use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; + use dip_sender_runtime_template::{AccountId, System}; + use kilt_support::deposit::Deposit; + use sp_core::{ecdsa, ed25519, sr25519, Pair}; use super::*; pub const PARA_ID: u32 = 2_000; - pub(crate) fn para_ext() -> TestExternalities { - use dip_sender_runtime_template::System; + pub(crate) fn did_auth_key() -> ed25519::Pair { + ed25519::Pair::from_seed(&[200u8; 32]) + } + fn generate_did_details() -> DidDetails { + let auth_key: DidVerificationKey = did_auth_key().public().into(); + let att_key: DidVerificationKey = sr25519::Pair::from_seed(&[100u8; 32]).public().into(); + let del_key: DidVerificationKey = ecdsa::Pair::from_seed(&[101u8; 32]).public().into(); + + let mut details = DidDetails::new( + auth_key, + 0u32, + Deposit { + amount: 1u64.into(), + owner: AccountId::new([1u8; 32]), + }, + ) + .unwrap(); + details.update_attestation_key(att_key, 0u32).unwrap(); + details.update_delegation_key(del_key, 0u32).unwrap(); + details + .add_key_agreement_key(DidEncryptionKey::X25519([100u8; 32]), 0u32) + .unwrap(); + details + } + + pub(crate) fn para_ext() -> TestExternalities { let mut t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); @@ -43,7 +72,10 @@ pub(super) mod sender { .unwrap(); let mut ext = TestExternalities::new(t); + let did: DidIdentifier = did_auth_key().public().into(); + let details = generate_did_details(); ext.execute_with(|| { + did::pallet::Did::::insert(&did, details); System::set_block_number(1); }); ext @@ -62,16 +94,18 @@ pub(super) mod sender { pub(super) mod receiver { pub(crate) use dip_receiver_runtime_template::{ - AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, + AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, UNIT, }; + use dip_receiver_runtime_template::System; use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; use xcm_executor::traits::Convert; use super::*; pub const PARA_ID: u32 = 2_001; - const INITIAL_BALANCE: Balance = 1_000_000_000; + pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([90u8; 32]); + const INITIAL_BALANCE: Balance = 100_000 * UNIT; pub(crate) fn sender_parachain_account() -> AccountId { AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(sender::PARA_ID))).into()) @@ -79,8 +113,6 @@ pub(super) mod receiver { } pub(crate) fn para_ext() -> TestExternalities { - use dip_receiver_runtime_template::System; - let mut t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); @@ -93,7 +125,10 @@ pub(super) mod receiver { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(sender_parachain_account(), INITIAL_BALANCE)], + balances: vec![ + (sender_parachain_account(), INITIAL_BALANCE), + (DISPATCHER_ACCOUNT, INITIAL_BALANCE), + ], } .assimilate_storage(&mut t) .unwrap(); diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 037561708c..e8cabc3997 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -18,13 +18,13 @@ use super::*; -use dip_receiver_runtime_template::{ - AccountId as ReceiverAccountId, DidIdentifier as ReceiverDidIdentifier, DipReceiver, -}; -use dip_sender_runtime_template::DipSender; - +use did::Did; +use dip_support::latest::Proof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; +use pallet_did_lookup::linkable_account::LinkableAccountId; +use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleRootGenerator}; +use sp_core::Pair; use xcm::latest::{ Junction::Parachain, Junctions::{Here, X1}, @@ -32,23 +32,24 @@ use xcm::latest::{ }; use xcm_emulator::TestExt; +use cumulus_pallet_xcmp_queue::Event as XcmpEvent; +use dip_receiver_runtime_template::{ + DidIdentifier, DidLookup, DipReceiver, Runtime as ReceiverRuntime, RuntimeCall as ReceiverRuntimeCall, + RuntimeEvent, System, +}; +use dip_sender_runtime_template::{AccountId as SenderAccountId, DipSender, Runtime as SenderRuntime}; + #[test] fn commit_identity() { Network::reset(); - ReceiverParachain::execute_with(|| { - use dip_receiver_runtime_template::Balances; - use para::receiver::sender_parachain_account; - - let sender_balance = Balances::free_balance(sender_parachain_account()); - println!("Sender balance: {:?}", sender_balance); - }); + let did: DidIdentifier = para::sender::did_auth_key().public().into(); // 1. Send identity proof from DIP sender to DIP receiver. SenderParachain::execute_with(|| { assert_ok!(DipSender::commit_identity( - RawOrigin::Signed(ReceiverAccountId::from([0u8; 32])).into(), - ReceiverDidIdentifier::from([0u8; 32]), + RawOrigin::Signed(SenderAccountId::from([0u8; 32])).into(), + did.clone(), Box::new(ParentThen(X1(Parachain(para::receiver::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), Weight::from_ref_time(4_000), @@ -56,9 +57,6 @@ fn commit_identity() { }); // 2. Verify that the proof has made it to the DIP receiver. ReceiverParachain::execute_with(|| { - use cumulus_pallet_xcmp_queue::Event as XcmpEvent; - use dip_receiver_runtime_template::{RuntimeEvent, System}; - // 2.1 Verify that there was no XCM error. assert!(!System::events().iter().any(|r| matches!( r.event, @@ -68,8 +66,34 @@ fn commit_identity() { weight: _ }) ))); - // 2.2 Verify the proof digest is the same that was sent. - let details = DipReceiver::identity_proofs(dip_sender_runtime_template::AccountId::from([0u8; 32])); - assert_eq!(details, Some([0u8; 32])); + // 2.2 Verify the proof digest was stored correctly. + assert!(DipReceiver::identity_proofs(&did).is_some()); + }); + // 3. Call an extrinsic on the receiver chain with a valid proof + let did_details = + SenderParachain::execute_with(|| Did::get(&did).expect("DID details should be stored on the sender chain.")); + // 3.1 Generate a proof + let CompleteMerkleProof { proof, .. } = + DidMerkleRootGenerator::::generate_proof(&did_details, [did_details.authentication_key].iter()) + .expect("Proof generation should not fail"); + // 3.2 Call the `dispatch_as` extrinsic on the receiver chain with the generated + // proof + ReceiverParachain::execute_with(|| { + assert_ok!(DipReceiver::dispatch_as( + RawOrigin::Signed(para::receiver::DISPATCHER_ACCOUNT).into(), + did.clone(), + Proof { + blinded: proof.blinded, + revealed: proof.revealed, + } + .into(), + Box::new(ReceiverRuntimeCall::DidLookup(pallet_did_lookup::Call::< + ReceiverRuntime, + >::associate_sender {})), + )); + // Verify the account -> DID link exists and contains the right information + let linked_did = DidLookup::connected_dids::(para::receiver::DISPATCHER_ACCOUNT.into()) + .map(|link| link.did); + assert_eq!(linked_did, Some(did)); }); } diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index c1e42fe7fa..27a347bb57 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -121,7 +121,7 @@ impl From for DidPublicKey { /// Verification methods a verification key can /// fulfil, according to the [DID specification](https://w3c.github.io/did-spec-registries/#verification-relationships). -#[derive(Clone, Copy, RuntimeDebug, Decode, Encode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Copy, RuntimeDebug, Decode, Encode, PartialEq, Eq, TypeInfo, MaxEncodedLen, PartialOrd, Ord)] pub enum DidVerificationKeyRelationship { /// Key used to authenticate all the DID operations. Authentication, @@ -239,9 +239,7 @@ impl> DidVerifiableIdentifier for I { /// It is currently used to keep track of all the past and current /// attestation keys a DID might control. #[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq, Ord, PartialOrd, Eq, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound())] -pub struct DidPublicKeyDetails { +pub struct DidPublicKeyDetails { /// A public key the DID controls. pub key: DidPublicKey, /// The block number in which the verification key was added to the DID. diff --git a/pallets/pallet-dip-receiver/Cargo.toml b/pallets/pallet-dip-receiver/Cargo.toml index 8e7e5a1ac5..6a6d6f98d2 100644 --- a/pallets/pallet-dip-receiver/Cargo.toml +++ b/pallets/pallet-dip-receiver/Cargo.toml @@ -37,5 +37,5 @@ std = [ ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks" + "frame-system/runtime-benchmarks", ] diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index 551b6bc80a..cb93b72281 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use crate::traits::IdentityProofVerifier; pub type VersionedIdentityProofOf = - VersionedIdentityProof<::ProofLeafKey, ::ProofLeafValue>; + VersionedIdentityProof<::BlindedValue, ::ProofLeaf>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -51,14 +51,14 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + type BlindedValue: Parameter; type Identifier: Parameter + MaxEncodedLen; - type ProofLeafKey: Parameter; - type ProofLeafValue: Parameter; + type ProofLeaf: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< + BlindedValue = Self::BlindedValue, ProofDigest = Self::ProofDigest, - LeafKey = Self::ProofLeafKey, - LeafValue = Self::ProofLeafValue, + ProofLeaf = Self::ProofLeaf, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -84,6 +84,7 @@ pub mod pallet { Dispatch, IdentityNotFound, InvalidProof, + UnsupportedVersion, } // The new origin other pallets can use. @@ -104,13 +105,14 @@ pub mod pallet { let event = match action { VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); - Event::::IdentityInfoUpdated(identifier, proof) + Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { IdentityProofs::::remove(&identifier); - Event::::IdentityInfoDeleted(identifier) + Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) } - }; + _ => Err(Error::::UnsupportedVersion), + }?; Self::deposit_event(event); diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs index 460dc48b0b..ef7642fff9 100644 --- a/pallets/pallet-dip-receiver/src/traits.rs +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -20,31 +20,31 @@ use dip_support::VersionedIdentityProof; use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { + type BlindedValue; + type Error; type ProofDigest; - type LeafKey; - type LeafValue; + type ProofLeaf; type VerificationResult; - type Error; fn verify_proof_against_digest( - proof: VersionedIdentityProof, + proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, LeafKey, LeafValue)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, Leaf, BlindedValue)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { - type ProofDigest = ProofDigest; + type BlindedValue = BlindedValue; type Error = (); - type LeafKey = LeafKey; - type LeafValue = LeafValue; + type ProofDigest = ProofDigest; + type ProofLeaf = Leaf; type VerificationResult = (); fn verify_proof_against_digest( - _proof: VersionedIdentityProof, + _proof: VersionedIdentityProof, _digest: Self::ProofDigest, ) -> Result { Ok(()) diff --git a/pallets/pallet-dip-sender/src/lib.rs b/pallets/pallet-dip-sender/src/lib.rs index 57d3c38fa0..4af958ccd8 100644 --- a/pallets/pallet-dip-sender/src/lib.rs +++ b/pallets/pallet-dip-sender/src/lib.rs @@ -48,7 +48,11 @@ pub mod pallet { type Identifier: Parameter; type Identity; type ProofOutput: Clone + Eq + Debug; - type IdentityProofGenerator: IdentityProofGenerator; + type IdentityProofGenerator: IdentityProofGenerator< + Self::Identifier, + Self::Identity, + Output = Self::ProofOutput, + >; type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -98,7 +102,7 @@ pub mod pallet { let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { Ok(Some((identity, _))) => { - let identity_proof = T::IdentityProofGenerator::generate_proof(&identifier, &identity) + let identity_proof = T::IdentityProofGenerator::generate_commitment(&identifier, &identity) .map_err(|_| Error::::IdentityProofGeneration)?; Ok(IdentityProofAction::Updated(identifier, identity_proof, ())) } diff --git a/pallets/pallet-dip-sender/src/traits.rs b/pallets/pallet-dip-sender/src/traits.rs index 097bd6a37f..6667699cc0 100644 --- a/pallets/pallet-dip-sender/src/traits.rs +++ b/pallets/pallet-dip-sender/src/traits.rs @@ -21,25 +21,28 @@ use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; pub mod identity_generation { + use sp_std::marker::PhantomData; - pub trait IdentityProofGenerator { + pub trait IdentityProofGenerator { type Error; + type Output; - fn generate_proof(identifier: &Identifier, identity: &Identity) -> Result; + fn generate_commitment(identifier: &Identifier, identity: &Identity) -> Result; } // Implement the `IdentityProofGenerator` by returning the `Default` value for // the `Output` type. - pub struct DefaultIdentityProofGenerator; + pub struct DefaultIdentityProofGenerator(PhantomData); - impl IdentityProofGenerator - for DefaultIdentityProofGenerator + impl IdentityProofGenerator + for DefaultIdentityProofGenerator where Output: Default, { type Error = (); + type Output = Output; - fn generate_proof(_identifier: &Identifier, _identity: &Identity) -> Result { + fn generate_commitment(_identifier: &Identifier, _identity: &Identity) -> Result { Ok(Output::default()) } } diff --git a/runtime-api/did/src/did_details.rs b/runtime-api/did/src/did_details.rs index 509aa72928..84af78fe42 100644 --- a/runtime-api/did/src/did_details.rs +++ b/runtime-api/did/src/did_details.rs @@ -24,7 +24,7 @@ use did::{did_details::DidPublicKeyDetails, AccountIdOf, BalanceOf, BlockNumberO use kilt_support::deposit::Deposit; #[derive(Encode, Decode, TypeInfo, Clone, Debug, Eq, PartialEq, MaxEncodedLen)] -pub struct DidDetails { +pub struct DidDetails { pub authentication_key: Key, pub key_agreement_keys: BTreeSet, pub delegation_key: Option, diff --git a/runtime-api/did/src/lib.rs b/runtime-api/did/src/lib.rs index 291e779601..52a6f6e361 100644 --- a/runtime-api/did/src/lib.rs +++ b/runtime-api/did/src/lib.rs @@ -39,7 +39,7 @@ pub struct DidLinkedInfo< Url, Balance, Key: Ord, - BlockNumber: MaxEncodedLen, + BlockNumber, > { pub identifier: DidIdentifier, pub accounts: Vec, diff --git a/runtime-api/dip-sender/Cargo.toml b/runtime-api/dip-sender/Cargo.toml new file mode 100644 index 0000000000..12b20140f3 --- /dev/null +++ b/runtime-api/dip-sender/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "kilt-runtime-api-dip-sender" +description = "Runtime APIs for integrating the DIP sender component." + +[dependencies] +# External dependencies +parity-scale-codec.workspace = true + +# Internal dependencies + +# Substrate dependencies +sp-api.workspace = true + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "sp-api/std" +] diff --git a/runtime-api/dip-sender/src/lib.rs b/runtime-api/dip-sender/src/lib.rs new file mode 100644 index 0000000000..ce420714e5 --- /dev/null +++ b/runtime-api/dip-sender/src/lib.rs @@ -0,0 +1,33 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] + +use parity_scale_codec::Codec; + +sp_api::decl_runtime_apis! { + pub trait DipSender where + DidIdentifier: Codec, + KeyId: Codec, + KeyIds: Codec + IntoIterator, + Success: Codec, + Error: Codec, + { + fn generate_proof(identifier: DidIdentifier, keys: KeyIds) -> Result; + } +} diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index f6c49d6463..c9467dd679 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -25,8 +25,11 @@ smallvec.workspace = true attestation.workspace = true ctype.workspace = true delegation = {workspace = true, optional = true} -did = {workspace = true, optional = true} +did.workspace = true +dip-support.workspace = true pallet-did-lookup = {workspace = true, optional = true} +pallet-dip-receiver.workspace = true +pallet-dip-sender.workspace = true pallet-inflation = {workspace = true, optional = true} kilt-support.workspace = true parachain-staking.workspace = true @@ -45,6 +48,7 @@ sp-core.workspace = true sp-io.workspace = true sp-runtime.workspace = true sp-std.workspace = true +sp-trie.workspace = true # Cumulus dependencies cumulus-primitives-core.workspace = true @@ -61,6 +65,8 @@ fast-gov = [] runtime-benchmarks = [ "attestation/runtime-benchmarks", "ctype/runtime-benchmarks", + "did/runtime-benchmarks", + "pallet-dip-receiver/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "kilt-support/runtime-benchmarks", @@ -77,6 +83,8 @@ std = [ "attestation/std", "ctype/std", "cumulus-primitives-core/std", + "did/std", + "dip-support/std", "frame-support/std", "frame-system/std", "kilt-asset-dids/std", @@ -84,6 +92,8 @@ std = [ "log/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-dip-receiver/std", + "pallet-dip-sender/std", "pallet-membership/std", "pallet-transaction-payment/std", "parachain-staking/std", @@ -96,6 +106,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "sp-trie/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -103,7 +114,6 @@ std = [ try-runtime = [ "attestation/try-runtime", "delegation", - "did", "frame-support/try-runtime", "frame-system/try-runtime", "kilt-support/try-runtime", diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs new file mode 100644 index 0000000000..5f292b3662 --- /dev/null +++ b/runtimes/common/src/dip/mod.rs @@ -0,0 +1,82 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use frame_support::RuntimeDebug; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +pub mod receiver; +pub mod sender; + +#[cfg(test)] +mod tests; + +#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] +pub enum KeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} + +impl From for KeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceValue; + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsKey(pub KeyId); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsValue(pub DidPublicKeyDetails); + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub enum ProofLeaf { + // The key and value for the leaves of a merkle proof that contain a reference + // (by ID) to the key details, provided in a separate leaf. + KeyReference(KeyReferenceKey, KeyReferenceValue), + // The key and value for the leaves of a merkle proof that contain the actual + // details of a DID public key. The key is the ID of the key, and the value is its details, including creation + // block number. + KeyDetails(KeyDetailsKey, KeyDetailsValue), +} + +impl ProofLeaf +where + KeyId: Encode, + BlockNumber: Encode, +{ + pub(crate) fn encoded_key(&self) -> Vec { + match self { + ProofLeaf::KeyReference(key, _) => key.encode(), + ProofLeaf::KeyDetails(key, _) => key.encode(), + } + } + + pub(crate) fn encoded_value(&self) -> Vec { + match self { + ProofLeaf::KeyReference(_, value) => value.encode(), + ProofLeaf::KeyDetails(_, value) => value.encode(), + } + } +} diff --git a/runtimes/common/src/dip/receiver.rs b/runtimes/common/src/dip/receiver.rs new file mode 100644 index 0000000000..f63470cd85 --- /dev/null +++ b/runtimes/common/src/dip/receiver.rs @@ -0,0 +1,118 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::did_details::DidPublicKeyDetails; +use dip_support::{v1, VersionedIdentityProof}; +use frame_support::RuntimeDebug; +use pallet_dip_receiver::traits::IdentityProofVerifier; +use parity_scale_codec::Encode; +use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; +use sp_trie::{verify_trie_proof, LayoutV1}; + +use crate::dip::{sender, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; + +// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by +// having a vector of `KeyRelationship` instead. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub struct ProofEntry { + pub key: DidPublicKeyDetails, + pub relationship: KeyRelationship, +} + +// Contains the list of revealed public keys after a given merkle proof has been +// correctly verified. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub struct VerificationResult(pub Vec>); + +impl From>> for VerificationResult { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +pub struct DidMerkleProofVerifier(PhantomData<(KeyId, BlockNumber, Hasher)>); + +impl IdentityProofVerifier for DidMerkleProofVerifier +where + KeyId: Encode + Clone + Ord, + BlockNumber: Encode + Clone + Ord, + Hasher: sp_core::Hasher, +{ + type BlindedValue = Vec; + // TODO: Proper error handling + type Error = (); + type ProofDigest = ::Out; + type ProofLeaf = ProofLeaf; + type VerificationResult = VerificationResult; + + fn verify_proof_against_digest( + proof: VersionedIdentityProof, + digest: Self::ProofDigest, + ) -> Result { + let proof: v1::Proof<_, _> = proof.try_into()?; + // TODO: more efficient by removing cloning and/or collecting. + // Did not find another way of mapping a Vec<(Vec, Vec)> to a + // Vec<(Vec, Option>)>. + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof_leaves).map_err(|_| ())?; + + // At this point, we know the proof is valid. We just need to map the revealed + // leaves to something the consumer can easily operate on. + + // Create a map of the revealed public keys + //TODO: Avoid cloning, and use a map of references for the lookup + let public_keys: BTreeMap> = proof + .revealed + .clone() + .into_iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { + Some((key_id, key_details)) + } else { + None + } + }) + .collect(); + // Create a list of the revealed keys by consuming the provided key reference + // leaves, and looking up the full details from the just-built `public_keys` + // map. + let keys: Vec> = proof + .revealed + .into_iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { + // TODO: Better error handling. + let key_details = public_keys + .get(&key_id) + .expect("Key ID should be present in the map of revealed public keys."); + Some(ProofEntry { + key: key_details.clone(), + relationship: key_relationship, + }) + } else { + None + } + }) + .collect(); + Ok(keys.into()) + } +} diff --git a/runtimes/common/src/dip/sender.rs b/runtimes/common/src/dip/sender.rs new file mode 100644 index 0000000000..8ae4658ce2 --- /dev/null +++ b/runtimes/common/src/dip/sender.rs @@ -0,0 +1,221 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; +use dip_support::latest::Proof; +use frame_support::RuntimeDebug; +use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet, marker::PhantomData, vec::Vec}; +use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; + +use crate::dip::{KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, KeyRelationship, ProofLeaf}; + +pub type BlindedValue = Vec; + +pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; + +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct CompleteMerkleProof { + pub root: Root, + pub proof: Proof, +} + +pub struct DidMerkleRootGenerator(PhantomData); + +impl DidMerkleRootGenerator +where + T: did::Config, +{ + // Calls the function in the `sp_trie` crate to generate the merkle root given + // the provided `DidDetails`. + // Each key in the merkle tree is added in the following way: + // - keys in the `public_keys` map are added by value in the merkle tree, with + // the leaf key being the key ID and the value being the key details + // - keys everywhere else in the DidDetails are added by reference, with the + // leaf key being the encoding of the tuple (keyID, key relationship) and the + // value being hte empty tuple + // A valid proof will contain a leaf with the key details for each reference + // leaf, with multiple reference leaves potentially referring to the same + // details leaf, as we already do with out `DidDetails` type. + fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { + let mut trie = TrieHash::>::default(); + let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); + + // Authentication key + let auth_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey( + identity.authentication_key, + DidVerificationKeyRelationship::Authentication.into(), + ), + KeyReferenceValue, + ); + trie_builder + .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + // Attestation key, if present + if let Some(att_key_id) = identity.attestation_key { + let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + ); + trie_builder + .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + }; + // Delegation key, if present + if let Some(del_key_id) = identity.delegation_key { + let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + ); + trie_builder + .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + }; + // Key agreement keys + identity + .key_agreement_keys + .iter() + .try_for_each(|id| -> Result<(), ()> { + let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(*id, KeyRelationship::Encryption), + KeyReferenceValue, + ); + trie_builder + .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + Ok(()) + })?; + // Public keys + identity + .public_keys + .iter() + .try_for_each(|(id, key_details)| -> Result<(), ()> { + let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*id), KeyDetailsValue(key_details.clone())); + trie_builder + .insert(key_leaf.encoded_key().as_slice(), key_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + Ok(()) + })?; + trie_builder.commit(); + Ok(trie_builder.root().to_owned()) + } + + // TODO: Better error handling + // Only used for testing and as part of the features exposed by the runtime API + // of the provider. Given the provided `DidDetails` and a list of key IDs, it + // generates a merkle proof which only reveals the details of the provided key + // IDs. + #[allow(clippy::result_unit_err)] + pub fn generate_proof<'a, K>( + identity: &DidDetails, + mut key_ids: K, + ) -> Result>, ()> + where + K: Iterator>, + { + let mut db = MemoryDB::default(); + let root = Self::calculate_root_with_db(identity, &mut db)?; + + #[allow(clippy::type_complexity)] + let leaves: BTreeSet, T::BlockNumber>> = + key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { + let key_details = identity.public_keys.get(key_id).ok_or(())?; + // Adds a key reference leaf for each relationship the key ID is part of. + if *key_id == identity.authentication_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), + KeyReferenceValue, + )); + } + if Some(*key_id) == identity.attestation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + )); + } + if Some(*key_id) == identity.delegation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + )); + } + if identity.key_agreement_keys.contains(key_id) { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, KeyRelationship::Encryption), + KeyReferenceValue, + )); + }; + // Then adds the actual key details to the merkle proof. + // If the same key is specified twice, the old key is simply replaced with a new + // key of the same value. + let key_details_leaf = + ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); + if !set.contains(&key_details_leaf) { + set.insert(key_details_leaf); + } + Ok(set) + })?; + let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); + let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; + Ok(CompleteMerkleProof { + root, + proof: DidMerkleProof:: { + blinded: proof, + revealed: leaves.into_iter().collect::>(), + }, + }) + } +} + +impl IdentityProofGenerator> for DidMerkleRootGenerator +where + T: did::Config, +{ + // TODO: Proper error handling + type Error = (); + type Output = T::Hash; + + fn generate_commitment(_identifier: &T::DidIdentifier, identity: &DidDetails) -> Result { + let mut db = MemoryDB::default(); + Self::calculate_root_with_db(identity, &mut db) + } +} + +pub struct DidIdentityProvider(PhantomData); + +impl IdentityProvider, ()> for DidIdentityProvider +where + T: did::Config, +{ + // TODO: Proper error handling + type Error = (); + + fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { + match ( + did::Pallet::::get_did(identifier), + did::Pallet::::get_deleted_did(identifier), + ) { + (Some(details), _) => Ok(Some((details, ()))), + (_, Some(_)) => Ok(None), + _ => Err(()), + } + } +} diff --git a/runtimes/common/src/dip/tests.rs b/runtimes/common/src/dip/tests.rs new file mode 100644 index 0000000000..ae88fb7362 --- /dev/null +++ b/runtimes/common/src/dip/tests.rs @@ -0,0 +1,315 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{ + did_details::{DidCreationDetails, DidEncryptionKey}, + DidVerificationKeyRelationship, KeyIdOf, +}; +use dip_support::latest::Proof; +use frame_support::{ + assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, +}; +use frame_system::{ + mocking::{MockBlock, MockUncheckedExtrinsic}, + EnsureSigned, RawOrigin, +}; +use pallet_dip_receiver::traits::IdentityProofVerifier; +use parity_scale_codec::Encode; +use sp_core::{ecdsa, ed25519, sr25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; +use sp_io::TestExternalities; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup}, + AccountId32, +}; +use sp_std::collections::btree_set::BTreeSet; + +use crate::dip::{ + receiver::DidMerkleProofVerifier, + sender::{CompleteMerkleProof, DidMerkleRootGenerator}, + ProofLeaf, +}; + +pub(crate) type AccountId = AccountId32; +pub(crate) type Balance = u128; +pub(crate) type Block = MockBlock; +pub(crate) type BlockNumber = u64; +pub(crate) type Hashing = BlakeTwo256; +pub(crate) type Index = u64; +pub(crate) type UncheckedExtrinsic = MockUncheckedExtrinsic; + +construct_runtime!( + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + Did: did, + } +); + +impl frame_system::Config for TestRuntime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type BaseCallFilter = Everything; + type BlockHashCount = ConstU64<250>; + type BlockLength = (); + type BlockNumber = BlockNumber; + type BlockWeights = (); + type DbWeight = RocksDbWeight; + type Hash = ::Out; + type Hashing = Hashing; + type Header = Header; + type Index = Index; + type Lookup = IdentityLookup; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16<38>; + type SystemWeightInfo = (); + type Version = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 500u64.into(); +} + +impl pallet_balances::Config for TestRuntime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +parameter_types! { + pub Deposit: Balance = 500u64.into(); + pub Fee: Balance = 500u64.into(); + pub MaxBlocksTxValidity: BlockNumber = 10u64; + #[derive(Debug, Clone, Eq, PartialEq)] + pub const MaxTotalKeyAgreementKeys: u32 = 2; +} + +impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { + fn derive_verification_key_relationship(&self) -> did::DeriveDidCallKeyRelationshipResult { + Ok(DidVerificationKeyRelationship::Authentication) + } + + #[cfg(feature = "runtime-benchmarks")] + fn get_call_for_did_call_benchmark() -> Self { + RuntimeCall::System(frame_system::Call::remark { remark: vec![] }) + } +} + +impl did::Config for TestRuntime { + type Currency = Balances; + type Deposit = Deposit; + type DidIdentifier = AccountId; + type EnsureOrigin = EnsureSigned; + type Fee = Fee; + type FeeCollector = (); + type MaxBlocksTxValidity = MaxBlocksTxValidity; + type MaxNewKeyAgreementKeys = ConstU32<2>; + type MaxNumberOfServicesPerDid = ConstU32<1>; + type MaxNumberOfTypesPerService = ConstU32<1>; + type MaxNumberOfUrlsPerService = ConstU32<1>; + type MaxPublicKeysPerDid = ConstU32<5>; + type MaxServiceIdLength = ConstU32<100>; + type MaxServiceTypeLength = ConstU32<100>; + type MaxServiceUrlLength = ConstU32<100>; + type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; + type OriginSuccess = AccountId; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type WeightInfo = (); +} + +fn base_ext() -> TestExternalities { + TestExternalities::new( + frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(), + ) +} + +const ALICE: AccountId = AccountId::new([1u8; 32]); + +#[test] +fn minimal_did_merkle_proof() { + base_ext().execute_with(|| { + // Give Alice some balance + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); + // Generate a DID for alice + let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); + let did: AccountId = did_auth_key.public().into_account().into(); + let create_details = DidCreationDetails { + did: did.clone(), + submitter: ALICE, + new_attestation_key: None, + new_delegation_key: None, + new_key_agreement_keys: BTreeSet::new().try_into().unwrap(), + new_service_details: vec![], + }; + // Create Alice's DID with only authentication key + assert_ok!(Did::create( + RawOrigin::Signed(ALICE).into(), + Box::new(create_details.clone()), + did_auth_key.sign(&create_details.encode()).into() + )); + let did_details = Did::get_did(&did).expect("DID should be present"); + + // 1. Create the DID merkle proof revealing only the authentication key + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( + &did_details, + [did_details.authentication_key].iter(), + ) + .expect("Merkle proof generation should not fail."); + println!("{:?} - {:?} - {:?} bytes", root, proof, proof.encoded_size()); + // Verify the generated merkle proof + assert_ok!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + proof.clone().into(), + root + ) + ); + + // 2. Fail to generate a Merkle proof for a key that does not exist + assert_err!( + DidMerkleRootGenerator::::generate_proof( + &did_details, + [<::Out>::default()].iter() + ), + () + ); + + // 3. Fail to verify a merkle proof with a compromised merkle root + let new_root = <::Out>::default(); + assert_err!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + proof.into(), + new_root + ), + () + ); + }) +} + +#[test] +fn complete_did_merkle_proof() { + base_ext().execute_with(|| { + // Give Alice some balance + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); + // Generate a DID for alice + let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); + let did_att_key = sr25519::Pair::from_seed(&[150u8; 32]); + let did_del_key = ecdsa::Pair::from_seed(&[200u8; 32]); + let enc_keys = BTreeSet::from_iter(vec![ + DidEncryptionKey::X25519([250u8; 32]), + DidEncryptionKey::X25519([251u8; 32]), + ]); + let did: AccountId = did_auth_key.public().into_account().into(); + let create_details = DidCreationDetails { + did: did.clone(), + submitter: ALICE, + new_attestation_key: Some(did_att_key.public().into()), + new_delegation_key: Some(did_del_key.public().into()), + new_key_agreement_keys: enc_keys + .try_into() + .expect("BTreeSet to BoundedBTreeSet should not fail"), + new_service_details: vec![], + }; + // Create Alice's DID with only authentication key + assert_ok!(Did::create( + RawOrigin::Signed(ALICE).into(), + Box::new(create_details.clone()), + did_auth_key.sign(&create_details.encode()).into() + )); + let did_details = Did::get_did(&did).expect("DID should be present"); + + // 1. Create the DID merkle proof revealing only the authentication key + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( + &did_details, + [did_details.authentication_key].iter(), + ) + .expect("Merkle proof generation should not fail."); + // Verify the generated merkle proof + assert_ok!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + proof.into(), + root + ) + ); + + // 2. Create the DID merkle proof revealing all the keys + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( + &did_details, + [ + did_details.authentication_key, + did_details.attestation_key.unwrap(), + did_details.delegation_key.unwrap(), + ] + .iter() + .chain(did_details.key_agreement_keys.iter()), + ) + .expect("Merkle proof generation should not fail."); + // Verify the generated merkle proof + assert_ok!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + proof.into(), + root + ) + ); + + // 2. Create the DID merkle proof revealing only the key reference and not the + // key ID + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( + &did_details, + [did_details.authentication_key].iter(), + ) + .expect("Merkle proof generation should not fail."); + let reference_only_authentication_leaf: Vec<_> = proof + .revealed + .into_iter() + .filter(|l| !matches!(l, ProofLeaf::KeyDetails(_, _))) + .collect(); + // Fail to verify the generated merkle proof + assert_err!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + Proof { + blinded: proof.blinded, + revealed: reference_only_authentication_leaf + } + .into(), + root + ), + () + ); + }) +} diff --git a/runtimes/common/src/lib.rs b/runtimes/common/src/lib.rs index d627b067a9..e94d361b43 100644 --- a/runtimes/common/src/lib.rs +++ b/runtimes/common/src/lib.rs @@ -43,6 +43,7 @@ use sp_std::marker::PhantomData; pub mod assets; pub mod authorization; pub mod constants; +pub mod dip; pub mod errors; pub mod fees; pub mod migrations; From 06ecb8f9b1395808d53655a48e16f40e1834103b Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 13 Apr 2023 12:07:15 +0300 Subject: [PATCH 03/28] feat: add DID key verification relationship logic (#502) Fixes https://github.com/KILTprotocol/ticket/issues/2587. This PR adds a new trait `DipCallOriginFilter` that exposes a `fn check_proof(call: Call, proof: Self::Proof) -> Result` function, called by the runtime to verify if a call can be dispatched with the provided proof or not. --- Cargo.lock | 2 + dip-template/runtimes/dip-receiver/Cargo.toml | 10 +- dip-template/runtimes/dip-receiver/src/dip.rs | 95 ++++++++++++++++++- dip-template/runtimes/dip-receiver/src/lib.rs | 12 ++- pallets/pallet-dip-receiver/src/lib.rs | 18 +++- pallets/pallet-dip-receiver/src/origin.rs | 27 ++++-- pallets/pallet-dip-receiver/src/traits.rs | 8 ++ 7 files changed, 153 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69a1fae44d..a4c8a307b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2340,6 +2340,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", + "did", "frame-executive", "frame-support", "frame-system", @@ -2356,6 +2357,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "pallet-xcm", "parachain-info", "parity-scale-codec", diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml index 1737cf1525..07f7c5e50b 100644 --- a/dip-template/runtimes/dip-receiver/Cargo.toml +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -18,6 +18,7 @@ parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # DIP +did.workspace = true pallet-did-lookup.workspace = true pallet-dip-receiver.workspace = true runtime-common.workspace = true @@ -35,6 +36,7 @@ pallet-sudo.workspace = true pallet-timestamp.workspace = true pallet-transaction-payment.workspace = true pallet-transaction-payment-rpc-runtime-api.workspace = true +pallet-utility.workspace = true polkadot-parachain.workspace = true sp-api.workspace = true sp-block-builder.workspace = true @@ -73,6 +75,7 @@ default = [ std = [ "parity-scale-codec/std", "scale-info/std", + "did/std", "pallet-did-lookup/std", "pallet-dip-receiver/std", "runtime-common/std", @@ -88,6 +91,7 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-utility/std", "polkadot-parachain/std", "sp-api/std", "sp-block-builder/std", @@ -117,10 +121,10 @@ std = [ ] runtime-benchmarks = [ - "frame-system/runtime-benchmarks", - "frame-support/runtime-benchmarks", "pallet-dip-receiver/runtime-benchmarks", "runtime-common/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs index 18a5ff1b05..b170209347 100644 --- a/dip-template/runtimes/dip-receiver/src/dip.rs +++ b/dip-template/runtimes/dip-receiver/src/dip.rs @@ -16,13 +16,19 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use runtime_common::dip::{receiver::DidMerkleProofVerifier, ProofLeaf}; +use did::DidVerificationKeyRelationship; +use pallet_dip_receiver::traits::DipCallOriginFilter; +use runtime_common::dip::{ + receiver::{DidMerkleProofVerifier, VerificationResult}, + ProofLeaf, +}; use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_receiver::Config for Runtime { type BlindedValue = Vec>; + type DipCallOriginFilter = DipCallFilter; type Identifier = DidIdentifier; type ProofLeaf = ProofLeaf; type ProofDigest = Hash; @@ -31,3 +37,90 @@ impl pallet_dip_receiver::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; } + +fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { + match call { + RuntimeCall::DidLookup { .. } => Some(DidVerificationKeyRelationship::Authentication), + RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls).ok(), + RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls).ok(), + RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls).ok(), + _ => None, + } +} + +// Taken and adapted from `impl +// did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall` +// in Spiritnet/Peregrine runtime. +fn single_key_relationship(calls: &[RuntimeCall]) -> Result { + let first_call_relationship = calls.get(0).and_then(derive_verification_key_relationship).ok_or(())?; + calls + .iter() + .skip(1) + .map(derive_verification_key_relationship) + .try_fold(first_call_relationship, |acc, next| { + if next == Some(acc) { + Ok(acc) + } else { + Err(()) + } + }) +} + +pub struct DipCallFilter; + +impl DipCallOriginFilter for DipCallFilter { + type Error = (); + type Proof = VerificationResult; + type Success = (); + + // Accepts only a DipOrigin for the DidLookup pallet calls. + fn check_proof(call: RuntimeCall, proof: Self::Proof) -> Result { + let key_relationship = single_key_relationship(&[call])?; + if proof.0.iter().any(|l| l.relationship == key_relationship.into()) { + Ok(()) + } else { + Err(()) + } + } +} + +#[cfg(test)] +mod dip_call_origin_filter_tests { + use super::*; + + use frame_support::assert_err; + + #[test] + fn test_key_relationship_derivation() { + // Can call DidLookup functions with an authentication key + let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); + assert_eq!( + single_key_relationship(&[did_lookup_call]), + Ok(DidVerificationKeyRelationship::Authentication) + ); + // Can't call System functions with a DID key (hence a DIP origin) + let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + assert_err!(single_key_relationship(&[system_call]), ()); + // Can't call empty batch with a DID key + let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); + assert_err!(single_key_relationship(&[empty_batch_call]), ()); + // Can call batch with a DipLookup with an authentication key + let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], + }); + assert_eq!( + single_key_relationship(&[did_lookup_batch_call]), + Ok(DidVerificationKeyRelationship::Authentication) + ); + // Can't call a batch with different required keys + let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![ + // Authentication key + pallet_did_lookup::Call::associate_sender {}.into(), + // No key + frame_system::Call::remark { remark: vec![] }.into(), + ], + }); + assert_err!(single_key_relationship(&[did_lookup_batch_call]), ()); + } +} diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs index c678bf0c2c..1037d471ba 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -119,6 +119,7 @@ construct_runtime!( Timestamp: pallet_timestamp = 2, ParachainInfo: parachain_info = 3, Sudo: pallet_sudo = 4, + Utility: pallet_utility = 5, // Money Balances: pallet_balances = 10, @@ -277,6 +278,13 @@ impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; } +impl pallet_utility::Config for Runtime { + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; impl pallet_balances::Config for Runtime { @@ -358,8 +366,8 @@ impl pallet_did_lookup::Config for Runtime { type Currency = Balances; type Deposit = ConstU128; type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin; - type OriginSuccess = DipOrigin; + type EnsureOrigin = EnsureDipOrigin; + type OriginSuccess = DipOrigin; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index cb93b72281..a351ae1b9a 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -36,8 +36,9 @@ pub mod pallet { use dip_support::{latest::IdentityProofAction, VersionedIdentityProof, VersionedIdentityProofAction}; - use crate::traits::IdentityProofVerifier; + use crate::traits::{DipCallOriginFilter, IdentityProofVerifier}; + pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier>::VerificationResult; pub type VersionedIdentityProofOf = VersionedIdentityProof<::BlindedValue, ::ProofLeaf>; @@ -52,6 +53,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type BlindedValue: Parameter; + type DipCallOriginFilter: DipCallOriginFilter<::RuntimeCall, Proof = VerificationResultOf>; type Identifier: Parameter + MaxEncodedLen; type ProofLeaf: Parameter; type ProofDigest: Parameter + MaxEncodedLen; @@ -81,6 +83,7 @@ pub mod pallet { #[pallet::error] pub enum Error { + BadOrigin, Dispatch, IdentityNotFound, InvalidProof, @@ -89,7 +92,11 @@ pub mod pallet { // The new origin other pallets can use. #[pallet::origin] - pub type Origin = DipOrigin<::Identifier, ::AccountId>; + pub type Origin = DipOrigin< + ::Identifier, + ::AccountId, + <::DipCallOriginFilter as DipCallOriginFilter<::RuntimeCall>>::Success, + >; // TODO: Benchmarking #[pallet::call] @@ -130,12 +137,17 @@ pub mod pallet { ) -> DispatchResult { let submitter = ensure_signed(origin)?; let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; - let _ = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) + let proof_verification_result = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) .map_err(|_| Error::::InvalidProof)?; + // TODO: Better error handling + // TODO: Avoid cloning `call` + let proof_result = T::DipCallOriginFilter::check_proof(*call.clone(), proof_verification_result) + .map_err(|_| Error::::BadOrigin)?; // TODO: Proper DID signature verification (and cross-chain replay protection) let did_origin = DipOrigin { identifier, account_address: submitter, + details: proof_result, }; // TODO: Use dispatch info for weight calculation let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; diff --git a/pallets/pallet-dip-receiver/src/origin.rs b/pallets/pallet-dip-receiver/src/origin.rs index e24ed1aedb..f030044479 100644 --- a/pallets/pallet-dip-receiver/src/origin.rs +++ b/pallets/pallet-dip-receiver/src/origin.rs @@ -22,19 +22,22 @@ use scale_info::TypeInfo; use sp_std::marker::PhantomData; #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct DipOrigin { +pub struct DipOrigin { pub identifier: Identifier, pub account_address: AccountId, + pub details: Details, } -pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId)>); +pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId, Details)>); #[cfg(not(feature = "runtime-benchmarks"))] -impl EnsureOrigin for EnsureDipOrigin +impl EnsureOrigin + for EnsureDipOrigin where - OuterOrigin: From> + Into, OuterOrigin>>, + OuterOrigin: From> + + Into, OuterOrigin>>, { - type Success = DipOrigin; + type Success = DipOrigin; fn try_origin(o: OuterOrigin) -> Result { o.into() @@ -42,14 +45,17 @@ where } #[cfg(feature = "runtime-benchmarks")] -impl EnsureOrigin for EnsureDipOrigin +impl EnsureOrigin + for EnsureDipOrigin where - OuterOrigin: From> + Into, OuterOrigin>>, + OuterOrigin: From> + + Into, OuterOrigin>>, // Additional trait bounds only valid when benchmarking Identifier: From<[u8; 32]>, AccountId: From<[u8; 32]>, + Details: Default, { - type Success = DipOrigin; + type Success = DipOrigin; fn try_origin(o: OuterOrigin) -> Result { o.into() @@ -59,12 +65,13 @@ where Ok(OuterOrigin::from(DipOrigin { identifier: Identifier::from([0u8; 32]), account_address: AccountId::from([0u8; 32]), + details: Default::default(), })) } } -impl kilt_support::traits::CallSources - for DipOrigin +impl kilt_support::traits::CallSources + for DipOrigin where Identifier: Clone, AccountId: Clone, diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs index ef7642fff9..c134a038f5 100644 --- a/pallets/pallet-dip-receiver/src/traits.rs +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -50,3 +50,11 @@ impl IdentityProofVerifier Ok(()) } } + +pub trait DipCallOriginFilter { + type Error; + type Proof; + type Success; + + fn check_proof(call: Call, proof: Self::Proof) -> Result; +} From c37cbf1d9a62d1f349316564fa17c782142cfb7e Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 13 Apr 2023 14:28:15 +0300 Subject: [PATCH 04/28] chore: rename sender -> provider and receiver -> consumer (#503) Fixes https://github.com/KILTprotocol/ticket/issues/2588, based on top of https://github.com/KILTprotocol/kilt-node/pull/502. Won't be reviewed as it is merely a renaming PR. Will merge onto `aa/dip` once https://github.com/KILTprotocol/kilt-node/pull/502 gets reviewed and merged. --- Cargo.lock | 32 +++++------ Cargo.toml | 10 ++-- .../{dip-receiver => dip-consumer}/Cargo.toml | 6 +- .../{dip-receiver => dip-consumer}/build.rs | 0 .../src/chain_spec.rs | 8 +-- .../{dip-receiver => dip-consumer}/src/cli.rs | 0 .../src/command.rs | 12 ++-- .../{dip-sender => dip-consumer}/src/main.rs | 2 +- .../{dip-receiver => dip-consumer}/src/rpc.rs | 2 +- .../src/service.rs | 2 +- .../{dip-sender => dip-provider}/Cargo.toml | 6 +- .../{dip-sender => dip-provider}/build.rs | 0 .../src/chain_spec.rs | 8 +-- .../{dip-sender => dip-provider}/src/cli.rs | 0 .../src/command.rs | 12 ++-- .../src/main.rs | 2 +- .../{dip-sender => dip-provider}/src/rpc.rs | 2 +- .../src/service.rs | 2 +- .../{dip-receiver => dip-consumer}/Cargo.toml | 10 ++-- .../{dip-receiver => dip-consumer}/build.rs | 0 .../{dip-receiver => dip-consumer}/src/dip.rs | 6 +- .../{dip-receiver => dip-consumer}/src/lib.rs | 8 +-- .../src/xcm_config.rs | 2 +- .../{dip-sender => dip-provider}/Cargo.toml | 14 ++--- .../{dip-sender => dip-provider}/build.rs | 0 .../{dip-sender => dip-provider}/src/dip.rs | 20 +++---- .../{dip-sender => dip-provider}/src/lib.rs | 14 ++--- .../src/xcm_config.rs | 0 dip-template/runtimes/xcm-tests/Cargo.toml | 6 +- dip-template/runtimes/xcm-tests/src/lib.rs | 28 +++++----- dip-template/runtimes/xcm-tests/src/para.rs | 22 ++++---- dip-template/runtimes/xcm-tests/src/tests.rs | 55 ++++++++++--------- .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../src/origin.rs | 0 .../src/traits.rs | 0 .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../src/traits.rs | 2 +- .../{dip-sender => dip-provider}/Cargo.toml | 4 +- .../{dip-sender => dip-provider}/src/lib.rs | 2 +- runtimes/common/Cargo.toml | 10 ++-- .../src/dip/{receiver.rs => consumer.rs} | 6 +- runtimes/common/src/dip/mod.rs | 4 +- .../common/src/dip/{sender.rs => provider.rs} | 2 +- runtimes/common/src/dip/tests.rs | 6 +- 46 files changed, 167 insertions(+), 164 deletions(-) rename dip-template/nodes/{dip-receiver => dip-consumer}/Cargo.toml (94%) rename dip-template/nodes/{dip-receiver => dip-consumer}/build.rs (100%) rename dip-template/nodes/{dip-receiver => dip-consumer}/src/chain_spec.rs (97%) rename dip-template/nodes/{dip-receiver => dip-consumer}/src/cli.rs (100%) rename dip-template/nodes/{dip-receiver => dip-consumer}/src/command.rs (97%) rename dip-template/nodes/{dip-sender => dip-consumer}/src/main.rs (94%) rename dip-template/nodes/{dip-receiver => dip-consumer}/src/rpc.rs (97%) rename dip-template/nodes/{dip-receiver => dip-consumer}/src/service.rs (99%) rename dip-template/nodes/{dip-sender => dip-provider}/Cargo.toml (94%) rename dip-template/nodes/{dip-sender => dip-provider}/build.rs (100%) rename dip-template/nodes/{dip-sender => dip-provider}/src/chain_spec.rs (97%) rename dip-template/nodes/{dip-sender => dip-provider}/src/cli.rs (100%) rename dip-template/nodes/{dip-sender => dip-provider}/src/command.rs (97%) rename dip-template/nodes/{dip-receiver => dip-provider}/src/main.rs (94%) rename dip-template/nodes/{dip-sender => dip-provider}/src/rpc.rs (96%) rename dip-template/nodes/{dip-sender => dip-provider}/src/service.rs (99%) rename dip-template/runtimes/{dip-receiver => dip-consumer}/Cargo.toml (94%) rename dip-template/runtimes/{dip-receiver => dip-consumer}/build.rs (100%) rename dip-template/runtimes/{dip-receiver => dip-consumer}/src/dip.rs (96%) rename dip-template/runtimes/{dip-receiver => dip-consumer}/src/lib.rs (98%) rename dip-template/runtimes/{dip-receiver => dip-consumer}/src/xcm_config.rs (98%) rename dip-template/runtimes/{dip-sender => dip-provider}/Cargo.toml (92%) rename dip-template/runtimes/{dip-sender => dip-provider}/build.rs (100%) rename dip-template/runtimes/{dip-sender => dip-provider}/src/dip.rs (76%) rename dip-template/runtimes/{dip-sender => dip-provider}/src/lib.rs (96%) rename dip-template/runtimes/{dip-sender => dip-provider}/src/xcm_config.rs (100%) rename pallets/{pallet-dip-receiver => pallet-dip-consumer}/Cargo.toml (97%) rename pallets/{pallet-dip-receiver => pallet-dip-consumer}/src/lib.rs (100%) rename pallets/{pallet-dip-receiver => pallet-dip-consumer}/src/origin.rs (100%) rename pallets/{pallet-dip-receiver => pallet-dip-consumer}/src/traits.rs (100%) rename pallets/{pallet-dip-sender => pallet-dip-provider}/Cargo.toml (97%) rename pallets/{pallet-dip-sender => pallet-dip-provider}/src/lib.rs (100%) rename pallets/{pallet-dip-sender => pallet-dip-provider}/src/traits.rs (99%) rename runtime-api/{dip-sender => dip-provider}/Cargo.toml (80%) rename runtime-api/{dip-sender => dip-provider}/src/lib.rs (93%) rename runtimes/common/src/dip/{receiver.rs => consumer.rs} (95%) rename runtimes/common/src/dip/{sender.rs => provider.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index a4c8a307b8..a472644713 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2280,7 +2280,7 @@ dependencies = [ ] [[package]] -name = "dip-receiver-node-template" +name = "dip-consumer-node-template" version = "1.11.0-dev" dependencies = [ "clap", @@ -2291,7 +2291,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-relay-chain-interface", - "dip-receiver-runtime-template", + "dip-consumer-runtime-template", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -2329,7 +2329,7 @@ dependencies = [ ] [[package]] -name = "dip-receiver-runtime-template" +name = "dip-consumer-runtime-template" version = "1.11.0-dev" dependencies = [ "cumulus-pallet-aura-ext", @@ -2345,13 +2345,12 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", - "kilt-runtime-api-dip-sender", "pallet-aura", "pallet-authorship", "pallet-balances", "pallet-collator-selection", "pallet-did-lookup", - "pallet-dip-receiver", + "pallet-dip-consumer", "pallet-session", "pallet-sudo", "pallet-timestamp", @@ -2382,7 +2381,7 @@ dependencies = [ ] [[package]] -name = "dip-sender-node-template" +name = "dip-provider-node-template" version = "1.11.0-dev" dependencies = [ "clap", @@ -2393,7 +2392,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-relay-chain-interface", - "dip-sender-runtime-template", + "dip-provider-runtime-template", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -2431,7 +2430,7 @@ dependencies = [ ] [[package]] -name = "dip-sender-runtime-template" +name = "dip-provider-runtime-template" version = "1.11.0-dev" dependencies = [ "cumulus-pallet-aura-ext", @@ -2448,11 +2447,12 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-runtime-api-dip-provider", "pallet-aura", "pallet-authorship", "pallet-balances", "pallet-collator-selection", - "pallet-dip-sender", + "pallet-dip-provider", "pallet-session", "pallet-sudo", "pallet-timestamp", @@ -2496,8 +2496,8 @@ version = "1.11.0-dev" dependencies = [ "cumulus-pallet-xcmp-queue", "did", - "dip-receiver-runtime-template", - "dip-sender-runtime-template", + "dip-consumer-runtime-template", + "dip-provider-runtime-template", "dip-support", "frame-support", "frame-system", @@ -4286,7 +4286,7 @@ dependencies = [ ] [[package]] -name = "kilt-runtime-api-dip-sender" +name = "kilt-runtime-api-dip-provider" version = "1.11.0-dev" dependencies = [ "parity-scale-codec", @@ -6242,7 +6242,7 @@ dependencies = [ ] [[package]] -name = "pallet-dip-receiver" +name = "pallet-dip-consumer" version = "1.11.0-dev" dependencies = [ "cumulus-pallet-xcm", @@ -6256,7 +6256,7 @@ dependencies = [ ] [[package]] -name = "pallet-dip-sender" +name = "pallet-dip-provider" version = "1.11.0-dev" dependencies = [ "dip-support", @@ -9409,8 +9409,8 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-did-lookup", - "pallet-dip-receiver", - "pallet-dip-sender", + "pallet-dip-consumer", + "pallet-dip-provider", "pallet-inflation", "pallet-membership", "pallet-transaction-payment", diff --git a/Cargo.toml b/Cargo.toml index 292ea62c90..221c4cac81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,8 +51,8 @@ attestation = {path = "pallets/attestation", default-features = false} ctype = {path = "pallets/ctype", default-features = false} delegation = {path = "pallets/delegation", default-features = false} did = {path = "pallets/did", default-features = false} -pallet-dip-receiver = {path = "pallets/pallet-dip-receiver", default-features = false} -pallet-dip-sender = {path = "pallets/pallet-dip-sender", default-features = false} +pallet-dip-consumer = {path = "pallets/pallet-dip-consumer", default-features = false} +pallet-dip-provider = {path = "pallets/pallet-dip-provider", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "pallets/pallet-inflation", default-features = false} pallet-web3-names = {path = "pallets/pallet-web3-names", default-features = false} @@ -66,12 +66,12 @@ kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} # Templates -dip-receiver-runtime-template = {path = "dip-template/runtimes/dip-receiver", default-features = false} -dip-sender-runtime-template = {path = "dip-template/runtimes/dip-sender", default-features = false} +dip-consumer-runtime-template = {path = "dip-template/runtimes/dip-consumer", default-features = false} +dip-provider-runtime-template = {path = "dip-template/runtimes/dip-provider", default-features = false} # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} -kilt-runtime-api-dip-sender = {path = "runtime-api/dip-sender", default-features = false} +kilt-runtime-api-dip-provider = {path = "runtime-api/dip-provider", default-features = false} kilt-runtime-api-public-credentials = {path = "runtime-api/public-credentials", default-features = false} kilt-runtime-api-staking = {path = "runtime-api/staking", default-features = false} diff --git a/dip-template/nodes/dip-receiver/Cargo.toml b/dip-template/nodes/dip-consumer/Cargo.toml similarity index 94% rename from dip-template/nodes/dip-receiver/Cargo.toml rename to dip-template/nodes/dip-consumer/Cargo.toml index 1f217b7d01..ed4ecf7dd2 100644 --- a/dip-template/nodes/dip-receiver/Cargo.toml +++ b/dip-template/nodes/dip-consumer/Cargo.toml @@ -7,8 +7,8 @@ license-file.workspace = true readme.workspace = true repository.workspace = true version.workspace = true -name = "dip-receiver-node-template" -description = "Node template for the KILT Decentralized Identity Provider (DIP) receiver." +name = "dip-consumer-node-template" +description = "Node template for the KILT Decentralized Identity Provider (DIP) consumer." build = "build.rs" [dependencies] @@ -19,7 +19,7 @@ serde = {workspace = true, features = ["std", "derive"]} jsonrpsee = {workspace = true, features = ["server"]} # Internal dependencies -dip-receiver-runtime-template = { workspace = true, features = ["std"] } +dip-consumer-runtime-template = { workspace = true, features = ["std"] } # Substrate frame-benchmarking.workspace = true diff --git a/dip-template/nodes/dip-receiver/build.rs b/dip-template/nodes/dip-consumer/build.rs similarity index 100% rename from dip-template/nodes/dip-receiver/build.rs rename to dip-template/nodes/dip-consumer/build.rs diff --git a/dip-template/nodes/dip-receiver/src/chain_spec.rs b/dip-template/nodes/dip-consumer/src/chain_spec.rs similarity index 97% rename from dip-template/nodes/dip-receiver/src/chain_spec.rs rename to dip-template/nodes/dip-consumer/src/chain_spec.rs index 50daa7a567..4330bf5c62 100644 --- a/dip-template/nodes/dip-receiver/src/chain_spec.rs +++ b/dip-template/nodes/dip-consumer/src/chain_spec.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use cumulus_primitives_core::ParaId; -use dip_receiver_runtime_template::{ +use dip_consumer_runtime_template::{ AccountId, AuraId, BalancesConfig, CollatorSelectionConfig, GenesisConfig, ParachainInfoConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, EXISTENTIAL_DEPOSIT, SS58_PREFIX, WASM_BINARY, }; @@ -111,8 +111,8 @@ pub fn development_config() -> ChainSpec { properties.insert("ss58Format".into(), SS58_PREFIX.into()); ChainSpec::from_genesis( - "DIP receiver dev", - "dip-receiver-dev", + "DIP consumer dev", + "dip-consumer-dev", ChainType::Development, move || { testnet_genesis( @@ -133,7 +133,7 @@ pub fn development_config() -> ChainSpec { }, Vec::new(), None, - "dip-receiver-dev".into(), + "dip-consumer-dev".into(), None, None, Extensions { diff --git a/dip-template/nodes/dip-receiver/src/cli.rs b/dip-template/nodes/dip-consumer/src/cli.rs similarity index 100% rename from dip-template/nodes/dip-receiver/src/cli.rs rename to dip-template/nodes/dip-consumer/src/cli.rs diff --git a/dip-template/nodes/dip-receiver/src/command.rs b/dip-template/nodes/dip-consumer/src/command.rs similarity index 97% rename from dip-template/nodes/dip-receiver/src/command.rs rename to dip-template/nodes/dip-consumer/src/command.rs index 0e9db24e47..697b6f9363 100644 --- a/dip-template/nodes/dip-receiver/src/command.rs +++ b/dip-template/nodes/dip-consumer/src/command.rs @@ -20,7 +20,7 @@ use std::{fs::create_dir_all, net::SocketAddr}; use cumulus_client_cli::generate_genesis_block; use cumulus_primitives_core::ParaId; -use dip_receiver_runtime_template::Block; +use dip_consumer_runtime_template::Block; use log::{info, warn}; use parity_scale_codec::Encode; use sc_cli::{ @@ -51,7 +51,7 @@ fn load_spec(id: &str) -> std::result::Result, String> { impl SubstrateCli for Cli { fn impl_name() -> String { - "DIP Receiver Node Template".into() + "DIP Consumer Node Template".into() } fn impl_version() -> String { @@ -60,7 +60,7 @@ impl SubstrateCli for Cli { fn description() -> String { format!( - "DIP Receiver Node Template\n\nThe command-line arguments provided first will be \ + "DIP Consumer Node Template\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relay chain node.\n\n\ {} -- ", @@ -85,13 +85,13 @@ impl SubstrateCli for Cli { } fn native_runtime_version(_spec: &Box) -> &'static RuntimeVersion { - &dip_receiver_runtime_template::VERSION + &dip_consumer_runtime_template::VERSION } } impl SubstrateCli for RelayChainCli { fn impl_name() -> String { - "DIP Receiver Node Template".into() + "DIP Consumer Node Template".into() } fn impl_version() -> String { @@ -100,7 +100,7 @@ impl SubstrateCli for RelayChainCli { fn description() -> String { format!( - "DIP Receiver Node Template\n\nThe command-line arguments provided first will be \ + "DIP Consumer Node Template\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relay chain node.\n\n\ {} -- ", diff --git a/dip-template/nodes/dip-sender/src/main.rs b/dip-template/nodes/dip-consumer/src/main.rs similarity index 94% rename from dip-template/nodes/dip-sender/src/main.rs rename to dip-template/nodes/dip-consumer/src/main.rs index a3e57e48c6..21ec9b13ea 100644 --- a/dip-template/nodes/dip-sender/src/main.rs +++ b/dip-template/nodes/dip-consumer/src/main.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! KILT Decentralized Identity Provider (DIP) sender CLI. +//! KILT Decentralized Identity Provider (DIP) consumer CLI. #![warn(missing_docs)] diff --git a/dip-template/nodes/dip-receiver/src/rpc.rs b/dip-template/nodes/dip-consumer/src/rpc.rs similarity index 97% rename from dip-template/nodes/dip-receiver/src/rpc.rs rename to dip-template/nodes/dip-consumer/src/rpc.rs index 30a8bd3b09..2437c0e63b 100644 --- a/dip-template/nodes/dip-receiver/src/rpc.rs +++ b/dip-template/nodes/dip-consumer/src/rpc.rs @@ -23,7 +23,7 @@ use substrate_frame_rpc_system::AccountNonceApi; use std::{error::Error, sync::Arc}; -use dip_receiver_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use dip_consumer_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; use jsonrpsee::RpcModule; use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; use sc_client_api::AuxStore; diff --git a/dip-template/nodes/dip-receiver/src/service.rs b/dip-template/nodes/dip-consumer/src/service.rs similarity index 99% rename from dip-template/nodes/dip-receiver/src/service.rs rename to dip-template/nodes/dip-consumer/src/service.rs index 82c07368a7..86a0ee102e 100644 --- a/dip-template/nodes/dip-receiver/src/service.rs +++ b/dip-template/nodes/dip-consumer/src/service.rs @@ -30,7 +30,7 @@ use cumulus_client_service::{ use cumulus_primitives_core::ParaId; use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_relay_chain_interface::RelayChainInterface; -use dip_receiver_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; +use dip_consumer_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; use frame_benchmarking::benchmarking::HostFunctions; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use sc_basic_authorship::ProposerFactory; diff --git a/dip-template/nodes/dip-sender/Cargo.toml b/dip-template/nodes/dip-provider/Cargo.toml similarity index 94% rename from dip-template/nodes/dip-sender/Cargo.toml rename to dip-template/nodes/dip-provider/Cargo.toml index c77a4b72ec..3edfbb6a7b 100644 --- a/dip-template/nodes/dip-sender/Cargo.toml +++ b/dip-template/nodes/dip-provider/Cargo.toml @@ -7,8 +7,8 @@ license-file.workspace = true readme.workspace = true repository.workspace = true version.workspace = true -name = "dip-sender-node-template" -description = "Node template for the KILT Decentralized Identity Provider (DIP) sender." +name = "dip-provider-node-template" +description = "Node template for the KILT Decentralized Identity Provider (DIP) provider." build = "build.rs" [dependencies] @@ -19,7 +19,7 @@ serde = {workspace = true, features = ["std", "derive"]} jsonrpsee = {workspace = true, features = ["server"]} # Internal dependencies -dip-sender-runtime-template = { workspace = true, features = ["std"] } +dip-provider-runtime-template = { workspace = true, features = ["std"] } # Substrate frame-benchmarking.workspace = true diff --git a/dip-template/nodes/dip-sender/build.rs b/dip-template/nodes/dip-provider/build.rs similarity index 100% rename from dip-template/nodes/dip-sender/build.rs rename to dip-template/nodes/dip-provider/build.rs diff --git a/dip-template/nodes/dip-sender/src/chain_spec.rs b/dip-template/nodes/dip-provider/src/chain_spec.rs similarity index 97% rename from dip-template/nodes/dip-sender/src/chain_spec.rs rename to dip-template/nodes/dip-provider/src/chain_spec.rs index 8fe5269ada..7c323d7be3 100644 --- a/dip-template/nodes/dip-sender/src/chain_spec.rs +++ b/dip-template/nodes/dip-provider/src/chain_spec.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use cumulus_primitives_core::ParaId; -use dip_sender_runtime_template::{ +use dip_provider_runtime_template::{ AccountId, AuraId, BalancesConfig, CollatorSelectionConfig, GenesisConfig, ParachainInfoConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, EXISTENTIAL_DEPOSIT, SS58_PREFIX, WASM_BINARY, }; @@ -110,8 +110,8 @@ pub fn development_config() -> ChainSpec { properties.insert("ss58Format".into(), SS58_PREFIX.into()); ChainSpec::from_genesis( - "DIP sender dev", - "dip-sender-dev", + "DIP provider dev", + "dip-provider-dev", ChainType::Development, move || { testnet_genesis( @@ -132,7 +132,7 @@ pub fn development_config() -> ChainSpec { }, Vec::new(), None, - "dip-sender-dev".into(), + "dip-provider-dev".into(), None, None, Extensions { diff --git a/dip-template/nodes/dip-sender/src/cli.rs b/dip-template/nodes/dip-provider/src/cli.rs similarity index 100% rename from dip-template/nodes/dip-sender/src/cli.rs rename to dip-template/nodes/dip-provider/src/cli.rs diff --git a/dip-template/nodes/dip-sender/src/command.rs b/dip-template/nodes/dip-provider/src/command.rs similarity index 97% rename from dip-template/nodes/dip-sender/src/command.rs rename to dip-template/nodes/dip-provider/src/command.rs index da2116679a..8ec7f22c79 100644 --- a/dip-template/nodes/dip-sender/src/command.rs +++ b/dip-template/nodes/dip-provider/src/command.rs @@ -20,7 +20,7 @@ use std::{fs::create_dir_all, net::SocketAddr}; use cumulus_client_cli::generate_genesis_block; use cumulus_primitives_core::ParaId; -use dip_sender_runtime_template::Block; +use dip_provider_runtime_template::Block; use log::{info, warn}; use parity_scale_codec::Encode; use sc_cli::{ @@ -51,7 +51,7 @@ fn load_spec(id: &str) -> std::result::Result, String> { impl SubstrateCli for Cli { fn impl_name() -> String { - "DIP Sender Node Template".into() + "DIP Provider Node Template".into() } fn impl_version() -> String { @@ -60,7 +60,7 @@ impl SubstrateCli for Cli { fn description() -> String { format!( - "DIP Sender Node Template\n\nThe command-line arguments provided first will be \ + "DIP Provider Node Template\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relay chain node.\n\n\ {} -- ", @@ -85,13 +85,13 @@ impl SubstrateCli for Cli { } fn native_runtime_version(_spec: &Box) -> &'static RuntimeVersion { - &dip_sender_runtime_template::VERSION + &dip_provider_runtime_template::VERSION } } impl SubstrateCli for RelayChainCli { fn impl_name() -> String { - "DIP Sender Node Template".into() + "DIP Provider Node Template".into() } fn impl_version() -> String { @@ -100,7 +100,7 @@ impl SubstrateCli for RelayChainCli { fn description() -> String { format!( - "DIP Sender Node Template\n\nThe command-line arguments provided first will be \ + "DIP Provider Node Template\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relay chain node.\n\n\ {} -- ", diff --git a/dip-template/nodes/dip-receiver/src/main.rs b/dip-template/nodes/dip-provider/src/main.rs similarity index 94% rename from dip-template/nodes/dip-receiver/src/main.rs rename to dip-template/nodes/dip-provider/src/main.rs index e0250cebc8..272c3dd49b 100644 --- a/dip-template/nodes/dip-receiver/src/main.rs +++ b/dip-template/nodes/dip-provider/src/main.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! KILT Decentralized Identity Provider (DIP) receiver CLI. +//! KILT Decentralized Identity Provider (DIP) Provider CLI. #![warn(missing_docs)] diff --git a/dip-template/nodes/dip-sender/src/rpc.rs b/dip-template/nodes/dip-provider/src/rpc.rs similarity index 96% rename from dip-template/nodes/dip-sender/src/rpc.rs rename to dip-template/nodes/dip-provider/src/rpc.rs index 79365ebb0e..eb86db0d58 100644 --- a/dip-template/nodes/dip-sender/src/rpc.rs +++ b/dip-template/nodes/dip-provider/src/rpc.rs @@ -23,7 +23,7 @@ use substrate_frame_rpc_system::AccountNonceApi; use std::{error::Error, sync::Arc}; -use dip_sender_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use dip_provider_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; use jsonrpsee::RpcModule; use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; use sc_client_api::AuxStore; diff --git a/dip-template/nodes/dip-sender/src/service.rs b/dip-template/nodes/dip-provider/src/service.rs similarity index 99% rename from dip-template/nodes/dip-sender/src/service.rs rename to dip-template/nodes/dip-provider/src/service.rs index 9421403248..fe0c485bc2 100644 --- a/dip-template/nodes/dip-sender/src/service.rs +++ b/dip-template/nodes/dip-provider/src/service.rs @@ -30,7 +30,7 @@ use cumulus_client_service::{ use cumulus_primitives_core::ParaId; use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_relay_chain_interface::RelayChainInterface; -use dip_sender_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; +use dip_provider_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi}; use frame_benchmarking::benchmarking::HostFunctions; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use sc_basic_authorship::ProposerFactory; diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml similarity index 94% rename from dip-template/runtimes/dip-receiver/Cargo.toml rename to dip-template/runtimes/dip-consumer/Cargo.toml index 07f7c5e50b..2073d9d275 100644 --- a/dip-template/runtimes/dip-receiver/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -1,11 +1,11 @@ [package] authors.workspace = true -description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) receiver." +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) consumer." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "dip-receiver-runtime-template" +name = "dip-consumer-runtime-template" readme.workspace = true repository.workspace = true version.workspace = true @@ -20,7 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true pallet-did-lookup.workspace = true -pallet-dip-receiver.workspace = true +pallet-dip-consumer.workspace = true runtime-common.workspace = true # Substrate @@ -77,7 +77,7 @@ std = [ "scale-info/std", "did/std", "pallet-did-lookup/std", - "pallet-dip-receiver/std", + "pallet-dip-consumer/std", "runtime-common/std", "frame-executive/std", "frame-support/std", @@ -121,7 +121,7 @@ std = [ ] runtime-benchmarks = [ - "pallet-dip-receiver/runtime-benchmarks", + "pallet-dip-consumer/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-receiver/build.rs b/dip-template/runtimes/dip-consumer/build.rs similarity index 100% rename from dip-template/runtimes/dip-receiver/build.rs rename to dip-template/runtimes/dip-consumer/build.rs diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs similarity index 96% rename from dip-template/runtimes/dip-receiver/src/dip.rs rename to dip-template/runtimes/dip-consumer/src/dip.rs index b170209347..77fc5e79cf 100644 --- a/dip-template/runtimes/dip-receiver/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,16 +17,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::DidVerificationKeyRelationship; -use pallet_dip_receiver::traits::DipCallOriginFilter; +use pallet_dip_consumer::traits::DipCallOriginFilter; use runtime_common::dip::{ - receiver::{DidMerkleProofVerifier, VerificationResult}, + consumer::{DidMerkleProofVerifier, VerificationResult}, ProofLeaf, }; use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; -impl pallet_dip_receiver::Config for Runtime { +impl pallet_dip_consumer::Config for Runtime { type BlindedValue = Vec>; type DipCallOriginFilter = DipCallFilter; type Identifier = DidIdentifier; diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs similarity index 98% rename from dip-template/runtimes/dip-receiver/src/lib.rs rename to dip-template/runtimes/dip-consumer/src/lib.rs index 1037d471ba..955a43885f 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -47,7 +47,7 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; -use pallet_dip_receiver::{DipOrigin, EnsureDipOrigin}; +use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; @@ -142,14 +142,14 @@ construct_runtime!( DidLookup: pallet_did_lookup = 40, // DIP - DipReceiver: pallet_dip_receiver = 50, + DipConsumer: pallet_dip_consumer = 50, } ); #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("dip-receiver-runtime-template"), - impl_name: create_runtime_str!("dip-receiver-runtime-template"), + spec_name: create_runtime_str!("dip-consumer-runtime-template"), + impl_name: create_runtime_str!("dip-consumer-runtime-template"), authoring_version: 1, spec_version: 11100, impl_version: 0, diff --git a/dip-template/runtimes/dip-receiver/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs similarity index 98% rename from dip-template/runtimes/dip-receiver/src/xcm_config.rs rename to dip-template/runtimes/dip-consumer/src/xcm_config.rs index cc0ee09fcc..bb7775be4f 100644 --- a/dip-template/runtimes/dip-receiver/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -55,7 +55,7 @@ impl Contains for DipTransactSafeCalls { fn contains(t: &RuntimeCall) -> bool { matches!( t, - RuntimeCall::DipReceiver(pallet_dip_receiver::Call::process_identity_action { .. }) + RuntimeCall::DipConsumer(pallet_dip_consumer::Call::process_identity_action { .. }) ) } } diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml similarity index 92% rename from dip-template/runtimes/dip-sender/Cargo.toml rename to dip-template/runtimes/dip-provider/Cargo.toml index 3d638bb4dd..77647b7d8d 100644 --- a/dip-template/runtimes/dip-sender/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -1,11 +1,11 @@ [package] authors.workspace = true -description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) sender." +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) provider." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "dip-sender-runtime-template" +name = "dip-provider-runtime-template" readme.workspace = true repository.workspace = true version.workspace = true @@ -20,8 +20,8 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true dip-support.workspace = true -kilt-runtime-api-dip-sender.workspace = true -pallet-dip-sender.workspace = true +kilt-runtime-api-dip-provider.workspace = true +pallet-dip-provider.workspace = true runtime-common.workspace = true # Substrate @@ -76,8 +76,8 @@ std = [ "scale-info/std", "did/std", "dip-support/std", - "kilt-runtime-api-dip-sender/std", - "pallet-dip-sender/std", + "kilt-runtime-api-dip-provider/std", + "pallet-dip-provider/std", "runtime-common/std", "frame-executive/std", "frame-support/std", @@ -119,7 +119,7 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", - "pallet-dip-sender/runtime-benchmarks", + "pallet-dip-provider/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-sender/build.rs b/dip-template/runtimes/dip-provider/build.rs similarity index 100% rename from dip-template/runtimes/dip-sender/build.rs rename to dip-template/runtimes/dip-provider/build.rs diff --git a/dip-template/runtimes/dip-sender/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs similarity index 76% rename from dip-template/runtimes/dip-sender/src/dip.rs rename to dip-template/runtimes/dip-provider/src/dip.rs index a8afc90aca..6fa0d577bd 100644 --- a/dip-template/runtimes/dip-sender/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -18,27 +18,27 @@ use did::did_details::DidDetails; use dip_support::VersionedIdentityProofAction; -use pallet_dip_sender::traits::{TxBuilder, XcmRouterDispatcher}; +use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; -use runtime_common::dip::sender::{DidIdentityProvider, DidMerkleRootGenerator}; +use runtime_common::dip::provider::{DidIdentityProvider, DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; #[derive(Encode, Decode)] -enum ReceiverParachainCalls { +enum ConsumerParachainCalls { #[codec(index = 50)] - DipReceiver(ReceiverParachainDipReceiverCalls), + DipConsumer(ConsumerParachainDipConsumerCalls), } #[derive(Encode, Decode)] -enum ReceiverParachainDipReceiverCalls { +enum ConsumerParachainDipConsumerCalls { #[codec(index = 0)] ProcessIdentityAction(VersionedIdentityProofAction), } -pub struct ReceiverParachainTxBuilder; -impl TxBuilder for ReceiverParachainTxBuilder { +pub struct ConsumerParachainTxBuilder; +impl TxBuilder for ConsumerParachainTxBuilder { type Error = (); fn build( @@ -46,14 +46,14 @@ impl TxBuilder for ReceiverParachainTxBuilder { action: VersionedIdentityProofAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = - ReceiverParachainCalls::DipReceiver(ReceiverParachainDipReceiverCalls::ProcessIdentityAction(action)) + ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) .encode() .into(); Ok(double_encoded) } } -impl pallet_dip_sender::Config for Runtime { +impl pallet_dip_provider::Config for Runtime { type Identifier = DidIdentifier; type Identity = DidDetails; type IdentityProofDispatcher = XcmRouterDispatcher; @@ -61,5 +61,5 @@ impl pallet_dip_sender::Config for Runtime { type IdentityProvider = DidIdentityProvider; type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; - type TxBuilder = ReceiverParachainTxBuilder; + type TxBuilder = ConsumerParachainTxBuilder; } diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs similarity index 96% rename from dip-template/runtimes/dip-sender/src/lib.rs rename to dip-template/runtimes/dip-provider/src/lib.rs index d13871665f..d1db46563a 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -48,10 +48,10 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; -use pallet_dip_sender::traits::IdentityProvider; +use pallet_dip_provider::traits::IdentityProvider; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; -use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; use sp_api::impl_runtime_apis; use sp_consensus_aura::SlotDuration; use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; @@ -142,14 +142,14 @@ construct_runtime!( Did: did = 40, // DIP - DipSender: pallet_dip_sender = 50, + DipProvider: pallet_dip_provider = 50, } ); #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("dip-sender-runtime-template"), - impl_name: create_runtime_str!("dip-sender-runtime-template"), + spec_name: create_runtime_str!("dip-provider-runtime-template"), + impl_name: create_runtime_str!("dip-provider-runtime-template"), authoring_version: 1, spec_version: 11100, impl_version: 0, @@ -530,9 +530,9 @@ impl_runtime_apis! { // TODO: `keys` could and should be a BTreeSet, but it makes it more complicated for clients to build the type. So we use a Vec, since the keys are deduplicated anyway at proof creation time. // TODO: Support generating different versions of the proof, based on the provided parameter - impl kilt_runtime_api_dip_sender::DipSender, Vec>, CompleteMerkleProof>, ()> for Runtime { + impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { - if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { + if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) } else { Err(()) diff --git a/dip-template/runtimes/dip-sender/src/xcm_config.rs b/dip-template/runtimes/dip-provider/src/xcm_config.rs similarity index 100% rename from dip-template/runtimes/dip-sender/src/xcm_config.rs rename to dip-template/runtimes/dip-provider/src/xcm_config.rs diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 4c032a3e9a..91e6ae3fbb 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -8,13 +8,13 @@ readme.workspace = true repository.workspace = true version.workspace = true name = "dip-templates-xcm-tests" -description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) sender and receiver templates." +description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) provider and consumer templates." [dependencies] cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } -dip-receiver-runtime-template = { workspace = true, features = ["std"] } -dip-sender-runtime-template = { workspace = true, features = ["std"] } +dip-consumer-runtime-template = { workspace = true, features = ["std"] } +dip-provider-runtime-template = { workspace = true, features = ["std"] } dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/lib.rs b/dip-template/runtimes/xcm-tests/src/lib.rs index 9eb8ed5de7..fa1dd5c00f 100644 --- a/dip-template/runtimes/xcm-tests/src/lib.rs +++ b/dip-template/runtimes/xcm-tests/src/lib.rs @@ -27,22 +27,22 @@ mod relay; mod tests; decl_test_parachain! { - pub struct SenderParachain { - Runtime = para::sender::Runtime, - RuntimeOrigin = para::sender::RuntimeOrigin, - XcmpMessageHandler = para::sender::XcmpQueue, - DmpMessageHandler = para::sender::DmpQueue, - new_ext = para::sender::para_ext(), + pub struct ProviderParachain { + Runtime = para::provider::Runtime, + RuntimeOrigin = para::provider::RuntimeOrigin, + XcmpMessageHandler = para::provider::XcmpQueue, + DmpMessageHandler = para::provider::DmpQueue, + new_ext = para::provider::para_ext(), } } decl_test_parachain! { - pub struct ReceiverParachain { - Runtime = para::receiver::Runtime, - RuntimeOrigin = para::receiver::RuntimeOrigin, - XcmpMessageHandler = para::receiver::XcmpQueue, - DmpMessageHandler = para::receiver::DmpQueue, - new_ext = para::receiver::para_ext(), + pub struct ConsumerParachain { + Runtime = para::consumer::Runtime, + RuntimeOrigin = para::consumer::RuntimeOrigin, + XcmpMessageHandler = para::consumer::XcmpQueue, + DmpMessageHandler = para::consumer::DmpQueue, + new_ext = para::consumer::para_ext(), } } @@ -52,8 +52,8 @@ decl_test_network! { parachains = vec![ // TODO: Change when and if the macro will allow arbitrary expressions. // Until then, these have to match the PARA_ID consts in the para submodules. - (2_000, SenderParachain), - (2_001, ReceiverParachain), + (2_000, ProviderParachain), + (2_001, ConsumerParachain), ], } } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index 15fc36c469..77e5f3907f 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -21,11 +21,11 @@ use frame_support::traits::GenesisBuild; use sp_io::TestExternalities; use xcm_emulator::decl_test_parachain; -pub(super) mod sender { - pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; +pub(super) mod provider { + pub(crate) use dip_provider_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; - use dip_sender_runtime_template::{AccountId, System}; + use dip_provider_runtime_template::{AccountId, System}; use kilt_support::deposit::Deposit; use sp_core::{ecdsa, ed25519, sr25519, Pair}; @@ -82,7 +82,7 @@ pub(super) mod sender { } decl_test_parachain! { - pub struct SenderParachain { + pub struct ProviderParachain { Runtime = Runtime, RuntimeOrigin = RuntimeOrigin, XcmpMessageHandler = XcmpQueue, @@ -92,12 +92,12 @@ pub(super) mod sender { } } -pub(super) mod receiver { - pub(crate) use dip_receiver_runtime_template::{ +pub(super) mod consumer { + pub(crate) use dip_consumer_runtime_template::{ AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, UNIT, }; - use dip_receiver_runtime_template::System; + use dip_consumer_runtime_template::System; use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; use xcm_executor::traits::Convert; @@ -107,9 +107,9 @@ pub(super) mod receiver { pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([90u8; 32]); const INITIAL_BALANCE: Balance = 100_000 * UNIT; - pub(crate) fn sender_parachain_account() -> AccountId { - AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(sender::PARA_ID))).into()) - .expect("Conversion of account from sender parachain to receiver parachain should not fail.") + pub(crate) fn provider_parachain_account() -> AccountId { + AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(provider::PARA_ID))).into()) + .expect("Conversion of account from provider parachain to consumer parachain should not fail.") } pub(crate) fn para_ext() -> TestExternalities { @@ -126,7 +126,7 @@ pub(super) mod receiver { pallet_balances::GenesisConfig:: { balances: vec![ - (sender_parachain_account(), INITIAL_BALANCE), + (provider_parachain_account(), INITIAL_BALANCE), (DISPATCHER_ACCOUNT, INITIAL_BALANCE), ], } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index e8cabc3997..9c04ad8abb 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -23,7 +23,7 @@ use dip_support::latest::Proof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use pallet_did_lookup::linkable_account::LinkableAccountId; -use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleRootGenerator}; use sp_core::Pair; use xcm::latest::{ Junction::Parachain, @@ -33,30 +33,30 @@ use xcm::latest::{ use xcm_emulator::TestExt; use cumulus_pallet_xcmp_queue::Event as XcmpEvent; -use dip_receiver_runtime_template::{ - DidIdentifier, DidLookup, DipReceiver, Runtime as ReceiverRuntime, RuntimeCall as ReceiverRuntimeCall, +use dip_consumer_runtime_template::{ + DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; -use dip_sender_runtime_template::{AccountId as SenderAccountId, DipSender, Runtime as SenderRuntime}; +use dip_provider_runtime_template::{AccountId as ProviderAccountId, DipProvider, Runtime as ProviderRuntime}; #[test] fn commit_identity() { Network::reset(); - let did: DidIdentifier = para::sender::did_auth_key().public().into(); + let did: DidIdentifier = para::provider::did_auth_key().public().into(); - // 1. Send identity proof from DIP sender to DIP receiver. - SenderParachain::execute_with(|| { - assert_ok!(DipSender::commit_identity( - RawOrigin::Signed(SenderAccountId::from([0u8; 32])).into(), + // 1. Send identity proof from DIP provider to DIP consumer. + ProviderParachain::execute_with(|| { + assert_ok!(DipProvider::commit_identity( + RawOrigin::Signed(ProviderAccountId::from([0u8; 32])).into(), did.clone(), - Box::new(ParentThen(X1(Parachain(para::receiver::PARA_ID))).into()), + Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), Weight::from_ref_time(4_000), )); }); - // 2. Verify that the proof has made it to the DIP receiver. - ReceiverParachain::execute_with(|| { + // 2. Verify that the proof has made it to the DIP consumer. + ConsumerParachain::execute_with(|| { // 2.1 Verify that there was no XCM error. assert!(!System::events().iter().any(|r| matches!( r.event, @@ -67,32 +67,35 @@ fn commit_identity() { }) ))); // 2.2 Verify the proof digest was stored correctly. - assert!(DipReceiver::identity_proofs(&did).is_some()); + assert!(DipConsumer::identity_proofs(&did).is_some()); + }); + // 3. Call an extrinsic on the consumer chain with a valid proof + let did_details = ProviderParachain::execute_with(|| { + Did::get(&did).expect("DID details should be stored on the provider chain.") }); - // 3. Call an extrinsic on the receiver chain with a valid proof - let did_details = - SenderParachain::execute_with(|| Did::get(&did).expect("DID details should be stored on the sender chain.")); // 3.1 Generate a proof - let CompleteMerkleProof { proof, .. } = - DidMerkleRootGenerator::::generate_proof(&did_details, [did_details.authentication_key].iter()) - .expect("Proof generation should not fail"); - // 3.2 Call the `dispatch_as` extrinsic on the receiver chain with the generated + let CompleteMerkleProof { proof, .. } = DidMerkleRootGenerator::::generate_proof( + &did_details, + [did_details.authentication_key].iter(), + ) + .expect("Proof generation should not fail"); + // 3.2 Call the `dispatch_as` extrinsic on the consumer chain with the generated // proof - ReceiverParachain::execute_with(|| { - assert_ok!(DipReceiver::dispatch_as( - RawOrigin::Signed(para::receiver::DISPATCHER_ACCOUNT).into(), + ConsumerParachain::execute_with(|| { + assert_ok!(DipConsumer::dispatch_as( + RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), Proof { blinded: proof.blinded, revealed: proof.revealed, } .into(), - Box::new(ReceiverRuntimeCall::DidLookup(pallet_did_lookup::Call::< - ReceiverRuntime, + Box::new(ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::< + ConsumerRuntime, >::associate_sender {})), )); // Verify the account -> DID link exists and contains the right information - let linked_did = DidLookup::connected_dids::(para::receiver::DISPATCHER_ACCOUNT.into()) + let linked_did = DidLookup::connected_dids::(para::consumer::DISPATCHER_ACCOUNT.into()) .map(|link| link.did); assert_eq!(linked_did, Some(did)); }); diff --git a/pallets/pallet-dip-receiver/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml similarity index 97% rename from pallets/pallet-dip-receiver/Cargo.toml rename to pallets/pallet-dip-consumer/Cargo.toml index 6a6d6f98d2..a59f1e8f9d 100644 --- a/pallets/pallet-dip-receiver/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -5,7 +5,7 @@ documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "pallet-dip-receiver" +name = "pallet-dip-consumer" readme.workspace = true repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs similarity index 100% rename from pallets/pallet-dip-receiver/src/lib.rs rename to pallets/pallet-dip-consumer/src/lib.rs diff --git a/pallets/pallet-dip-receiver/src/origin.rs b/pallets/pallet-dip-consumer/src/origin.rs similarity index 100% rename from pallets/pallet-dip-receiver/src/origin.rs rename to pallets/pallet-dip-consumer/src/origin.rs diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs similarity index 100% rename from pallets/pallet-dip-receiver/src/traits.rs rename to pallets/pallet-dip-consumer/src/traits.rs diff --git a/pallets/pallet-dip-sender/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml similarity index 97% rename from pallets/pallet-dip-sender/Cargo.toml rename to pallets/pallet-dip-provider/Cargo.toml index a5d54cf134..6283ad284a 100644 --- a/pallets/pallet-dip-sender/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -5,7 +5,7 @@ documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "pallet-dip-sender" +name = "pallet-dip-provider" readme.workspace = true repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-sender/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs similarity index 100% rename from pallets/pallet-dip-sender/src/lib.rs rename to pallets/pallet-dip-provider/src/lib.rs diff --git a/pallets/pallet-dip-sender/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs similarity index 99% rename from pallets/pallet-dip-sender/src/traits.rs rename to pallets/pallet-dip-provider/src/traits.rs index 6667699cc0..a589fa3b1d 100644 --- a/pallets/pallet-dip-sender/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -122,7 +122,7 @@ pub mod identity_dispatch { .expect("Failed to build call"); // TODO: Set an error handler and an appendix to refund any leftover funds to - // the sender parachain sovereign account. + // the provider parachain sovereign account. let operation = [vec![ WithdrawAsset(asset.clone().into()), BuyExecution { diff --git a/runtime-api/dip-sender/Cargo.toml b/runtime-api/dip-provider/Cargo.toml similarity index 80% rename from runtime-api/dip-sender/Cargo.toml rename to runtime-api/dip-provider/Cargo.toml index 12b20140f3..3ba00df694 100644 --- a/runtime-api/dip-sender/Cargo.toml +++ b/runtime-api/dip-provider/Cargo.toml @@ -7,8 +7,8 @@ license-file.workspace = true readme.workspace = true repository.workspace = true version.workspace = true -name = "kilt-runtime-api-dip-sender" -description = "Runtime APIs for integrating the DIP sender component." +name = "kilt-runtime-api-dip-provider" +description = "Runtime APIs for integrating the DIP provider component." [dependencies] # External dependencies diff --git a/runtime-api/dip-sender/src/lib.rs b/runtime-api/dip-provider/src/lib.rs similarity index 93% rename from runtime-api/dip-sender/src/lib.rs rename to runtime-api/dip-provider/src/lib.rs index ce420714e5..2cdcb534c1 100644 --- a/runtime-api/dip-sender/src/lib.rs +++ b/runtime-api/dip-provider/src/lib.rs @@ -21,7 +21,7 @@ use parity_scale_codec::Codec; sp_api::decl_runtime_apis! { - pub trait DipSender where + pub trait DipProvider where DidIdentifier: Codec, KeyId: Codec, KeyIds: Codec + IntoIterator, diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index c9467dd679..6d6284722e 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -28,8 +28,8 @@ delegation = {workspace = true, optional = true} did.workspace = true dip-support.workspace = true pallet-did-lookup = {workspace = true, optional = true} -pallet-dip-receiver.workspace = true -pallet-dip-sender.workspace = true +pallet-dip-consumer.workspace = true +pallet-dip-provider.workspace = true pallet-inflation = {workspace = true, optional = true} kilt-support.workspace = true parachain-staking.workspace = true @@ -66,7 +66,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "ctype/runtime-benchmarks", "did/runtime-benchmarks", - "pallet-dip-receiver/runtime-benchmarks", + "pallet-dip-consumer/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "kilt-support/runtime-benchmarks", @@ -92,8 +92,8 @@ std = [ "log/std", "pallet-authorship/std", "pallet-balances/std", - "pallet-dip-receiver/std", - "pallet-dip-sender/std", + "pallet-dip-consumer/std", + "pallet-dip-provider/std", "pallet-membership/std", "pallet-transaction-payment/std", "parachain-staking/std", diff --git a/runtimes/common/src/dip/receiver.rs b/runtimes/common/src/dip/consumer.rs similarity index 95% rename from runtimes/common/src/dip/receiver.rs rename to runtimes/common/src/dip/consumer.rs index f63470cd85..8c6c492850 100644 --- a/runtimes/common/src/dip/receiver.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -19,12 +19,12 @@ use did::did_details::DidPublicKeyDetails; use dip_support::{v1, VersionedIdentityProof}; use frame_support::RuntimeDebug; -use pallet_dip_receiver::traits::IdentityProofVerifier; +use pallet_dip_consumer::traits::IdentityProofVerifier; use parity_scale_codec::Encode; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -use crate::dip::{sender, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; +use crate::dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; // TODO: Avoid repetition of the same key if it appears multiple times, e.g., by // having a vector of `KeyRelationship` instead. @@ -53,7 +53,7 @@ where BlockNumber: Encode + Clone + Ord, Hasher: sp_core::Hasher, { - type BlindedValue = Vec; + type BlindedValue = Vec; // TODO: Proper error handling type Error = (); type ProofDigest = ::Out; diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index 5f292b3662..645f1ea4d6 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -22,8 +22,8 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::vec::Vec; -pub mod receiver; -pub mod sender; +pub mod consumer; +pub mod provider; #[cfg(test)] mod tests; diff --git a/runtimes/common/src/dip/sender.rs b/runtimes/common/src/dip/provider.rs similarity index 99% rename from runtimes/common/src/dip/sender.rs rename to runtimes/common/src/dip/provider.rs index 8ae4658ce2..0f555a199a 100644 --- a/runtimes/common/src/dip/sender.rs +++ b/runtimes/common/src/dip/provider.rs @@ -19,7 +19,7 @@ use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; use dip_support::latest::Proof; use frame_support::RuntimeDebug; -use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; +use pallet_dip_provider::traits::{IdentityProofGenerator, IdentityProvider}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet, marker::PhantomData, vec::Vec}; diff --git a/runtimes/common/src/dip/tests.rs b/runtimes/common/src/dip/tests.rs index ae88fb7362..b1ce9f9430 100644 --- a/runtimes/common/src/dip/tests.rs +++ b/runtimes/common/src/dip/tests.rs @@ -28,7 +28,7 @@ use frame_system::{ mocking::{MockBlock, MockUncheckedExtrinsic}, EnsureSigned, RawOrigin, }; -use pallet_dip_receiver::traits::IdentityProofVerifier; +use pallet_dip_consumer::traits::IdentityProofVerifier; use parity_scale_codec::Encode; use sp_core::{ecdsa, ed25519, sr25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; use sp_io::TestExternalities; @@ -40,8 +40,8 @@ use sp_runtime::{ use sp_std::collections::btree_set::BTreeSet; use crate::dip::{ - receiver::DidMerkleProofVerifier, - sender::{CompleteMerkleProof, DidMerkleRootGenerator}, + consumer::DidMerkleProofVerifier, + provider::{CompleteMerkleProof, DidMerkleRootGenerator}, ProofLeaf, }; From 738c08e254107722e550ca49f26a3002580ee62d Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 May 2023 16:05:27 +0200 Subject: [PATCH 05/28] feat: add DID signature verification and replay protection (#516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/KILTprotocol/ticket/issues/2553. The main changes are: * Removed half-baked versioning support, to re-introduce a proper one once versioning and version negotiation will be tackeld * Moved most of the logic from the `runtime-common` crate to a new `kilt-dip-support` crate, after making all the types generic over all the runtime configurations * Updated the `pallet-dip-consumer` pallet to have a bunch more details, more info below * Updated the XCM-simulator based tests ## Updates to the `dip-support` crate The `dip-support` does not contain any versioned types anymore. Support for versioning was half-baked and has been removed. Proper thought around versioning and version negotiation will happen in the (short) future. ## New `kilt-dip-support` crate The new `kilt-dip-support` crate contains types and traits that are not generic over ANY DIP deployment, but that can be used by every consumer that relies on the KILT chain as their provider. Hence, types are opinionated towards the way KILT implements the DIP protocol, but generic over the runtime configuration. The most relevant types are `MerkleProofAndDidSignatureVerifier`, exported from the root, `DidMerkleProofVerifier`, exported from the `merkle` module, and `DidSignatureAndCallVerifier`, exported from the `did` module. When they are chained together, they allow a user to submit a Merkle proof revealing parts of their DID Document, and to use one of the revealed keys to sign a specifically-crafted payload which provides authenticity guarantees of the DID subject. The default verification logic for the DID signature is the following: 1. Verify that the block number specified in the signature is no older than `SIGNATURE_VALIDITY` blocks, as specified by the runtime 2. Verify that the signature can be verified over the encoded tuple formed by `(call, identity_details, submitter_address, block_number, genesis_hash, signed_extra)`, where `signed_extra` can be anything more that the runtime might require be included in the signature. 3. Verify that one of the keys revealed in the Merkle proof can be used to verify the provided signature 4. [OPTIONAL] If the type variant which also performs authorization checks on the call itself is used, i.e., the `DidSignatureAndCallVerifier` type, then the verification will also include checking whether the used verification relationship can be used to dispatch the specified call or not, as we do ourselves in our did pallet within the `submit_did_call`, but in a more generic way. The verification logic for Merkle proofs has remained unchanged, and it can now be chained with the DID verification logic. ## Refreshed `pallet-dip-consumer` The pallet has been updated so that now the `dispatch_as` extrinsic takes a generic `Proof` instead of an `IdentityProof`, and internally performs the following checks: 1. Verify the origin is a signed origin (as before) 2. Verify that the call passes a preliminary filter, before any heavy computation is executed on the provided proof, via the `DipCallOriginFilter` type. This step will typically immediately filter out any calls that cannot be called with a Dip origin regardless of the content of the proof. 3. Retrieves the identity details from storage, if they exist 4. Delegate everything to the `ProofVerifier`, which has a mutable reference to the details fetched at step 3, in case they need to update some values that will be written to storage (e.g., the nonce) 5. The maybe mutated details are written back into storage 6. The call is dispatched with the new `DipOrigin`, which carries the additional information as returned by the `ProofVerifier`. In the demo runtime, this will be the set of keys revealed as part of the Merkle proof, that have been verified, parsed, and used for verifying the DID signature provided as part of the proof. The pallet logic is very simple, and most of the (complex) logic lies in the types that have been exposed as part of the `kilt-dip-support` crate. This makes the pallet easier to understand, and more generic to be used in different contexts potentially for different identity providers as well, where each provider might require users to provide a different identity proof. ## Minor changes Other things have been updated to reflect the relocation of some files into the new `kilt-dip-support` crate. ## How to test 1. Build [this version](https://github.com/KILTprotocol/sdk-js/pull/751) of the SDK 2. Clone and `yarn install && yarn build` [this version](https://github.com/KILTprotocol/polkadot-apps/pull/8) of the PolkadotJS apps 3. Copy-paste the `@kiltprotocol/type-definitions` output into the root `node_modules` folder of the PolkadotJS apps 4. Follow the steps to set up the HRMP channels, create the DIDs, and push the identity commitment from provider to consumer 5. Use [this version](https://github.com/KILTprotocol/kilt-did-utilities/pull/14) of the kilt-did-utilities CLI tool to generate a valid DIP DID signature. 6. Enjoy ☀️☀️☀️ --- Cargo.lock | 23 +- Cargo.toml | 3 +- crates/{dip => dip-support}/Cargo.toml | 2 - .../{dip/src/v1.rs => dip-support/src/lib.rs} | 14 +- crates/dip/src/lib.rs | 73 ---- crates/kilt-dip-support/Cargo.toml | 44 +++ crates/kilt-dip-support/src/did.rs | 239 +++++++++++++ crates/kilt-dip-support/src/lib.rs | 95 ++++++ crates/kilt-dip-support/src/merkle.rs | 251 ++++++++++++++ crates/kilt-dip-support/src/traits.rs | 78 +++++ dip-template/runtimes/dip-consumer/Cargo.toml | 4 + dip-template/runtimes/dip-consumer/src/dip.rs | 95 ++++-- dip-template/runtimes/dip-consumer/src/lib.rs | 5 +- dip-template/runtimes/dip-provider/src/dip.rs | 8 +- dip-template/runtimes/dip-provider/src/lib.rs | 6 +- dip-template/runtimes/xcm-tests/Cargo.toml | 2 +- dip-template/runtimes/xcm-tests/src/tests.rs | 54 ++- pallets/pallet-dip-consumer/src/identity.rs | 43 +++ pallets/pallet-dip-consumer/src/lib.rs | 117 ++++--- pallets/pallet-dip-consumer/src/traits.rs | 47 ++- pallets/pallet-dip-provider/src/lib.rs | 19 +- pallets/pallet-dip-provider/src/traits.rs | 10 +- runtimes/common/Cargo.toml | 4 +- runtimes/common/src/dip/consumer.rs | 118 ------- runtimes/common/src/dip/did.rs | 42 +++ .../common/src/dip/{provider.rs => merkle.rs} | 41 +-- runtimes/common/src/dip/mod.rs | 66 +--- runtimes/common/src/dip/tests.rs | 315 ------------------ runtimes/common/src/lib.rs | 6 +- runtimes/peregrine/src/lib.rs | 4 +- runtimes/spiritnet/src/lib.rs | 4 +- 31 files changed, 1075 insertions(+), 757 deletions(-) rename crates/{dip => dip-support}/Cargo.toml (94%) rename crates/{dip/src/v1.rs => dip-support/src/lib.rs} (74%) delete mode 100644 crates/dip/src/lib.rs create mode 100644 crates/kilt-dip-support/Cargo.toml create mode 100644 crates/kilt-dip-support/src/did.rs create mode 100644 crates/kilt-dip-support/src/lib.rs create mode 100644 crates/kilt-dip-support/src/merkle.rs create mode 100644 crates/kilt-dip-support/src/traits.rs create mode 100644 pallets/pallet-dip-consumer/src/identity.rs delete mode 100644 runtimes/common/src/dip/consumer.rs create mode 100644 runtimes/common/src/dip/did.rs rename runtimes/common/src/dip/{provider.rs => merkle.rs} (86%) delete mode 100644 runtimes/common/src/dip/tests.rs diff --git a/Cargo.lock b/Cargo.lock index a472644713..e51576ab80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2341,10 +2341,12 @@ dependencies = [ "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", + "dip-support", "frame-executive", "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-dip-support", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -2487,7 +2489,6 @@ dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "sp-std", ] [[package]] @@ -2498,9 +2499,9 @@ dependencies = [ "did", "dip-consumer-runtime-template", "dip-provider-runtime-template", - "dip-support", "frame-support", "frame-system", + "kilt-dip-support", "kilt-support", "pallet-balances", "pallet-did-lookup", @@ -4211,6 +4212,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "kilt-dip-support" +version = "1.11.0-dev" +dependencies = [ + "did", + "frame-support", + "frame-system", + "pallet-dip-consumer", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "sp-trie", +] + [[package]] name = "kilt-parachain" version = "1.11.0-dev" @@ -9400,10 +9417,10 @@ dependencies = [ "cumulus-primitives-core", "delegation", "did", - "dip-support", "frame-support", "frame-system", "kilt-asset-dids", + "kilt-dip-support", "kilt-support", "log", "pallet-authorship", diff --git a/Cargo.toml b/Cargo.toml index 221c4cac81..4be4c260c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,8 +60,9 @@ parachain-staking = {path = "pallets/parachain-staking", default-features = fals public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) -dip-support = {path = "crates/dip", default-features = false} +dip-support = {path = "crates/dip-support", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} +kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} diff --git a/crates/dip/Cargo.toml b/crates/dip-support/Cargo.toml similarity index 94% rename from crates/dip/Cargo.toml rename to crates/dip-support/Cargo.toml index 0b0a7af1e1..276b3929ac 100644 --- a/crates/dip/Cargo.toml +++ b/crates/dip-support/Cargo.toml @@ -14,7 +14,6 @@ version.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -sp-std.workspace = true # Substrate dependencies frame-support.workspace = true @@ -24,6 +23,5 @@ default = ["std"] std = [ "parity-scale-codec/std", "scale-info/std", - "sp-std/std", "frame-support/std" ] diff --git a/crates/dip/src/v1.rs b/crates/dip-support/src/lib.rs similarity index 74% rename from crates/dip/src/v1.rs rename to crates/dip-support/src/lib.rs index bdbda97bf6..bde5dbfee3 100644 --- a/crates/dip/src/v1.rs +++ b/crates/dip-support/src/lib.rs @@ -16,20 +16,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +// TODO: Crate documentation + +#![cfg_attr(not(feature = "std"), no_std)] + use frame_support::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_std::vec::Vec; -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] pub enum IdentityProofAction { Updated(Identifier, Proof, Details), Deleted(Identifier), } - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct Proof { - pub blinded: BlindedValue, - // TODO: Probably replace with a different data structure for better lookup capabilities - pub revealed: Vec, -} diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs deleted file mode 100644 index a795d7c855..0000000000 --- a/crates/dip/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -// TODO: Crate documentation - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -// Export v1 behind a namespace and also as the latest -pub mod v1; -pub mod latest { - pub use crate::v1::*; -} - -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[non_exhaustive] -pub enum VersionedIdentityProofAction { - #[codec(index = 1)] - V1(v1::IdentityProofAction), -} - -impl From> - for VersionedIdentityProofAction -{ - fn from(value: v1::IdentityProofAction) -> Self { - Self::V1(value) - } -} - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -#[non_exhaustive] -pub enum VersionedIdentityProof { - #[codec(index = 1)] - V1(v1::Proof), -} - -impl From> for VersionedIdentityProof { - fn from(value: v1::Proof) -> Self { - Self::V1(value) - } -} - -impl TryFrom> for v1::Proof { - // Proper error handling - type Error = (); - - fn try_from(value: VersionedIdentityProof) -> Result { - #[allow(irrefutable_let_patterns)] - if let VersionedIdentityProof::V1(v1::Proof { blinded, revealed }) = value { - Ok(Self { blinded, revealed }) - } else { - Err(()) - } - } -} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml new file mode 100644 index 0000000000..ebc86ee9b0 --- /dev/null +++ b/crates/kilt-dip-support/Cargo.toml @@ -0,0 +1,44 @@ +[package] +authors.workspace = true +description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "kilt-dip-support" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +# Internal dependencies +did.workspace = true +pallet-dip-consumer.workspace = true + +# Parity dependencies +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# Substrate dependencies +frame-system.workspace = true +frame-support.workspace = true +sp-runtime.workspace = true +sp-core.workspace = true +sp-trie.workspace = true +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "did/std", + "pallet-dip-consumer/std", + "parity-scale-codec/std", + "scale-info/std", + "frame-system/std", + "frame-support/std", + "sp-runtime/std", + "sp-core/std", + "sp-trie/std", + "sp-std/std" +] +runtime-benchmarks = [] diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs new file mode 100644 index 0000000000..07bd50d2e6 --- /dev/null +++ b/crates/kilt-dip-support/src/did.rs @@ -0,0 +1,239 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{ + did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, + DidSignature, DidVerificationKeyRelationship, +}; +use frame_support::ensure; +use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::{ConstU64, Get, RuntimeDebug}; +use sp_runtime::traits::CheckedSub; +use sp_std::marker::PhantomData; + +use crate::{ + merkle::ProofEntry, + traits::{Bump, DidDipOriginFilter}, +}; + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] +pub struct TimeBoundDidSignature { + pub signature: DidSignature, + pub block_number: BlockNumber, +} + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] +pub struct MerkleEntriesAndDidSignature { + pub merkle_entries: MerkleEntries, + pub did_signature: TimeBoundDidSignature, +} + +/// A type that verifies a DID signature over some DID keys revealed by a +/// previously-verified Merkle proof. It requires the `Details` type to +/// implement the `Bump` trait to avoid replay attacks. The basic verification +/// logic verifies that the signature has been generated over the encoded tuple +/// (call, identity details, submitter_address, submission_block_number, +/// genesis_hash). Additional details can be added to the end of the tuple by +/// providing a `SignedExtraProvider`. +pub struct MerkleRevealedDidSignatureVerifier< + BlockNumber, + Digest, + Details, + AccountId, + MerkleProofEntries, + BlockNumberProvider, + const SIGNATURE_VALIDITY: u64, + GenesisHashProvider, + Hash, + SignedExtraProvider = (), + SignedExtra = (), +>( + #[allow(clippy::type_complexity)] + PhantomData<( + BlockNumber, + Digest, + Details, + AccountId, + MerkleProofEntries, + BlockNumberProvider, + ConstU64, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, + )>, +); + +impl< + Call, + Subject, + BlockNumber, + Digest, + Details, + AccountId, + MerkleProofEntries, + BlockNumberProvider, + const SIGNATURE_VALIDITY: u64, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, + > IdentityProofVerifier + for MerkleRevealedDidSignatureVerifier< + BlockNumber, + Digest, + Details, + AccountId, + MerkleProofEntries, + BlockNumberProvider, + SIGNATURE_VALIDITY, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, + > where + AccountId: Encode, + BlockNumber: Encode + CheckedSub + Into + PartialOrd + sp_std::fmt::Debug, + Call: Encode, + Digest: Encode, + Details: Bump + Encode, + MerkleProofEntries: AsRef<[ProofEntry]>, + BlockNumberProvider: Get, + GenesisHashProvider: Get, + Hash: Encode, + SignedExtraProvider: Get, + SignedExtra: Encode, +{ + // TODO: Error handling + type Error = (); + /// The proof must be a list of Merkle leaves that have been previously + /// verified by the Merkle proof verifier, and the additional DID signature. + type Proof = MerkleEntriesAndDidSignature; + /// The `Details` that are part of the identity details must implement the + /// `Bump` trait. + type IdentityDetails = IdentityDetails; + /// The type of the submitter's accounts. + type Submitter = AccountId; + /// Successful verifications return the verification key used to validate + /// the provided signature and its relationship to the DID subject. + type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); + + fn verify_proof_for_call_against_entry( + call: &Call, + _subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, + ) -> Result { + let block_number = BlockNumberProvider::get(); + let is_signature_fresh = + if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.did_signature.block_number) { + // False if the signature is too old. + blocks_ago_from_now.into() <= SIGNATURE_VALIDITY + } else { + // Signature generated at a future time, not possible to verify. + false + }; + ensure!(is_signature_fresh, ()); + let encoded_payload = ( + call, + &proof_entry.details, + submitter, + &proof.did_signature.block_number, + GenesisHashProvider::get(), + SignedExtraProvider::get(), + ) + .encode(); + // Only consider verification keys from the set of revealed Merkle leaves. + let mut proof_verification_keys = proof.merkle_entries.as_ref().iter().filter_map( + |ProofEntry { + key: DidPublicKeyDetails { key, .. }, + relationship, + }| { + if let DidPublicKey::PublicVerificationKey(k) = key { + Some(( + k, + DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."), + )) + } else { + None + } + }, + ); + let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { + verification_key + .verify_signature(&encoded_payload, &proof.did_signature.signature) + .is_ok() + }); + if let Some((key, relationship)) = valid_signing_key { + proof_entry.details.bump(); + Ok((key.clone(), relationship)) + } else { + Err(()) + } + } +} + +/// A type that chains a DID signature verification, as provided by +/// `MerkleRevealedDidSignatureVerifier`, and a call filtering logic based on +/// the type of key used in the signature. +/// Verification bails out early in case of invalid DID signatures. Otherwise, +/// the retrieved key and its relationship is passed to the call verifier to do +/// some additional lookups on the call. +/// The `CallVerifier` only performs internal checks, while all input and output +/// types are taken from the provided `DidSignatureVerifier` type. +pub struct DidSignatureAndCallVerifier( + PhantomData<(DidSignatureVerifier, CallVerifier)>, +); + +impl IdentityProofVerifier + for DidSignatureAndCallVerifier +where + DidSignatureVerifier: IdentityProofVerifier, + CallVerifier: DidDipOriginFilter, +{ + // FIXME: Better error handling + type Error = (); + /// The input proof is the same accepted by the `DidSignatureVerifier`. + type Proof = DidSignatureVerifier::Proof; + /// The identity details are the same accepted by the + /// `DidSignatureVerifier`. + type IdentityDetails = DidSignatureVerifier::IdentityDetails; + /// The submitter address is the same accepted by the + /// `DidSignatureVerifier`. + type Submitter = DidSignatureVerifier::Submitter; + /// The verification result is the same accepted by the + /// `DidSignatureVerifier`. + type VerificationResult = DidSignatureVerifier::VerificationResult; + + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, + ) -> Result { + let did_signing_key = + DidSignatureVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof) + .map_err(|_| ())?; + CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?; + Ok(did_signing_key) + } +} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs new file mode 100644 index 0000000000..c2f18ba0f8 --- /dev/null +++ b/crates/kilt-dip-support/src/lib.rs @@ -0,0 +1,95 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Crate documentation + +#![cfg_attr(not(feature = "std"), no_std)] + +use pallet_dip_consumer::traits::IdentityProofVerifier; +use sp_std::marker::PhantomData; + +use crate::did::MerkleEntriesAndDidSignature; + +pub mod did; +pub mod merkle; +pub mod traits; + +/// A type that chains a Merkle proof verification with a DID signature +/// verification. The required input of this type is a tuple (A, B) where A is +/// the type of input required by the `MerkleProofVerifier` and B is a +/// `DidSignature`. +/// The successful output of this type is the output type of the +/// `MerkleProofVerifier`, meaning that DID signature verification happens +/// internally and does not transform the result in any way. +pub struct MerkleProofAndDidSignatureVerifier( + PhantomData<(BlockNumber, MerkleProofVerifier, DidSignatureVerifier)>, +); + +impl IdentityProofVerifier + for MerkleProofAndDidSignatureVerifier +where + BlockNumber: Clone, + MerkleProofVerifier: IdentityProofVerifier, + // TODO: get rid of this if possible + MerkleProofVerifier::VerificationResult: Clone, + DidSignatureVerifier: IdentityProofVerifier< + Call, + Subject, + Proof = MerkleEntriesAndDidSignature, + IdentityDetails = MerkleProofVerifier::IdentityDetails, + Submitter = MerkleProofVerifier::Submitter, + >, +{ + // FIXME: Better error handling + type Error = (); + // FIXME: Better type declaration + type Proof = MerkleEntriesAndDidSignature; + type IdentityDetails = DidSignatureVerifier::IdentityDetails; + type Submitter = MerkleProofVerifier::Submitter; + type VerificationResult = MerkleProofVerifier::VerificationResult; + + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, + ) -> Result { + let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + &proof.merkle_entries, + ) + .map_err(|_| ())?; + DidSignatureVerifier::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + // FIXME: Remove `clone()` requirement + &MerkleEntriesAndDidSignature { + merkle_entries: merkle_proof_verification.clone(), + did_signature: proof.did_signature.clone(), + }, + ) + .map_err(|_| ())?; + Ok(merkle_proof_verification) + } +} diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs new file mode 100644 index 0000000000..d86e84475a --- /dev/null +++ b/crates/kilt-dip-support/src/merkle.rs @@ -0,0 +1,251 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use frame_support::{traits::ConstU32, RuntimeDebug}; +use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::BoundedVec; +use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_trie::{verify_trie_proof, LayoutV1}; + +pub type BlindedValue = Vec; + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] +pub struct MerkleProof { + pub blinded: BlindedValue, + // TODO: Probably replace with a different data structure for better lookup capabilities + pub revealed: Vec, +} + +#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] +pub enum DidKeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} + +impl From for DidKeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) + } +} + +impl TryFrom for DidVerificationKeyRelationship { + // TODO: Error handling + type Error = (); + + fn try_from(value: DidKeyRelationship) -> Result { + if let DidKeyRelationship::Verification(rel) = value { + Ok(rel) + } else { + Err(()) + } + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceKey(pub KeyId, pub DidKeyRelationship); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceValue; +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsKey(pub KeyId); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsValue(pub DidPublicKeyDetails); + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub enum ProofLeaf { + // The key and value for the leaves of a merkle proof that contain a reference + // (by ID) to the key details, provided in a separate leaf. + KeyReference(KeyReferenceKey, KeyReferenceValue), + // The key and value for the leaves of a merkle proof that contain the actual + // details of a DID public key. The key is the ID of the key, and the value is its details, including creation + // block number. + KeyDetails(KeyDetailsKey, KeyDetailsValue), +} + +impl ProofLeaf +where + KeyId: Encode, +{ + pub fn encoded_key(&self) -> Vec { + match self { + ProofLeaf::KeyReference(key, _) => key.encode(), + ProofLeaf::KeyDetails(key, _) => key.encode(), + } + } +} + +impl ProofLeaf +where + BlockNumber: Encode, +{ + pub fn encoded_value(&self) -> Vec { + match self { + ProofLeaf::KeyReference(_, value) => value.encode(), + ProofLeaf::KeyDetails(_, value) => value.encode(), + } + } +} + +// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by +// having a vector of `DidKeyRelationship` instead. +#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] +pub struct ProofEntry { + pub key: DidPublicKeyDetails, + pub relationship: DidKeyRelationship, +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for ProofEntry +where + BlockNumber: Default, +{ + fn default() -> Self { + Self { + key: DidPublicKeyDetails { + key: did::did_details::DidPublicKey::PublicEncryptionKey(did::did_details::DidEncryptionKey::X25519( + [0u8; 32], + )), + block_number: BlockNumber::default(), + }, + relationship: DidVerificationKeyRelationship::Authentication.into(), + } + } +} + +// Contains the list of revealed public keys after a given Merkle proof has been +// correctly verified. +#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] +pub struct VerificationResult( + pub BoundedVec, ConstU32>, +); + +impl TryFrom>> + for VerificationResult +{ + // TODO: Better error handling + type Error = (); + + fn try_from(value: Vec>) -> Result { + let bounded_inner = value.try_into().map_err(|_| ())?; + Ok(Self(bounded_inner)) + } +} + +impl AsRef<[ProofEntry]> + for VerificationResult +{ + fn as_ref(&self) -> &[ProofEntry] { + self.0.as_ref() + } +} + +/// A type that verifies a Merkle proof that reveals some leaves representing +/// keys in a DID Document. +/// Can also be used on its own, without any DID signature verification. +pub struct DidMerkleProofVerifier( + PhantomData<( + Hasher, + AccountId, + KeyId, + BlockNumber, + Details, + ConstU32, + )>, +); + +impl + IdentityProofVerifier + for DidMerkleProofVerifier +where + // TODO: Remove `Debug` bound + BlockNumber: Encode + Clone + Debug, + Hasher: sp_core::Hasher, + KeyId: Encode + Clone + Ord + Into, +{ + // TODO: Proper error handling + type Error = (); + type Proof = MerkleProof>, ProofLeaf>; + type IdentityDetails = IdentityDetails; + type Submitter = AccountId; + type VerificationResult = VerificationResult; + + fn verify_proof_for_call_against_entry( + _call: &Call, + _subject: &Subject, + _submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, + ) -> Result { + // TODO: more efficient by removing cloning and/or collecting. + // Did not find another way of mapping a Vec<(Vec, Vec)> to a + // Vec<(Vec, Option>)>. + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + verify_trie_proof::, _, _, _>( + &proof_entry.digest.clone().into(), + &proof.blinded, + &proof_leaves, + ) + .map_err(|_| ())?; + + // At this point, we know the proof is valid. We just need to map the revealed + // leaves to something the consumer can easily operate on. + + // Create a map of the revealed public keys + //TODO: Avoid cloning, and use a map of references for the lookup + let public_keys: BTreeMap> = proof + .revealed + .clone() + .into_iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { + Some((key_id, key_details)) + } else { + None + } + }) + .collect(); + // Create a list of the revealed keys by consuming the provided key reference + // leaves, and looking up the full details from the just-built `public_keys` + // map. + let keys: Vec> = proof + .revealed + .iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { + // TODO: Better error handling. + let key_details = public_keys + .get(key_id) + .expect("Key ID should be present in the map of revealed public keys."); + Some(ProofEntry { + key: key_details.clone(), + relationship: *key_relationship, + }) + } else { + None + } + }) + .collect(); + keys.try_into() + } +} diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs new file mode 100644 index 0000000000..56d4dd09b1 --- /dev/null +++ b/crates/kilt-dip-support/src/traits.rs @@ -0,0 +1,78 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use sp_core::Get; +use sp_runtime::traits::{CheckedAdd, One, Zero}; +use sp_std::marker::PhantomData; + +// TODO: Switch to the `Incrementable` trait once it's added to the root of +// `frame_support`. +/// A trait for "bumpable" types, i.e., types that have some notion of order of +/// its members. +pub trait Bump { + /// Bump the type instance to its next value. Overflows are assumed to be + /// taken care of by the type internal logic. + fn bump(&mut self); +} + +impl Bump for T +where + T: CheckedAdd + Zero + One, +{ + // FIXME: Better implementation? + fn bump(&mut self) { + *self = self.checked_add(&Self::one()).unwrap_or_else(Self::zero); + } +} + +/// A trait for types that implement some sort of access control logic on the +/// provided input `Call` type. +pub trait DidDipOriginFilter { + /// The error type for cases where the checks fail. + type Error; + /// The type of additional information required by the type to perform the + /// checks on the `Call` input. + type OriginInfo; + /// The success type for cases where the checks succeed. + type Success; + + fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; +} + +pub struct GenesisProvider(PhantomData); + +impl Get for GenesisProvider +where + T: frame_system::Config, + T::BlockNumber: Zero, +{ + fn get() -> T::Hash { + frame_system::Pallet::::block_hash(T::BlockNumber::zero()) + } +} + +pub struct BlockNumberProvider(PhantomData); + +impl Get for BlockNumberProvider +where + T: frame_system::Config, +{ + fn get() -> T::BlockNumber { + frame_system::Pallet::::block_number() + } +} diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index 2073d9d275..de8f00d270 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -18,7 +18,9 @@ parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # DIP +dip-support.workspace = true did.workspace = true +kilt-dip-support.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true runtime-common.workspace = true @@ -75,7 +77,9 @@ default = [ std = [ "parity-scale-codec/std", "scale-info/std", + "dip-support/std", "did/std", + "kilt-dip-support/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", "runtime-common/std", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 77fc5e79cf..639ff16e6b 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,34 +16,71 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::DidVerificationKeyRelationship; -use pallet_dip_consumer::traits::DipCallOriginFilter; -use runtime_common::dip::{ - consumer::{DidMerkleProofVerifier, VerificationResult}, - ProofLeaf, +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; +use frame_support::traits::Contains; +use kilt_dip_support::{ + did::{DidSignatureAndCallVerifier, MerkleEntriesAndDidSignature, MerkleRevealedDidSignatureVerifier}, + merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, + traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, + MerkleProofAndDidSignatureVerifier, }; +use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_std::vec::Vec; -use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; + +pub type MerkleProofVerifier = DidMerkleProofVerifier, BlockNumber, u128, 10>; +pub type MerkleProofVerifierOutputOf = + >::VerificationResult; +pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< + BlockNumber, + Hash, + u128, + AccountId, + MerkleProofVerifierOutputOf, + BlockNumberProvider, + // Signatures are valid for 50 blocks + 50, + GenesisProvider, + Hash, +>; impl pallet_dip_consumer::Config for Runtime { - type BlindedValue = Vec>; - type DipCallOriginFilter = DipCallFilter; + type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; - type ProofLeaf = ProofLeaf; + type IdentityDetails = u128; + type Proof = MerkleEntriesAndDidSignature>, ProofLeaf>, BlockNumber>; type ProofDigest = Hash; - type ProofVerifier = DidMerkleProofVerifier; + type ProofVerifier = MerkleProofAndDidSignatureVerifier< + BlockNumber, + MerkleProofVerifier, + DidSignatureAndCallVerifier, DipCallFilter>, + >; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; } +pub struct PreliminaryDipOriginFilter; + +impl Contains for PreliminaryDipOriginFilter { + fn contains(t: &RuntimeCall) -> bool { + matches!( + t, + RuntimeCall::DidLookup { .. } + | RuntimeCall::Utility(pallet_utility::Call::batch { .. }) + | RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) + | RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) + ) + } +} + fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { RuntimeCall::DidLookup { .. } => Some(DidVerificationKeyRelationship::Authentication), - RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls).ok(), - RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls).ok(), - RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls).ok(), + RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls.iter()).ok(), + RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls.iter()).ok(), + RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls.iter()).ok(), _ => None, } } @@ -51,11 +88,15 @@ fn derive_verification_key_relationship(call: &RuntimeCall) -> Option Result { - let first_call_relationship = calls.get(0).and_then(derive_verification_key_relationship).ok_or(())?; +fn single_key_relationship<'a>( + calls: impl Iterator, +) -> Result { + let mut calls = calls.peekable(); + let first_call_relationship = calls + .peek() + .and_then(|k| derive_verification_key_relationship(k)) + .ok_or(())?; calls - .iter() - .skip(1) .map(derive_verification_key_relationship) .try_fold(first_call_relationship, |acc, next| { if next == Some(acc) { @@ -68,15 +109,15 @@ fn single_key_relationship(calls: &[RuntimeCall]) -> Result for DipCallFilter { +impl DidDipOriginFilter for DipCallFilter { type Error = (); - type Proof = VerificationResult; + type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); type Success = (); // Accepts only a DipOrigin for the DidLookup pallet calls. - fn check_proof(call: RuntimeCall, proof: Self::Proof) -> Result { - let key_relationship = single_key_relationship(&[call])?; - if proof.0.iter().any(|l| l.relationship == key_relationship.into()) { + fn check_call_origin_info(call: &RuntimeCall, info: &Self::OriginInfo) -> Result { + let key_relationship = single_key_relationship([call].into_iter())?; + if info.1 == key_relationship { Ok(()) } else { Err(()) @@ -95,21 +136,21 @@ mod dip_call_origin_filter_tests { // Can call DidLookup functions with an authentication key let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); assert_eq!( - single_key_relationship(&[did_lookup_call]), + single_key_relationship(vec![did_lookup_call].iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call System functions with a DID key (hence a DIP origin) let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(&[system_call]), ()); + assert_err!(single_key_relationship(vec![system_call].iter()), ()); // Can't call empty batch with a DID key let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(&[empty_batch_call]), ()); + assert_err!(single_key_relationship(vec![empty_batch_call].iter()), ()); // Can call batch with a DipLookup with an authentication key let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], }); assert_eq!( - single_key_relationship(&[did_lookup_batch_call]), + single_key_relationship(vec![did_lookup_batch_call].iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call a batch with different required keys @@ -121,6 +162,6 @@ mod dip_call_origin_filter_tests { frame_system::Call::remark { remark: vec![] }.into(), ], }); - assert_err!(single_key_relationship(&[did_lookup_batch_call]), ()); + assert_err!(single_key_relationship(vec![did_lookup_batch_call].iter()), ()); } } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index 955a43885f..602e4af539 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,6 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use kilt_dip_support::merkle::VerificationResult; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -366,8 +367,8 @@ impl pallet_did_lookup::Config for Runtime { type Currency = Balances; type Deposit = ConstU128; type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin; - type OriginSuccess = DipOrigin; + type EnsureOrigin = EnsureDipOrigin>; + type OriginSuccess = DipOrigin>; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 6fa0d577bd..e358eff8a5 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,10 +17,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::did_details::DidDetails; -use dip_support::VersionedIdentityProofAction; +use dip_support::IdentityProofAction; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; -use runtime_common::dip::provider::{DidIdentityProvider, DidMerkleRootGenerator}; +use runtime_common::dip::{did::DidIdentityProvider, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; @@ -34,7 +34,7 @@ enum ConsumerParachainCalls { #[derive(Encode, Decode)] enum ConsumerParachainDipConsumerCalls { #[codec(index = 0)] - ProcessIdentityAction(VersionedIdentityProofAction), + ProcessIdentityAction(IdentityProofAction), } pub struct ConsumerParachainTxBuilder; @@ -43,7 +43,7 @@ impl TxBuilder for ConsumerParachainTxBuilder { fn build( _dest: MultiLocation, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index d1db46563a..b3766aa160 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -51,7 +51,7 @@ use pallet_collator_selection::IdentityCollator; use pallet_dip_provider::traits::IdentityProvider; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; -use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleProofOf, DidMerkleRootGenerator}; use sp_api::impl_runtime_apis; use sp_consensus_aura::SlotDuration; use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; @@ -530,8 +530,8 @@ impl_runtime_apis! { // TODO: `keys` could and should be a BTreeSet, but it makes it more complicated for clients to build the type. So we use a Vec, since the keys are deduplicated anyway at proof creation time. // TODO: Support generating different versions of the proof, based on the provided parameter - impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { - fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { + impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { + fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) } else { diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 91e6ae3fbb..6be31a5cb1 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -15,9 +15,9 @@ cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } dip-consumer-runtime-template = { workspace = true, features = ["std"] } dip-provider-runtime-template = { workspace = true, features = ["std"] } -dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } +kilt-dip-support = { workspace = true, features = ["std"] } kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-did-lookup = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 9c04ad8abb..a868a64ea1 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -18,13 +18,18 @@ use super::*; -use did::Did; -use dip_support::latest::Proof; +use did::{Did, DidSignature}; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; +use kilt_dip_support::{ + did::{MerkleEntriesAndDidSignature, TimeBoundDidSignature}, + merkle::MerkleProof, +}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleRootGenerator}; +use parity_scale_codec::Encode; +use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleRootGenerator}; use sp_core::Pair; +use sp_runtime::traits::Zero; use xcm::latest::{ Junction::Parachain, Junctions::{Here, X1}, @@ -34,7 +39,7 @@ use xcm_emulator::TestExt; use cumulus_pallet_xcmp_queue::Event as XcmpEvent; use dip_consumer_runtime_template::{ - DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, + BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; use dip_provider_runtime_template::{AccountId as ProviderAccountId, DipProvider, Runtime as ProviderRuntime}; @@ -69,34 +74,53 @@ fn commit_identity() { // 2.2 Verify the proof digest was stored correctly. assert!(DipConsumer::identity_proofs(&did).is_some()); }); - // 3. Call an extrinsic on the consumer chain with a valid proof + // 3. Call an extrinsic on the consumer chain with a valid proof and signature let did_details = ProviderParachain::execute_with(|| { Did::get(&did).expect("DID details should be stored on the provider chain.") }); + let call = ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::::associate_sender {}); // 3.1 Generate a proof let CompleteMerkleProof { proof, .. } = DidMerkleRootGenerator::::generate_proof( &did_details, [did_details.authentication_key].iter(), ) .expect("Proof generation should not fail"); - // 3.2 Call the `dispatch_as` extrinsic on the consumer chain with the generated + // 3.2 Generate a DID signature + let genesis_hash = + ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())); + let system_block = ConsumerParachain::execute_with(frame_system::Pallet::::block_number); + let payload = ( + call.clone(), + 0u128, + para::consumer::DISPATCHER_ACCOUNT, + system_block, + genesis_hash, + ); + let signature: DidSignature = para::provider::did_auth_key().sign(&payload.encode()).into(); + // 3.3 Call the `dispatch_as` extrinsic on the consumer chain with the generated // proof ConsumerParachain::execute_with(|| { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - Proof { - blinded: proof.blinded, - revealed: proof.revealed, - } - .into(), - Box::new(ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::< - ConsumerRuntime, - >::associate_sender {})), + MerkleEntriesAndDidSignature { + merkle_entries: MerkleProof { + blinded: proof.blinded, + revealed: proof.revealed, + }, + did_signature: TimeBoundDidSignature { + signature, + block_number: system_block + } + }, + Box::new(call), )); // Verify the account -> DID link exists and contains the right information let linked_did = DidLookup::connected_dids::(para::consumer::DISPATCHER_ACCOUNT.into()) .map(|link| link.did); - assert_eq!(linked_did, Some(did)); + assert_eq!(linked_did, Some(did.clone())); + // Verify that the details of the DID subject have been bumped + let details = DipConsumer::identity_proofs(&did).map(|entry| entry.details); + assert_eq!(details, Some(1u128)); }); } diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs new file mode 100644 index 0000000000..eed39f68d4 --- /dev/null +++ b/pallets/pallet-dip-consumer/src/identity.rs @@ -0,0 +1,43 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::RuntimeDebug; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +/// The identity entry for any given user that uses the DIP protocol. +#[derive(Encode, Decode, MaxEncodedLen, Default, TypeInfo, RuntimeDebug)] +pub struct IdentityDetails { + /// The identity digest information, typically used to verify identity + /// proofs. + pub digest: Digest, + /// The details related to the user, stored in the pallet storage. + pub details: Details, +} + +impl From for IdentityDetails +where + Details: Default, +{ + fn from(value: Digest) -> Self { + Self { + digest: value, + details: Details::default(), + } + } +} diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index a351ae1b9a..f9712bf5a0 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -20,9 +20,11 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod origin; +pub mod identity; pub mod traits; +mod origin; + pub use crate::{origin::*, pallet::*}; #[frame_support::pallet] @@ -30,40 +32,64 @@ pub mod pallet { use super::*; use cumulus_pallet_xcm::ensure_sibling_para; - use frame_support::{dispatch::Dispatchable, pallet_prelude::*, Twox64Concat}; + use frame_support::{dispatch::Dispatchable, pallet_prelude::*, traits::Contains, Twox64Concat}; use frame_system::pallet_prelude::*; + use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; - use dip_support::{latest::IdentityProofAction, VersionedIdentityProof, VersionedIdentityProofAction}; + use dip_support::IdentityProofAction; - use crate::traits::{DipCallOriginFilter, IdentityProofVerifier}; + use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; - pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier>::VerificationResult; - pub type VersionedIdentityProofOf = - VersionedIdentityProof<::BlindedValue, ::ProofLeaf>; + pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< + ::RuntimeCall, + ::Identifier, + >>::VerificationResult; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); // TODO: Store also additional details received by the provider. #[pallet::storage] #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityProofs = - StorageMap<_, Twox64Concat, ::Identifier, ::ProofDigest>; + pub(crate) type IdentityProofs = StorageMap< + _, + Twox64Concat, + ::Identifier, + IdentityDetails<::ProofDigest, ::IdentityDetails>, + >; #[pallet::config] pub trait Config: frame_system::Config { - type BlindedValue: Parameter; - type DipCallOriginFilter: DipCallOriginFilter<::RuntimeCall, Proof = VerificationResultOf>; + /// Preliminary filter to filter out calls before doing any heavier + /// computations. + type DipCallOriginFilter: Contains<::RuntimeCall>; + /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; - type ProofLeaf: Parameter; + /// The details stored in this pallet associated with any given subject. + type IdentityDetails: Parameter + MaxEncodedLen + Default; + /// The proof users must provide to operate with their higher-level + /// identity. Depending on the use cases, this proof can contain + /// heterogeneous bits of information that the proof verifier will + /// utilize. For instance, a proof could contain both a Merkle proof and + /// a DID signature. + type Proof: Parameter; + /// The type of the committed proof digest used as the basis for + /// verifying identity proofs. type ProofDigest: Parameter + MaxEncodedLen; + /// The logic of the proof verifier, called upon each execution of the + /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier< - BlindedValue = Self::BlindedValue, - ProofDigest = Self::ProofDigest, - ProofLeaf = Self::ProofLeaf, + ::RuntimeCall, + Self::Identifier, + Proof = Self::Proof, + IdentityDetails = IdentityDetails, + Submitter = ::AccountId, >; + /// The overarching runtime call type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The overarching runtime origin type. type RuntimeOrigin: From> + From<::RuntimeOrigin> + Into::RuntimeOrigin>>; @@ -77,26 +103,29 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// The identity information related to a given subject has been + /// deleted. IdentityInfoDeleted(T::Identifier), + /// The identity information related to a given subject has been updated + /// to a new digest. IdentityInfoUpdated(T::Identifier, T::ProofDigest), } #[pallet::error] pub enum Error { - BadOrigin, - Dispatch, + /// An identity with the provided identifier could not be found. IdentityNotFound, + /// The identity proof provided could not be successfully verified. InvalidProof, - UnsupportedVersion, + /// The specified call could not be dispatched. + Dispatch, } - // The new origin other pallets can use. + /// The origin this pallet creates after a user has provided a valid + /// identity proof to dispatch other calls. #[pallet::origin] - pub type Origin = DipOrigin< - ::Identifier, - ::AccountId, - <::DipCallOriginFilter as DipCallOriginFilter<::RuntimeCall>>::Success, - >; + pub type Origin = + DipOrigin<::Identifier, ::AccountId, VerificationResultOf>; // TODO: Benchmarking #[pallet::call] @@ -105,20 +134,24 @@ pub mod pallet { #[pallet::weight(0)] pub fn process_identity_action( origin: OriginFor, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> DispatchResult { ensure_sibling_para(::RuntimeOrigin::from(origin))?; let event = match action { - VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { - IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); + IdentityProofAction::Updated(identifier, proof, _) => { + IdentityProofs::::mutate( + &identifier, + |entry: &mut Option< + IdentityDetails<::ProofDigest, ::IdentityDetails>, + >| { *entry = Some(proof.clone().into()) }, + ); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } - VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { + IdentityProofAction::Deleted(identifier) => { IdentityProofs::::remove(&identifier); Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) } - _ => Err(Error::::UnsupportedVersion), }?; Self::deposit_event(event); @@ -132,22 +165,28 @@ pub mod pallet { pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, - proof: VersionedIdentityProofOf, + proof: T::Proof, call: Box<::RuntimeCall>, ) -> DispatchResult { let submitter = ensure_signed(origin)?; - let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; - let proof_verification_result = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) - .map_err(|_| Error::::InvalidProof)?; - // TODO: Better error handling - // TODO: Avoid cloning `call` - let proof_result = T::DipCallOriginFilter::check_proof(*call.clone(), proof_verification_result) - .map_err(|_| Error::::BadOrigin)?; - // TODO: Proper DID signature verification (and cross-chain replay protection) + // TODO: Proper error handling + ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); + let mut proof_entry = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_entry( + &*call, + &identifier, + &submitter, + &mut proof_entry, + &proof, + ) + .map_err(|_| Error::::InvalidProof)?; + // Write the identity info to storage after it has optionally been updated by + // the `ProofVerifier`. + IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof_entry)); let did_origin = DipOrigin { identifier, account_address: submitter, - details: proof_result, + details: proof_verification_result, }; // TODO: Use dispatch info for weight calculation let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index c134a038f5..7f4645f166 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -16,45 +16,42 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::VersionedIdentityProof; use sp_std::marker::PhantomData; -pub trait IdentityProofVerifier { - type BlindedValue; +pub trait IdentityProofVerifier { type Error; - type ProofDigest; - type ProofLeaf; + type Proof; + type IdentityDetails; + type Submitter; type VerificationResult; - fn verify_proof_against_digest( - proof: VersionedIdentityProof, - digest: Self::ProofDigest, + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, Leaf, BlindedValue)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(Proof, ProofEntry, Submitter)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { - type BlindedValue = BlindedValue; type Error = (); - type ProofDigest = ProofDigest; - type ProofLeaf = Leaf; + type Proof = Proof; + type IdentityDetails = ProofEntry; + type Submitter = Submitter; type VerificationResult = (); - fn verify_proof_against_digest( - _proof: VersionedIdentityProof, - _digest: Self::ProofDigest, + fn verify_proof_for_call_against_entry( + _call: &Call, + _subject: &Subject, + _submitter: &Self::Submitter, + _proof_entry: &mut Self::IdentityDetails, + _proof: &Self::Proof, ) -> Result { Ok(()) } } - -pub trait DipCallOriginFilter { - type Error; - type Proof; - type Success; - - fn check_proof(call: Call, proof: Self::Proof) -> Result; -} diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 4af958ccd8..c826d191a7 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -33,13 +33,11 @@ pub mod pallet { use sp_std::{boxed::Box, fmt::Debug}; use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; - use dip_support::{v1::IdentityProofAction, VersionedIdentityProofAction}; + use dip_support::IdentityProofAction; use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; pub type IdentityProofActionOf = IdentityProofAction<::Identifier, ::ProofOutput>; - pub type VersionedIdentityProofActionOf = - VersionedIdentityProofAction<::Identifier, ::ProofOutput>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -72,7 +70,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - IdentityInfoDispatched(VersionedIdentityProofActionOf, Box), + IdentityInfoDispatched(IdentityProofActionOf, Box), } #[pallet::error] @@ -110,23 +108,18 @@ pub mod pallet { Err(_) => Err(Error::::IdentityNotFound), }?; // TODO: Add correct version creation based on lookup (?) - let versioned_action = VersionedIdentityProofAction::V1(action); let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( - versioned_action.clone(), - asset, - weight, - destination, - ) - .map_err(|_| Error::::Predispatch)?; + let (ticket, _) = + T::IdentityProofDispatcher::pre_dispatch::(action.clone(), asset, weight, destination) + .map_err(|_| Error::::Predispatch)?; // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the // fee, in addition to the cost on the target chain. T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; - Self::deposit_event(Event::IdentityInfoDispatched(versioned_action, Box::new(destination))); + Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); Ok(()) } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index a589fa3b1d..fd9b90d046 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::VersionedIdentityProofAction; +use dip_support::IdentityProofAction; use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; @@ -61,7 +61,7 @@ pub mod identity_dispatch { type Error; fn pre_dispatch>( - action: VersionedIdentityProofAction, + action: IdentityProofAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -80,7 +80,7 @@ pub mod identity_dispatch { type Error = (); fn pre_dispatch<_B>( - _action: VersionedIdentityProofAction, + _action: IdentityProofAction, _asset: MultiAsset, _weight: Weight, _destination: MultiLocation, @@ -111,7 +111,7 @@ pub mod identity_dispatch { type Error = SendError; fn pre_dispatch>( - action: VersionedIdentityProofAction, + action: IdentityProofAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -190,6 +190,6 @@ pub trait TxBuilder { fn build( dest: MultiLocation, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> Result, Self::Error>; } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 6d6284722e..4b1e03c0a6 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -26,7 +26,7 @@ attestation.workspace = true ctype.workspace = true delegation = {workspace = true, optional = true} did.workspace = true -dip-support.workspace = true +kilt-dip-support.workspace = true pallet-did-lookup = {workspace = true, optional = true} pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true @@ -84,10 +84,10 @@ std = [ "ctype/std", "cumulus-primitives-core/std", "did/std", - "dip-support/std", "frame-support/std", "frame-system/std", "kilt-asset-dids/std", + "kilt-dip-support/std", "kilt-support/std", "log/std", "pallet-authorship/std", diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs deleted file mode 100644 index 8c6c492850..0000000000 --- a/runtimes/common/src/dip/consumer.rs +++ /dev/null @@ -1,118 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use did::did_details::DidPublicKeyDetails; -use dip_support::{v1, VersionedIdentityProof}; -use frame_support::RuntimeDebug; -use pallet_dip_consumer::traits::IdentityProofVerifier; -use parity_scale_codec::Encode; -use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; -use sp_trie::{verify_trie_proof, LayoutV1}; - -use crate::dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; - -// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by -// having a vector of `KeyRelationship` instead. -#[derive(RuntimeDebug, PartialEq, Eq)] -pub struct ProofEntry { - pub key: DidPublicKeyDetails, - pub relationship: KeyRelationship, -} - -// Contains the list of revealed public keys after a given merkle proof has been -// correctly verified. -#[derive(RuntimeDebug, PartialEq, Eq)] -pub struct VerificationResult(pub Vec>); - -impl From>> for VerificationResult { - fn from(value: Vec>) -> Self { - Self(value) - } -} - -pub struct DidMerkleProofVerifier(PhantomData<(KeyId, BlockNumber, Hasher)>); - -impl IdentityProofVerifier for DidMerkleProofVerifier -where - KeyId: Encode + Clone + Ord, - BlockNumber: Encode + Clone + Ord, - Hasher: sp_core::Hasher, -{ - type BlindedValue = Vec; - // TODO: Proper error handling - type Error = (); - type ProofDigest = ::Out; - type ProofLeaf = ProofLeaf; - type VerificationResult = VerificationResult; - - fn verify_proof_against_digest( - proof: VersionedIdentityProof, - digest: Self::ProofDigest, - ) -> Result { - let proof: v1::Proof<_, _> = proof.try_into()?; - // TODO: more efficient by removing cloning and/or collecting. - // Did not find another way of mapping a Vec<(Vec, Vec)> to a - // Vec<(Vec, Option>)>. - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof_leaves).map_err(|_| ())?; - - // At this point, we know the proof is valid. We just need to map the revealed - // leaves to something the consumer can easily operate on. - - // Create a map of the revealed public keys - //TODO: Avoid cloning, and use a map of references for the lookup - let public_keys: BTreeMap> = proof - .revealed - .clone() - .into_iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { - Some((key_id, key_details)) - } else { - None - } - }) - .collect(); - // Create a list of the revealed keys by consuming the provided key reference - // leaves, and looking up the full details from the just-built `public_keys` - // map. - let keys: Vec> = proof - .revealed - .into_iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { - // TODO: Better error handling. - let key_details = public_keys - .get(&key_id) - .expect("Key ID should be present in the map of revealed public keys."); - Some(ProofEntry { - key: key_details.clone(), - relationship: key_relationship, - }) - } else { - None - } - }) - .collect(); - Ok(keys.into()) - } -} diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs new file mode 100644 index 0000000000..e954f008e7 --- /dev/null +++ b/runtimes/common/src/dip/did.rs @@ -0,0 +1,42 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::did_details::DidDetails; +use pallet_dip_provider::traits::IdentityProvider; +use sp_std::marker::PhantomData; + +pub struct DidIdentityProvider(PhantomData); + +impl IdentityProvider, ()> for DidIdentityProvider +where + T: did::Config, +{ + // TODO: Proper error handling + type Error = (); + + fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { + match ( + did::Pallet::::get_did(identifier), + did::Pallet::::get_deleted_did(identifier), + ) { + (Some(details), _) => Ok(Some((details, ()))), + (_, Some(_)) => Ok(None), + _ => Err(()), + } + } +} diff --git a/runtimes/common/src/dip/provider.rs b/runtimes/common/src/dip/merkle.rs similarity index 86% rename from runtimes/common/src/dip/provider.rs rename to runtimes/common/src/dip/merkle.rs index 0f555a199a..f92cc96805 100644 --- a/runtimes/common/src/dip/provider.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -17,19 +17,21 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; -use dip_support::latest::Proof; use frame_support::RuntimeDebug; -use pallet_dip_provider::traits::{IdentityProofGenerator, IdentityProvider}; +use kilt_dip_support::merkle::MerkleProof; +use pallet_dip_provider::traits::IdentityProofGenerator; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use crate::dip::{KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, KeyRelationship, ProofLeaf}; +use kilt_dip_support::merkle::{ + DidKeyRelationship, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, ProofLeaf, +}; pub type BlindedValue = Vec; - -pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; +pub type DidMerkleProofOf = + MerkleProof, ProofLeaf, ::BlockNumber>>; #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { @@ -95,7 +97,7 @@ where .iter() .try_for_each(|id| -> Result<(), ()> { let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(*id, KeyRelationship::Encryption), + KeyReferenceKey(*id, DidKeyRelationship::Encryption), KeyReferenceValue, ); trie_builder @@ -127,7 +129,7 @@ where pub fn generate_proof<'a, K>( identity: &DidDetails, mut key_ids: K, - ) -> Result>, ()> + ) -> Result>, ()> where K: Iterator>, { @@ -159,7 +161,7 @@ where } if identity.key_agreement_keys.contains(key_id) { set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, KeyRelationship::Encryption), + KeyReferenceKey(*key_id, DidKeyRelationship::Encryption), KeyReferenceValue, )); }; @@ -177,7 +179,7 @@ where let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; Ok(CompleteMerkleProof { root, - proof: DidMerkleProof:: { + proof: DidMerkleProofOf:: { blinded: proof, revealed: leaves.into_iter().collect::>(), }, @@ -198,24 +200,3 @@ where Self::calculate_root_with_db(identity, &mut db) } } - -pub struct DidIdentityProvider(PhantomData); - -impl IdentityProvider, ()> for DidIdentityProvider -where - T: did::Config, -{ - // TODO: Proper error handling - type Error = (); - - fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { - match ( - did::Pallet::::get_did(identifier), - did::Pallet::::get_deleted_did(identifier), - ) { - (Some(details), _) => Ok(Some((details, ()))), - (_, Some(_)) => Ok(None), - _ => Err(()), - } - } -} diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index 645f1ea4d6..a3955297d6 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -16,67 +16,5 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_std::vec::Vec; - -pub mod consumer; -pub mod provider; - -#[cfg(test)] -mod tests; - -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] -pub enum KeyRelationship { - Encryption, - Verification(DidVerificationKeyRelationship), -} - -impl From for KeyRelationship { - fn from(value: DidVerificationKeyRelationship) -> Self { - Self::Verification(value) - } -} - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceValue; - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsKey(pub KeyId); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsValue(pub DidPublicKeyDetails); - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { - // The key and value for the leaves of a merkle proof that contain a reference - // (by ID) to the key details, provided in a separate leaf. - KeyReference(KeyReferenceKey, KeyReferenceValue), - // The key and value for the leaves of a merkle proof that contain the actual - // details of a DID public key. The key is the ID of the key, and the value is its details, including creation - // block number. - KeyDetails(KeyDetailsKey, KeyDetailsValue), -} - -impl ProofLeaf -where - KeyId: Encode, - BlockNumber: Encode, -{ - pub(crate) fn encoded_key(&self) -> Vec { - match self { - ProofLeaf::KeyReference(key, _) => key.encode(), - ProofLeaf::KeyDetails(key, _) => key.encode(), - } - } - - pub(crate) fn encoded_value(&self) -> Vec { - match self { - ProofLeaf::KeyReference(_, value) => value.encode(), - ProofLeaf::KeyDetails(_, value) => value.encode(), - } - } -} +pub mod did; +pub mod merkle; diff --git a/runtimes/common/src/dip/tests.rs b/runtimes/common/src/dip/tests.rs deleted file mode 100644 index b1ce9f9430..0000000000 --- a/runtimes/common/src/dip/tests.rs +++ /dev/null @@ -1,315 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use did::{ - did_details::{DidCreationDetails, DidEncryptionKey}, - DidVerificationKeyRelationship, KeyIdOf, -}; -use dip_support::latest::Proof; -use frame_support::{ - assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, -}; -use frame_system::{ - mocking::{MockBlock, MockUncheckedExtrinsic}, - EnsureSigned, RawOrigin, -}; -use pallet_dip_consumer::traits::IdentityProofVerifier; -use parity_scale_codec::Encode; -use sp_core::{ecdsa, ed25519, sr25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; -use sp_io::TestExternalities; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup}, - AccountId32, -}; -use sp_std::collections::btree_set::BTreeSet; - -use crate::dip::{ - consumer::DidMerkleProofVerifier, - provider::{CompleteMerkleProof, DidMerkleRootGenerator}, - ProofLeaf, -}; - -pub(crate) type AccountId = AccountId32; -pub(crate) type Balance = u128; -pub(crate) type Block = MockBlock; -pub(crate) type BlockNumber = u64; -pub(crate) type Hashing = BlakeTwo256; -pub(crate) type Index = u64; -pub(crate) type UncheckedExtrinsic = MockUncheckedExtrinsic; - -construct_runtime!( - pub enum TestRuntime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - Balances: pallet_balances, - Did: did, - } -); - -impl frame_system::Config for TestRuntime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountId; - type BaseCallFilter = Everything; - type BlockHashCount = ConstU64<250>; - type BlockLength = (); - type BlockNumber = BlockNumber; - type BlockWeights = (); - type DbWeight = RocksDbWeight; - type Hash = ::Out; - type Hashing = Hashing; - type Header = Header; - type Index = Index; - type Lookup = IdentityLookup; - type MaxConsumers = ConstU32<16>; - type OnKilledAccount = (); - type OnNewAccount = (); - type OnSetCode = (); - type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SS58Prefix = ConstU16<38>; - type SystemWeightInfo = (); - type Version = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 500u64.into(); -} - -impl pallet_balances::Config for TestRuntime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type MaxLocks = ConstU32<50>; - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -parameter_types! { - pub Deposit: Balance = 500u64.into(); - pub Fee: Balance = 500u64.into(); - pub MaxBlocksTxValidity: BlockNumber = 10u64; - #[derive(Debug, Clone, Eq, PartialEq)] - pub const MaxTotalKeyAgreementKeys: u32 = 2; -} - -impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { - fn derive_verification_key_relationship(&self) -> did::DeriveDidCallKeyRelationshipResult { - Ok(DidVerificationKeyRelationship::Authentication) - } - - #[cfg(feature = "runtime-benchmarks")] - fn get_call_for_did_call_benchmark() -> Self { - RuntimeCall::System(frame_system::Call::remark { remark: vec![] }) - } -} - -impl did::Config for TestRuntime { - type Currency = Balances; - type Deposit = Deposit; - type DidIdentifier = AccountId; - type EnsureOrigin = EnsureSigned; - type Fee = Fee; - type FeeCollector = (); - type MaxBlocksTxValidity = MaxBlocksTxValidity; - type MaxNewKeyAgreementKeys = ConstU32<2>; - type MaxNumberOfServicesPerDid = ConstU32<1>; - type MaxNumberOfTypesPerService = ConstU32<1>; - type MaxNumberOfUrlsPerService = ConstU32<1>; - type MaxPublicKeysPerDid = ConstU32<5>; - type MaxServiceIdLength = ConstU32<100>; - type MaxServiceTypeLength = ConstU32<100>; - type MaxServiceUrlLength = ConstU32<100>; - type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; - type OriginSuccess = AccountId; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type WeightInfo = (); -} - -fn base_ext() -> TestExternalities { - TestExternalities::new( - frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(), - ) -} - -const ALICE: AccountId = AccountId::new([1u8; 32]); - -#[test] -fn minimal_did_merkle_proof() { - base_ext().execute_with(|| { - // Give Alice some balance - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); - // Generate a DID for alice - let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); - let did: AccountId = did_auth_key.public().into_account().into(); - let create_details = DidCreationDetails { - did: did.clone(), - submitter: ALICE, - new_attestation_key: None, - new_delegation_key: None, - new_key_agreement_keys: BTreeSet::new().try_into().unwrap(), - new_service_details: vec![], - }; - // Create Alice's DID with only authentication key - assert_ok!(Did::create( - RawOrigin::Signed(ALICE).into(), - Box::new(create_details.clone()), - did_auth_key.sign(&create_details.encode()).into() - )); - let did_details = Did::get_did(&did).expect("DID should be present"); - - // 1. Create the DID merkle proof revealing only the authentication key - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - println!("{:?} - {:?} - {:?} bytes", root, proof, proof.encoded_size()); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.clone().into(), - root - ) - ); - - // 2. Fail to generate a Merkle proof for a key that does not exist - assert_err!( - DidMerkleRootGenerator::::generate_proof( - &did_details, - [<::Out>::default()].iter() - ), - () - ); - - // 3. Fail to verify a merkle proof with a compromised merkle root - let new_root = <::Out>::default(); - assert_err!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - new_root - ), - () - ); - }) -} - -#[test] -fn complete_did_merkle_proof() { - base_ext().execute_with(|| { - // Give Alice some balance - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); - // Generate a DID for alice - let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); - let did_att_key = sr25519::Pair::from_seed(&[150u8; 32]); - let did_del_key = ecdsa::Pair::from_seed(&[200u8; 32]); - let enc_keys = BTreeSet::from_iter(vec![ - DidEncryptionKey::X25519([250u8; 32]), - DidEncryptionKey::X25519([251u8; 32]), - ]); - let did: AccountId = did_auth_key.public().into_account().into(); - let create_details = DidCreationDetails { - did: did.clone(), - submitter: ALICE, - new_attestation_key: Some(did_att_key.public().into()), - new_delegation_key: Some(did_del_key.public().into()), - new_key_agreement_keys: enc_keys - .try_into() - .expect("BTreeSet to BoundedBTreeSet should not fail"), - new_service_details: vec![], - }; - // Create Alice's DID with only authentication key - assert_ok!(Did::create( - RawOrigin::Signed(ALICE).into(), - Box::new(create_details.clone()), - did_auth_key.sign(&create_details.encode()).into() - )); - let did_details = Did::get_did(&did).expect("DID should be present"); - - // 1. Create the DID merkle proof revealing only the authentication key - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - root - ) - ); - - // 2. Create the DID merkle proof revealing all the keys - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [ - did_details.authentication_key, - did_details.attestation_key.unwrap(), - did_details.delegation_key.unwrap(), - ] - .iter() - .chain(did_details.key_agreement_keys.iter()), - ) - .expect("Merkle proof generation should not fail."); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - root - ) - ); - - // 2. Create the DID merkle proof revealing only the key reference and not the - // key ID - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - let reference_only_authentication_leaf: Vec<_> = proof - .revealed - .into_iter() - .filter(|l| !matches!(l, ProofLeaf::KeyDetails(_, _))) - .collect(); - // Fail to verify the generated merkle proof - assert_err!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - Proof { - blinded: proof.blinded, - revealed: reference_only_authentication_leaf - } - .into(), - root - ), - () - ); - }) -} diff --git a/runtimes/common/src/lib.rs b/runtimes/common/src/lib.rs index e94d361b43..bdc0477de2 100644 --- a/runtimes/common/src/lib.rs +++ b/runtimes/common/src/lib.rs @@ -35,7 +35,7 @@ use frame_system::limits; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use sp_runtime::{ generic, - traits::{Bounded, IdentifyAccount, Verify}, + traits::{BlakeTwo256, Bounded, IdentifyAccount, Verify}, FixedPointNumber, MultiSignature, Perquintill, SaturatedConversion, }; use sp_std::marker::PhantomData; @@ -101,8 +101,10 @@ pub type Amount = i128; /// Index of a transaction in the chain. pub type Index = u64; +/// Hasher for chain data. +pub type Hasher = BlakeTwo256; /// A hash of some data used by the chain. -pub type Hash = sp_core::H256; +pub type Hash = ::Out; /// Digest item type. pub type DigestItem = generic::DigestItem; diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index 722594edb0..dbfd756d74 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -62,7 +62,7 @@ use runtime_common::{ errors::PublicCredentialsApiError, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, - FeeSplit, Hash, Header, Index, Signature, SlowAdjustingFeeUpdate, + FeeSplit, Hash, Hasher, Header, Index, Signature, SlowAdjustingFeeUpdate, }; use crate::xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; @@ -130,7 +130,7 @@ impl frame_system::Config for Runtime { /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. - type Hashing = BlakeTwo256; + type Hashing = Hasher; /// The header type. type Header = runtime_common::Header; /// The ubiquitous event type. diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index 1ef0c9817f..0bc1c83c9c 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -62,7 +62,7 @@ use runtime_common::{ errors::PublicCredentialsApiError, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, - FeeSplit, Hash, Header, Index, Signature, SlowAdjustingFeeUpdate, + FeeSplit, Hash, Hasher, Header, Index, Signature, SlowAdjustingFeeUpdate, }; use crate::xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; @@ -130,7 +130,7 @@ impl frame_system::Config for Runtime { /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. - type Hashing = BlakeTwo256; + type Hashing = Hasher; /// The header type. type Header = runtime_common::Header; /// The ubiquitous event type. From 17c587e7cfd39e79ee06c7a568271ee8beecc505 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 23 May 2023 16:55:23 +0200 Subject: [PATCH 06/28] fix: new required elements for the DID pallet --- dip-template/runtimes/dip-provider/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index b3766aa160..b986a95e57 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -369,12 +369,13 @@ parameter_types! { } impl did::Config for Runtime { + type BaseDeposit = ConstU128; type Currency = Balances; - type Deposit = ConstU128; type DidIdentifier = DidIdentifier; type EnsureOrigin = EnsureDidOrigin; type Fee = ConstU128; type FeeCollector = (); + type KeyDeposit = ConstU128; type MaxBlocksTxValidity = ConstU32; type MaxNewKeyAgreementKeys = ConstU32<1>; type MaxNumberOfServicesPerDid = ConstU32<1>; @@ -389,6 +390,7 @@ impl did::Config for Runtime { type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; + type ServiceEndpointDeposit = ConstU128; type WeightInfo = (); } From 4064da694bc578ebf114359af12cc6a367ada1b2 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 25 May 2023 17:46:34 +0200 Subject: [PATCH 07/28] feat: support web3name and linked accounts in the identity proof (#525) Fixes https://github.com/KILTprotocol/ticket/issues/2668. --- Cargo.lock | 5 + crates/dip-support/src/lib.rs | 2 +- crates/kilt-dip-support/Cargo.toml | 2 + crates/kilt-dip-support/src/did.rs | 159 +++++++-- crates/kilt-dip-support/src/lib.rs | 24 +- crates/kilt-dip-support/src/merkle.rs | 301 ++++++++++++------ crates/kilt-dip-support/src/traits.rs | 1 - .../nodes/dip-provider/src/chain_spec.rs | 1 + dip-template/runtimes/dip-consumer/Cargo.toml | 3 + dip-template/runtimes/dip-consumer/src/dip.rs | 13 +- dip-template/runtimes/dip-consumer/src/lib.rs | 15 +- dip-template/runtimes/dip-provider/Cargo.toml | 6 + dip-template/runtimes/dip-provider/src/dip.rs | 12 +- dip-template/runtimes/dip-provider/src/lib.rs | 58 +++- dip-template/runtimes/xcm-tests/Cargo.toml | 1 + dip-template/runtimes/xcm-tests/src/para.rs | 47 ++- dip-template/runtimes/xcm-tests/src/tests.rs | 63 +++- .../pallet-did-lookup/src/linkable_account.rs | 9 + pallets/pallet-dip-consumer/src/lib.rs | 22 +- pallets/pallet-dip-consumer/src/origin.rs | 4 +- pallets/pallet-dip-consumer/src/traits.rs | 8 +- pallets/pallet-dip-provider/src/lib.rs | 23 +- pallets/pallet-dip-provider/src/traits.rs | 31 +- pallets/pallet-web3-names/src/lib.rs | 5 +- pallets/pallet-web3-names/src/web3_name.rs | 28 ++ runtime-api/dip-provider/src/lib.rs | 8 +- runtimes/common/Cargo.toml | 6 +- runtimes/common/src/dip/did.rs | 59 +++- runtimes/common/src/dip/merkle.rs | 228 ++++++++----- 29 files changed, 816 insertions(+), 328 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e51576ab80..d033efc860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2341,6 +2341,7 @@ dependencies = [ "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", + "dip-provider-runtime-template", "dip-support", "frame-executive", "frame-support", @@ -2454,12 +2455,14 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-collator-selection", + "pallet-did-lookup", "pallet-dip-provider", "pallet-session", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-web3-names", "pallet-xcm", "parachain-info", "parity-scale-codec", @@ -2505,6 +2508,7 @@ dependencies = [ "kilt-support", "pallet-balances", "pallet-did-lookup", + "pallet-web3-names", "parachain-info", "parity-scale-codec", "polkadot-parachain", @@ -4220,6 +4224,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-dip-consumer", + "pallet-dip-provider", "parity-scale-codec", "scale-info", "sp-core", diff --git a/crates/dip-support/src/lib.rs b/crates/dip-support/src/lib.rs index bde5dbfee3..31256a1dda 100644 --- a/crates/dip-support/src/lib.rs +++ b/crates/dip-support/src/lib.rs @@ -25,7 +25,7 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub enum IdentityProofAction { +pub enum IdentityDetailsAction { Updated(Identifier, Proof, Details), Deleted(Identifier), } diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index ebc86ee9b0..e06e17664e 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -14,6 +14,7 @@ version.workspace = true # Internal dependencies did.workspace = true pallet-dip-consumer.workspace = true +pallet-dip-provider.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} @@ -32,6 +33,7 @@ default = ["std"] std = [ "did/std", "pallet-dip-consumer/std", + "pallet-dip-provider/std", "parity-scale-codec/std", "scale-info/std", "frame-system/std", diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 07bd50d2e6..c1d37be9a4 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -22,6 +22,7 @@ use did::{ }; use frame_support::ensure; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; +use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::{ConstU64, Get, RuntimeDebug}; @@ -29,7 +30,7 @@ use sp_runtime::traits::CheckedSub; use sp_std::marker::PhantomData; use crate::{ - merkle::ProofEntry, + merkle::RevealedDidKey, traits::{Bump, DidDipOriginFilter}, }; @@ -40,8 +41,8 @@ pub struct TimeBoundDidSignature { } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct MerkleEntriesAndDidSignature { - pub merkle_entries: MerkleEntries, +pub struct MerkleLeavesAndDidSignature { + pub merkle_leaves: MerkleLeaves, pub did_signature: TimeBoundDidSignature, } @@ -53,6 +54,7 @@ pub struct MerkleEntriesAndDidSignature { /// genesis_hash). Additional details can be added to the end of the tuple by /// providing a `SignedExtraProvider`. pub struct MerkleRevealedDidSignatureVerifier< + KeyId, BlockNumber, Digest, Details, @@ -67,6 +69,7 @@ pub struct MerkleRevealedDidSignatureVerifier< >( #[allow(clippy::type_complexity)] PhantomData<( + KeyId, BlockNumber, Digest, Details, @@ -84,6 +87,7 @@ pub struct MerkleRevealedDidSignatureVerifier< impl< Call, Subject, + KeyId, BlockNumber, Digest, Details, @@ -97,6 +101,7 @@ impl< SignedExtra, > IdentityProofVerifier for MerkleRevealedDidSignatureVerifier< + KeyId, BlockNumber, Digest, Details, @@ -114,7 +119,7 @@ impl< Call: Encode, Digest: Encode, Details: Bump + Encode, - MerkleProofEntries: AsRef<[ProofEntry]>, + MerkleProofEntries: AsRef<[RevealedDidKey]>, BlockNumberProvider: Get, GenesisHashProvider: Get, Hash: Encode, @@ -125,7 +130,7 @@ impl< type Error = (); /// The proof must be a list of Merkle leaves that have been previously /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = MerkleEntriesAndDidSignature; + type Proof = MerkleLeavesAndDidSignature; /// The `Details` that are part of the identity details must implement the /// `Bump` trait. type IdentityDetails = IdentityDetails; @@ -135,11 +140,11 @@ impl< /// the provided signature and its relationship to the DID subject. type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); - fn verify_proof_for_call_against_entry( + fn verify_proof_for_call_against_details( call: &Call, _subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, + identity_details: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { let block_number = BlockNumberProvider::get(); @@ -151,43 +156,30 @@ impl< // Signature generated at a future time, not possible to verify. false }; + ensure!(is_signature_fresh, ()); let encoded_payload = ( call, - &proof_entry.details, + &identity_details.details, submitter, &proof.did_signature.block_number, GenesisHashProvider::get(), SignedExtraProvider::get(), ) .encode(); - // Only consider verification keys from the set of revealed Merkle leaves. - let mut proof_verification_keys = proof.merkle_entries.as_ref().iter().filter_map( - |ProofEntry { - key: DidPublicKeyDetails { key, .. }, - relationship, - }| { - if let DidPublicKey::PublicVerificationKey(k) = key { - Some(( - k, - DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."), - )) - } else { - None - } - }, - ); + // Only consider verification keys from the set of revealed keys. + let mut proof_verification_keys = proof.merkle_leaves.as_ref().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | { + let DidPublicKey::PublicVerificationKey(key) = key else { return None }; + Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) + }); let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { verification_key .verify_signature(&encoded_payload, &proof.did_signature.signature) .is_ok() }); - if let Some((key, relationship)) = valid_signing_key { - proof_entry.details.bump(); - Ok((key.clone(), relationship)) - } else { - Err(()) - } + let Some((key, relationship)) = valid_signing_key else { return Err(()) }; + identity_details.details.bump(); + Ok((key.clone(), relationship)) } } @@ -223,17 +215,114 @@ where /// `DidSignatureVerifier`. type VerificationResult = DidSignatureVerifier::VerificationResult; - fn verify_proof_for_call_against_entry( + fn verify_proof_for_call_against_details( call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, + identity_details: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { - let did_signing_key = - DidSignatureVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof) - .map_err(|_| ())?; + let did_signing_key = DidSignatureVerifier::verify_proof_for_call_against_details( + call, + subject, + submitter, + identity_details, + proof, + ) + .map_err(|_| ())?; CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?; Ok(did_signing_key) } } + +pub struct CombinedIdentityResult { + pub a: OutputA, + pub b: OutputB, + pub c: OutputC, +} + +impl From<(OutputA, OutputB, OutputC)> + for CombinedIdentityResult +{ + fn from(value: (OutputA, OutputB, OutputC)) -> Self { + Self { + a: value.0, + b: value.1, + c: value.2, + } + } +} + +impl CombinedIdentityResult +where + OutputB: Default, + OutputC: Default, +{ + pub fn from_a(a: OutputA) -> Self { + Self { + a, + b: OutputB::default(), + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputC: Default, +{ + pub fn from_b(b: OutputB) -> Self { + Self { + a: OutputA::default(), + b, + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputB: Default, +{ + pub fn from_c(c: OutputC) -> Self { + Self { + a: OutputA::default(), + b: OutputB::default(), + c, + } + } +} + +pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); + +impl IdentityProvider for CombineIdentityFrom +where + A: IdentityProvider, + B: IdentityProvider, + C: IdentityProvider, +{ + // TODO: Proper error handling + type Error = (); + type Success = CombinedIdentityResult, Option, Option>; + + fn retrieve(identifier: &Identifier) -> Result, Self::Error> { + match ( + A::retrieve(identifier), + B::retrieve(identifier), + C::retrieve(identifier), + ) { + // If no details is returned, return None for the whole result + (Ok(None), Ok(None), Ok(None)) => Ok(None), + // Otherwise, return `Some` or `None` depending on each result + (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { + a: ok_a, + b: ok_b, + c: ok_c, + })), + // If any of them returns an `Err`, return an `Err` + _ => Err(()), + } + } +} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index c2f18ba0f8..103a5c504f 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -23,7 +23,7 @@ use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_std::marker::PhantomData; -use crate::did::MerkleEntriesAndDidSignature; +use crate::did::MerkleLeavesAndDidSignature; pub mod did; pub mod merkle; @@ -50,7 +50,7 @@ where DidSignatureVerifier: IdentityProofVerifier< Call, Subject, - Proof = MerkleEntriesAndDidSignature, + Proof = MerkleLeavesAndDidSignature, IdentityDetails = MerkleProofVerifier::IdentityDetails, Submitter = MerkleProofVerifier::Submitter, >, @@ -58,34 +58,34 @@ where // FIXME: Better error handling type Error = (); // FIXME: Better type declaration - type Proof = MerkleEntriesAndDidSignature; + type Proof = MerkleLeavesAndDidSignature; type IdentityDetails = DidSignatureVerifier::IdentityDetails; type Submitter = MerkleProofVerifier::Submitter; type VerificationResult = MerkleProofVerifier::VerificationResult; - fn verify_proof_for_call_against_entry( + fn verify_proof_for_call_against_details( call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, + identity_details: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { - let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_entry( + let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_details( call, subject, submitter, - proof_entry, - &proof.merkle_entries, + identity_details, + &proof.merkle_leaves, ) .map_err(|_| ())?; - DidSignatureVerifier::verify_proof_for_call_against_entry( + DidSignatureVerifier::verify_proof_for_call_against_details( call, subject, submitter, - proof_entry, + identity_details, // FIXME: Remove `clone()` requirement - &MerkleEntriesAndDidSignature { - merkle_entries: merkle_proof_verification.clone(), + &MerkleLeavesAndDidSignature { + merkle_leaves: merkle_proof_verification.clone(), did_signature: proof.did_signature.clone(), }, ) diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index d86e84475a..01722d5555 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -21,8 +21,8 @@ use frame_support::{traits::ConstU32, RuntimeDebug}; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::BoundedVec; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_runtime::{BoundedVec, SaturatedConversion}; +use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; pub type BlindedValue = Vec; @@ -60,137 +60,222 @@ impl TryFrom for DidVerificationKeyRelationship { } #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceKey(pub KeyId, pub DidKeyRelationship); +pub struct DidKeyMerkleKey(pub KeyId, pub DidKeyRelationship); + +impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey { + fn from(value: (KeyId, DidKeyRelationship)) -> Self { + Self(value.0, value.1) + } +} + #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceValue; +pub struct DidKeyMerkleValue(pub DidPublicKeyDetails); + +impl From> for DidKeyMerkleValue { + fn from(value: DidPublicKeyDetails) -> Self { + Self(value) + } +} + #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsKey(pub KeyId); +pub struct Web3NameMerkleKey(pub Web3Name); + +impl From for Web3NameMerkleKey { + fn from(value: Web3Name) -> Self { + Self(value) + } +} #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsValue(pub DidPublicKeyDetails); +pub struct Web3NameMerkleValue(BlockNumber); + +impl From for Web3NameMerkleValue { + fn from(value: BlockNumber) -> Self { + Self(value) + } +} #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { +pub struct LinkedAccountMerkleKey(pub AccountId); + +impl From for LinkedAccountMerkleKey { + fn from(value: AccountId) -> Self { + Self(value) + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct LinkedAccountMerkleValue; + +impl From<()> for LinkedAccountMerkleValue { + fn from(_value: ()) -> Self { + Self + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub enum ProofLeaf { // The key and value for the leaves of a merkle proof that contain a reference // (by ID) to the key details, provided in a separate leaf. - KeyReference(KeyReferenceKey, KeyReferenceValue), - // The key and value for the leaves of a merkle proof that contain the actual - // details of a DID public key. The key is the ID of the key, and the value is its details, including creation - // block number. - KeyDetails(KeyDetailsKey, KeyDetailsValue), + DidKey(DidKeyMerkleKey, DidKeyMerkleValue), + Web3Name(Web3NameMerkleKey, Web3NameMerkleValue), + LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), } -impl ProofLeaf +impl ProofLeaf where KeyId: Encode, + Web3Name: Encode, + LinkedAccountId: Encode, { pub fn encoded_key(&self) -> Vec { match self { - ProofLeaf::KeyReference(key, _) => key.encode(), - ProofLeaf::KeyDetails(key, _) => key.encode(), + ProofLeaf::DidKey(key, _) => key.encode(), + ProofLeaf::Web3Name(key, _) => key.encode(), + ProofLeaf::LinkedAccount(key, _) => key.encode(), } } } -impl ProofLeaf +impl ProofLeaf where BlockNumber: Encode, { pub fn encoded_value(&self) -> Vec { match self { - ProofLeaf::KeyReference(_, value) => value.encode(), - ProofLeaf::KeyDetails(_, value) => value.encode(), + ProofLeaf::DidKey(_, value) => value.encode(), + ProofLeaf::Web3Name(_, value) => value.encode(), + ProofLeaf::LinkedAccount(_, value) => value.encode(), } } } -// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by -// having a vector of `DidKeyRelationship` instead. -#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] -pub struct ProofEntry { - pub key: DidPublicKeyDetails, +#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct RevealedDidKey { + pub id: KeyId, pub relationship: DidKeyRelationship, + pub details: DidPublicKeyDetails, } -#[cfg(feature = "runtime-benchmarks")] -impl Default for ProofEntry -where - BlockNumber: Default, -{ - fn default() -> Self { - Self { - key: DidPublicKeyDetails { - key: did::did_details::DidPublicKey::PublicEncryptionKey(did::did_details::DidEncryptionKey::X25519( - [0u8; 32], - )), - block_number: BlockNumber::default(), - }, - relationship: DidVerificationKeyRelationship::Authentication.into(), - } - } +#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct RevealedWeb3Name { + pub web3_name: Web3Name, + pub claimed_at: BlockNumber, } -// Contains the list of revealed public keys after a given Merkle proof has been -// correctly verified. #[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] -pub struct VerificationResult( - pub BoundedVec, ConstU32>, -); - -impl TryFrom>> - for VerificationResult -{ - // TODO: Better error handling - type Error = (); - - fn try_from(value: Vec>) -> Result { - let bounded_inner = value.try_into().map_err(|_| ())?; - Ok(Self(bounded_inner)) - } +pub struct VerificationResult< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, +> { + pub did_keys: BoundedVec, ConstU32>, + pub web3_name: Option>, + pub linked_accounts: BoundedVec>, } -impl AsRef<[ProofEntry]> - for VerificationResult +impl< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + > AsRef<[RevealedDidKey]> + for VerificationResult< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + > { - fn as_ref(&self) -> &[ProofEntry] { - self.0.as_ref() + fn as_ref(&self) -> &[RevealedDidKey] { + self.did_keys.as_ref() } } /// A type that verifies a Merkle proof that reveals some leaves representing /// keys in a DID Document. /// Can also be used on its own, without any DID signature verification. -pub struct DidMerkleProofVerifier( +pub struct DidMerkleProofVerifier< + Hasher, + AccountId, + KeyId, + BlockNumber, + Details, + Web3Name, + LinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, +>( + #[allow(clippy::type_complexity)] PhantomData<( Hasher, AccountId, KeyId, BlockNumber, Details, - ConstU32, + Web3Name, + LinkedAccountId, + ConstU32, + ConstU32, )>, ); -impl - IdentityProofVerifier - for DidMerkleProofVerifier -where +impl< + Call, + Subject, + Hasher, + AccountId, + KeyId, + BlockNumber, + Details, + Web3Name, + LinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + > IdentityProofVerifier + for DidMerkleProofVerifier< + Hasher, + AccountId, + KeyId, + BlockNumber, + Details, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + > where // TODO: Remove `Debug` bound BlockNumber: Encode + Clone + Debug, Hasher: sp_core::Hasher, KeyId: Encode + Clone + Ord + Into, + LinkedAccountId: Encode + Clone, + Web3Name: Encode + Clone, { // TODO: Proper error handling type Error = (); - type Proof = MerkleProof>, ProofLeaf>; + type Proof = MerkleProof>, ProofLeaf>; type IdentityDetails = IdentityDetails; type Submitter = AccountId; - type VerificationResult = VerificationResult; + type VerificationResult = VerificationResult< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; - fn verify_proof_for_call_against_entry( + fn verify_proof_for_call_against_details( _call: &Call, _subject: &Subject, _submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, + identity_details: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { // TODO: more efficient by removing cloning and/or collecting. @@ -202,7 +287,7 @@ where .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); verify_trie_proof::, _, _, _>( - &proof_entry.digest.clone().into(), + &identity_details.digest.clone().into(), &proof.blinded, &proof_leaves, ) @@ -210,42 +295,48 @@ where // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. - - // Create a map of the revealed public keys - //TODO: Avoid cloning, and use a map of references for the lookup - let public_keys: BTreeMap> = proof - .revealed - .clone() - .into_iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { - Some((key_id, key_details)) - } else { - None - } - }) - .collect(); - // Create a list of the revealed keys by consuming the provided key reference - // leaves, and looking up the full details from the just-built `public_keys` - // map. - let keys: Vec> = proof - .revealed - .iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { - // TODO: Better error handling. - let key_details = public_keys - .get(key_id) - .expect("Key ID should be present in the map of revealed public keys."); - Some(ProofEntry { - key: key_details.clone(), - relationship: *key_relationship, + #[allow(clippy::type_complexity)] + let (did_keys, web3_name, linked_accounts): ( + BoundedVec, ConstU32>, + Option>, + BoundedVec>, + ) = proof.revealed.iter().try_fold( + ( + BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), + None, + BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), + ), + |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { + ProofLeaf::DidKey(key_id, key_value) => { + keys.try_push(RevealedDidKey { + // TODO: Avoid cloning if possible + id: key_id.0.clone(), + relationship: key_id.1, + details: key_value.0.clone(), }) - } else { - None + .map_err(|_| ())?; + Ok::<_, ()>((keys, web3_name, linked_accounts)) } - }) - .collect(); - keys.try_into() + // TODO: Avoid cloning if possible + ProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( + keys, + Some(RevealedWeb3Name { + web3_name: revealed_web3_name.0.clone(), + claimed_at: details.0.clone(), + }), + linked_accounts, + )), + ProofLeaf::LinkedAccount(account_id, _) => { + linked_accounts.try_push(account_id.0.clone()).map_err(|_| ())?; + Ok::<_, ()>((keys, web3_name, linked_accounts)) + } + }, + )?; + + Ok(VerificationResult { + did_keys, + web3_name, + linked_accounts, + }) } } diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index 56d4dd09b1..66417991e8 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -34,7 +34,6 @@ impl Bump for T where T: CheckedAdd + Zero + One, { - // FIXME: Better implementation? fn bump(&mut self) { *self = self.checked_add(&Self::one()).unwrap_or_else(Self::zero); } diff --git a/dip-template/nodes/dip-provider/src/chain_spec.rs b/dip-template/nodes/dip-provider/src/chain_spec.rs index 7c323d7be3..af6493f67c 100644 --- a/dip-template/nodes/dip-provider/src/chain_spec.rs +++ b/dip-template/nodes/dip-provider/src/chain_spec.rs @@ -100,6 +100,7 @@ fn testnet_genesis( aura: Default::default(), aura_ext: Default::default(), polkadot_xcm: Default::default(), + did_lookup: Default::default(), } } diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index de8f00d270..b5a18965ee 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -18,6 +18,7 @@ parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # DIP +dip-provider-runtime-template.workspace = true dip-support.workspace = true did.workspace = true kilt-dip-support.workspace = true @@ -77,6 +78,7 @@ default = [ std = [ "parity-scale-codec/std", "scale-info/std", + "dip-provider-runtime-template/std", "dip-support/std", "did/std", "kilt-dip-support/std", @@ -125,6 +127,7 @@ std = [ ] runtime-benchmarks = [ + "dip-provider-runtime-template/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 639ff16e6b..b629fe5820 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,22 +17,26 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; +use dip_provider_runtime_template::Web3Name; use frame_support::traits::Contains; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleEntriesAndDidSignature, MerkleRevealedDidSignatureVerifier}, + did::{DidSignatureAndCallVerifier, MerkleLeavesAndDidSignature, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, MerkleProofAndDidSignatureVerifier, }; +use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_std::vec::Vec; use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; -pub type MerkleProofVerifier = DidMerkleProofVerifier, BlockNumber, u128, 10>; +pub type MerkleProofVerifier = + DidMerkleProofVerifier, BlockNumber, u128, Web3Name, LinkableAccountId, 10, 10>; pub type MerkleProofVerifierOutputOf = >::VerificationResult; pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< + KeyIdOf, BlockNumber, Hash, u128, @@ -49,7 +53,10 @@ impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = MerkleEntriesAndDidSignature>, ProofLeaf>, BlockNumber>; + type Proof = MerkleLeavesAndDidSignature< + MerkleProof>, ProofLeaf>, + BlockNumber, + >; type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< BlockNumber, diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index 602e4af539..03fcfb5dfe 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,7 +22,10 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use did::KeyIdOf; +use dip_provider_runtime_template::Web3Name; use kilt_dip_support::merkle::VerificationResult; +use pallet_did_lookup::linkable_account::LinkableAccountId; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -367,8 +370,16 @@ impl pallet_did_lookup::Config for Runtime { type Currency = Balances; type Deposit = ConstU128; type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin>; - type OriginSuccess = DipOrigin>; + type EnsureOrigin = EnsureDipOrigin< + DidIdentifier, + AccountId, + VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, + >; + type OriginSuccess = DipOrigin< + DidIdentifier, + AccountId, + VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, + >; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 77647b7d8d..ce1a7582bb 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -21,7 +21,9 @@ scale-info = {workspace = true, features = ["derive"]} did.workspace = true dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true +pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true +pallet-web3-names.workspace = true runtime-common.workspace = true # Substrate @@ -77,7 +79,9 @@ std = [ "did/std", "dip-support/std", "kilt-runtime-api-dip-provider/std", + "pallet-did-lookup/std", "pallet-dip-provider/std", + "pallet-web3-names/std", "runtime-common/std", "frame-executive/std", "frame-support/std", @@ -119,7 +123,9 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", + "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", + "pallet-web3-names/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index e358eff8a5..97afed38f8 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,11 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::did_details::DidDetails; -use dip_support::IdentityProofAction; +use dip_support::IdentityDetailsAction; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; -use runtime_common::dip::{did::DidIdentityProvider, merkle::DidMerkleRootGenerator}; +use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; @@ -34,7 +33,7 @@ enum ConsumerParachainCalls { #[derive(Encode, Decode)] enum ConsumerParachainDipConsumerCalls { #[codec(index = 0)] - ProcessIdentityAction(IdentityProofAction), + ProcessIdentityAction(IdentityDetailsAction), } pub struct ConsumerParachainTxBuilder; @@ -43,7 +42,7 @@ impl TxBuilder for ConsumerParachainTxBuilder { fn build( _dest: MultiLocation, - action: IdentityProofAction, + action: IdentityDetailsAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) @@ -55,10 +54,9 @@ impl TxBuilder for ConsumerParachainTxBuilder { impl pallet_dip_provider::Config for Runtime { type Identifier = DidIdentifier; - type Identity = DidDetails; type IdentityProofDispatcher = XcmRouterDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; - type IdentityProvider = DidIdentityProvider; + type IdentityProvider = LinkedDidInfoProviderOf; type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; type TxBuilder = ConsumerParachainTxBuilder; diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index b986a95e57..ce1a98ea3c 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -22,6 +22,9 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_web3_names::web3_name::AsciiWeb3Name; +use parity_scale_codec::{Decode, Encode}; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -140,6 +143,8 @@ construct_runtime!( // DID Did: did = 40, + DidLookup: pallet_did_lookup = 41, + Web3Names: pallet_web3_names = 42, // DIP DipProvider: pallet_dip_provider = 50, @@ -365,7 +370,7 @@ impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall parameter_types! { #[derive(Debug, Clone, Eq, PartialEq)] - pub const MaxTotalKeyAgreementKeys: u32 = 1; + pub const MaxTotalKeyAgreementKeys: u32 = 50; } impl did::Config for Runtime { @@ -377,11 +382,11 @@ impl did::Config for Runtime { type FeeCollector = (); type KeyDeposit = ConstU128; type MaxBlocksTxValidity = ConstU32; - type MaxNewKeyAgreementKeys = ConstU32<1>; + type MaxNewKeyAgreementKeys = ConstU32<50>; type MaxNumberOfServicesPerDid = ConstU32<1>; type MaxNumberOfTypesPerService = ConstU32<1>; type MaxNumberOfUrlsPerService = ConstU32<1>; - type MaxPublicKeysPerDid = ConstU32<4>; + type MaxPublicKeysPerDid = ConstU32<53>; type MaxServiceIdLength = ConstU32<100>; type MaxServiceTypeLength = ConstU32<100>; type MaxServiceUrlLength = ConstU32<100>; @@ -394,6 +399,40 @@ impl did::Config for Runtime { type WeightInfo = (); } +impl pallet_did_lookup::Config for Runtime { + type Currency = Balances; + type Deposit = ConstU128; + type DidIdentifier = DidIdentifier; + type EnsureOrigin = EnsureDidOrigin; + type OriginSuccess = DidRawOrigin; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +pub type Web3Name = AsciiWeb3Name; + +impl pallet_web3_names::Config for Runtime { + type BanOrigin = EnsureRoot; + type Currency = Balances; + type Deposit = ConstU128; + type MaxNameLength = ConstU32<32>; + type MinNameLength = ConstU32<3>; + type OriginSuccess = DidRawOrigin; + type OwnerOrigin = EnsureDidOrigin; + type RuntimeEvent = RuntimeEvent; + type Web3Name = Web3Name; + type Web3NameOwner = DidIdentifier; + type WeightInfo = (); +} + +#[derive(Encode, Decode)] +pub struct DipProofRequest { + identifier: DidIdentifier, + keys: Vec>, + accounts: Vec, + should_include_web3_name: bool, +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> SlotDuration { @@ -530,15 +569,14 @@ impl_runtime_apis! { } } - // TODO: `keys` could and should be a BTreeSet, but it makes it more complicated for clients to build the type. So we use a Vec, since the keys are deduplicated anyway at proof creation time. // TODO: Support generating different versions of the proof, based on the provided parameter - impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { - fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { - if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { - DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) - } else { - Err(()) + impl kilt_runtime_api_dip_provider::DipProvider>, ()> for Runtime { + fn generate_proof(request: DipProofRequest) -> Result>, ()> { + let Some(linked_did_info) = ::IdentityProvider::retrieve(&request.identifier)? else { return Err(()) }; + if linked_did_info.a.is_none() { + return Err(()); } + DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()) } } } diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 6be31a5cb1..ae3b735c77 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -21,6 +21,7 @@ kilt-dip-support = { workspace = true, features = ["std"] } kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-did-lookup = { workspace = true, features = ["std"] } +pallet-web3-names = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } parity-scale-codec = {workspace = true, features = ["std", "derive"]} polkadot-parachain = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index 77e5f3907f..c671ae0c83 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -25,9 +25,15 @@ pub(super) mod provider { pub(crate) use dip_provider_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; - use dip_provider_runtime_template::{AccountId, System}; + use dip_provider_runtime_template::{AccountId, Balance, BlockNumber, System, Web3Name}; use kilt_support::deposit::Deposit; + use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectionRecord}; + use pallet_web3_names::web3_name::Web3NameOwnership; use sp_core::{ecdsa, ed25519, sr25519, Pair}; + use sp_runtime::{ + traits::{One, Zero}, + AccountId32, SaturatedConversion, + }; use super::*; @@ -53,9 +59,13 @@ pub(super) mod provider { .unwrap(); details.update_attestation_key(att_key, 0u32).unwrap(); details.update_delegation_key(del_key, 0u32).unwrap(); - details - .add_key_agreement_key(DidEncryptionKey::X25519([100u8; 32]), 0u32) - .unwrap(); + let max_key_agreement_key_count: u8 = + ::MaxTotalKeyAgreementKeys::get().saturated_into(); + (1u8..max_key_agreement_key_count).for_each(|s| { + details + .add_key_agreement_key(DidEncryptionKey::X25519([s; 32]), 0u32) + .unwrap(); + }); details } @@ -74,8 +84,37 @@ pub(super) mod provider { let mut ext = TestExternalities::new(t); let did: DidIdentifier = did_auth_key().public().into(); let details = generate_did_details(); + let acc: AccountId32 = did_auth_key().public().into(); + let web3_name: Web3Name = b"test".to_vec().try_into().unwrap(); ext.execute_with(|| { did::pallet::Did::::insert(&did, details); + pallet_did_lookup::pallet::ConnectedDids::::insert( + LinkableAccountId::from(acc.clone()), + ConnectionRecord { + did: did.clone(), + deposit: Deposit { + amount: Balance::one(), + owner: acc.clone(), + }, + }, + ); + pallet_did_lookup::pallet::ConnectedAccounts::::insert( + &did, + LinkableAccountId::from(acc.clone()), + (), + ); + pallet_web3_names::pallet::Owner::::insert( + &web3_name, + Web3NameOwnership { + claimed_at: BlockNumber::zero(), + owner: did.clone(), + deposit: Deposit { + amount: Balance::one(), + owner: acc.clone(), + }, + }, + ); + pallet_web3_names::pallet::Names::::insert(did, web3_name); System::set_block_number(1); }); ext diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index a868a64ea1..3577a3932a 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -22,12 +22,16 @@ use did::{Did, DidSignature}; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use kilt_dip_support::{ - did::{MerkleEntriesAndDidSignature, TimeBoundDidSignature}, + did::{MerkleLeavesAndDidSignature, TimeBoundDidSignature}, merkle::MerkleProof, }; -use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectedAccounts}; +use pallet_web3_names::{Names, Owner}; use parity_scale_codec::Encode; -use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::{ + did::Web3OwnershipOf, + merkle::{CompleteMerkleProof, DidMerkleRootGenerator}, +}; use sp_core::Pair; use sp_runtime::traits::Zero; use xcm::latest::{ @@ -50,7 +54,7 @@ fn commit_identity() { let did: DidIdentifier = para::provider::did_auth_key().public().into(); - // 1. Send identity proof from DIP provider to DIP consumer. + // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { assert_ok!(DipProvider::commit_identity( RawOrigin::Signed(ProviderAccountId::from([0u8; 32])).into(), @@ -60,7 +64,7 @@ fn commit_identity() { Weight::from_ref_time(4_000), )); }); - // 2. Verify that the proof has made it to the DIP consumer. + // 2. Verify that the commitment has made it to the DIP consumer. ConsumerParachain::execute_with(|| { // 2.1 Verify that there was no XCM error. assert!(!System::events().iter().any(|r| matches!( @@ -78,13 +82,54 @@ fn commit_identity() { let did_details = ProviderParachain::execute_with(|| { Did::get(&did).expect("DID details should be stored on the provider chain.") }); + println!( + "Complete DID details encoded size: {:?} bytes", + did_details.encoded_size() + ); + let (web3_name, ownership_details) = ProviderParachain::execute_with(|| { + let web3_name = + Names::::get(&did).expect("Web3name should be linked to the DID on the provider chain."); + let ownership_details = Owner::::get(&web3_name) + .expect("Web3name details should be present for the retrieved web3name."); + (web3_name, ownership_details) + }); + println!( + "Web3name and ownership size: ({:?}, {:?}) bytes", + web3_name.encoded_size(), + ownership_details.encoded_size(), + ); + let linked_accounts = ProviderParachain::execute_with(|| { + ConnectedAccounts::::iter_key_prefix(&did).collect::>() + }); + println!("Linked accounts size: {:?} bytes", linked_accounts.encoded_size()); let call = ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::::associate_sender {}); // 3.1 Generate a proof let CompleteMerkleProof { proof, .. } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), + &( + Some(did_details.clone()), + Some(Web3OwnershipOf:: { + web3_name, + claimed_at: ownership_details.claimed_at, + }), + Some(linked_accounts.clone()), + ) + .into(), + [ + did_details.authentication_key, + did_details.attestation_key.unwrap(), + did_details.delegation_key.unwrap(), + ] + .iter(), + true, + linked_accounts.iter(), ) .expect("Proof generation should not fail"); + println!( + "Complete merkle proof size: {:?} bytes. Blinded part: {:?} bytes. Revealed part: {:?} bytes.", + proof.encoded_size(), + proof.blinded.encoded_size(), + proof.revealed.encoded_size() + ); // 3.2 Generate a DID signature let genesis_hash = ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())); @@ -103,8 +148,8 @@ fn commit_identity() { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - MerkleEntriesAndDidSignature { - merkle_entries: MerkleProof { + MerkleLeavesAndDidSignature { + merkle_leaves: MerkleProof { blinded: proof.blinded, revealed: proof.revealed, }, diff --git a/pallets/pallet-did-lookup/src/linkable_account.rs b/pallets/pallet-did-lookup/src/linkable_account.rs index 0f2e97b570..4d6ccc6f82 100644 --- a/pallets/pallet-did-lookup/src/linkable_account.rs +++ b/pallets/pallet-did-lookup/src/linkable_account.rs @@ -50,3 +50,12 @@ impl std::fmt::Display for LinkableAccountId { } } } + +// Default implementation required by the DipDidOrigin origin type, only for +// benchmarks. +#[cfg(feature = "runtime-benchmarks")] +impl Default for LinkableAccountId { + fn default() -> Self { + AccountId32::new([0u8; 32]).into() + } +} diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index f9712bf5a0..61818b6683 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -37,7 +37,7 @@ pub mod pallet { use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; - use dip_support::IdentityProofAction; + use dip_support::IdentityDetailsAction; use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; @@ -51,7 +51,7 @@ pub mod pallet { // TODO: Store also additional details received by the provider. #[pallet::storage] #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityProofs = StorageMap< + pub(crate) type IdentityEntries = StorageMap< _, Twox64Concat, ::Identifier, @@ -134,13 +134,13 @@ pub mod pallet { #[pallet::weight(0)] pub fn process_identity_action( origin: OriginFor, - action: IdentityProofAction, + action: IdentityDetailsAction, ) -> DispatchResult { ensure_sibling_para(::RuntimeOrigin::from(origin))?; let event = match action { - IdentityProofAction::Updated(identifier, proof, _) => { - IdentityProofs::::mutate( + IdentityDetailsAction::Updated(identifier, proof, _) => { + IdentityEntries::::mutate( &identifier, |entry: &mut Option< IdentityDetails<::ProofDigest, ::IdentityDetails>, @@ -148,8 +148,8 @@ pub mod pallet { ); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } - IdentityProofAction::Deleted(identifier) => { - IdentityProofs::::remove(&identifier); + IdentityDetailsAction::Deleted(identifier) => { + IdentityEntries::::remove(&identifier); Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) } }?; @@ -171,18 +171,18 @@ pub mod pallet { let submitter = ensure_signed(origin)?; // TODO: Proper error handling ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); - let mut proof_entry = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; - let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_entry( + let mut identity_entry = IdentityEntries::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( &*call, &identifier, &submitter, - &mut proof_entry, + &mut identity_entry, &proof, ) .map_err(|_| Error::::InvalidProof)?; // Write the identity info to storage after it has optionally been updated by // the `ProofVerifier`. - IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof_entry)); + IdentityEntries::::mutate(&identifier, |entry| *entry = Some(identity_entry)); let did_origin = DipOrigin { identifier, account_address: submitter, diff --git a/pallets/pallet-dip-consumer/src/origin.rs b/pallets/pallet-dip-consumer/src/origin.rs index f030044479..415d055e0f 100644 --- a/pallets/pallet-dip-consumer/src/origin.rs +++ b/pallets/pallet-dip-consumer/src/origin.rs @@ -17,6 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use frame_support::{traits::EnsureOrigin, RuntimeDebug}; +use kilt_support::traits::CallSources; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_std::marker::PhantomData; @@ -70,8 +71,7 @@ where } } -impl kilt_support::traits::CallSources - for DipOrigin +impl CallSources for DipOrigin where Identifier: Clone, AccountId: Clone, diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 7f4645f166..63a5c8a831 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -25,11 +25,11 @@ pub trait IdentityProofVerifier { type Submitter; type VerificationResult; - fn verify_proof_for_call_against_entry( + fn verify_proof_for_call_against_details( call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, + identity_details: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result; } @@ -45,11 +45,11 @@ impl IdentityProofVerifier Result { Ok(()) diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index c826d191a7..a970e22d50 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -33,26 +33,26 @@ pub mod pallet { use sp_std::{boxed::Box, fmt::Debug}; use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; - use dip_support::IdentityProofAction; + use dip_support::IdentityDetailsAction; use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; - pub type IdentityProofActionOf = IdentityProofAction<::Identifier, ::ProofOutput>; + pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; + pub type IdentityProofActionOf = IdentityDetailsAction<::Identifier, ::ProofOutput>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::config] pub trait Config: frame_system::Config { type Identifier: Parameter; - type Identity; - type ProofOutput: Clone + Eq + Debug; type IdentityProofGenerator: IdentityProofGenerator< Self::Identifier, - Self::Identity, + IdentityOf, Output = Self::ProofOutput, >; type IdentityProofDispatcher: IdentityProofDispatcher; - type IdentityProvider: IdentityProvider; + type IdentityProvider: IdentityProvider; + type ProofOutput: Clone + Eq + Debug; type RuntimeEvent: From> + IsType<::RuntimeEvent>; type TxBuilder: TxBuilder; } @@ -62,11 +62,6 @@ pub mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); - // #[pallet::storage] - // #[pallet::getter(fn destination_info)] - // pub type DestinationInfos = StorageMap<_, Blake2_128Concat, NetworkId, - // ()>; - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -99,12 +94,12 @@ pub mod pallet { let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { - Ok(Some((identity, _))) => { + Ok(Some(identity)) => { let identity_proof = T::IdentityProofGenerator::generate_commitment(&identifier, &identity) .map_err(|_| Error::::IdentityProofGeneration)?; - Ok(IdentityProofAction::Updated(identifier, identity_proof, ())) + Ok(IdentityDetailsAction::Updated(identifier, identity_proof, ())) } - Ok(None) => Ok(IdentityProofAction::Deleted(identifier)), + Ok(None) => Ok(IdentityDetailsAction::Deleted(identifier)), Err(_) => Err(Error::::IdentityNotFound), }?; // TODO: Add correct version creation based on lookup (?) diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index fd9b90d046..12784472e4 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::IdentityProofAction; +use dip_support::IdentityDetailsAction; use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; @@ -61,7 +61,7 @@ pub mod identity_dispatch { type Error; fn pre_dispatch>( - action: IdentityProofAction, + action: IdentityDetailsAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -80,7 +80,7 @@ pub mod identity_dispatch { type Error = (); fn pre_dispatch<_B>( - _action: IdentityProofAction, + _action: IdentityDetailsAction, _asset: MultiAsset, _weight: Weight, _destination: MultiLocation, @@ -111,7 +111,7 @@ pub mod identity_dispatch { type Error = SendError; fn pre_dispatch>( - action: IdentityProofAction, + action: IdentityDetailsAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -149,35 +149,38 @@ pub mod identity_dispatch { pub use identity_provision::*; pub mod identity_provision { + use sp_std::marker::PhantomData; - pub trait IdentityProvider { + pub trait IdentityProvider { type Error; + type Success; - fn retrieve(identifier: &Identifier) -> Result, Self::Error>; + fn retrieve(identifier: &Identifier) -> Result, Self::Error>; } // Return the `Default` value if `Identity` adn `Details` both implement it. - pub struct DefaultIdentityProvider; + pub struct DefaultIdentityProvider(PhantomData); - impl IdentityProvider for DefaultIdentityProvider + impl IdentityProvider for DefaultIdentityProvider where Identity: Default, - Details: Default, { type Error = (); + type Success = Identity; - fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { - Ok(Some((Identity::default(), Details::default()))) + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + Ok(Some(Identity::default())) } } // Always return `None`. Might be useful for tests. pub struct NoneIdentityProvider; - impl IdentityProvider for NoneIdentityProvider { + impl IdentityProvider for NoneIdentityProvider { type Error = (); + type Success = (); - fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { Ok(None) } } @@ -190,6 +193,6 @@ pub trait TxBuilder { fn build( dest: MultiLocation, - action: IdentityProofAction, + action: IdentityDetailsAction, ) -> Result, Self::Error>; } diff --git a/pallets/pallet-web3-names/src/lib.rs b/pallets/pallet-web3-names/src/lib.rs index d6ed7c4a18..43ecd1f638 100644 --- a/pallets/pallet-web3-names/src/lib.rs +++ b/pallets/pallet-web3-names/src/lib.rs @@ -115,6 +115,8 @@ pub mod pallet { /// The max encoded length of a name. #[pallet::constant] type MaxNameLength: Get; + // FIXME: Refactor the definition of AsciiWeb3Name so that we don't need to + // require `Ord` here /// The type of a name. type Web3Name: FullCodec + Debug @@ -122,7 +124,8 @@ pub mod pallet { + Clone + TypeInfo + TryFrom, Error = Error> - + MaxEncodedLen; + + MaxEncodedLen + + Ord; /// The type of a name owner. type Web3NameOwner: Parameter + MaxEncodedLen; /// Weight information for extrinsics in this pallet. diff --git a/pallets/pallet-web3-names/src/web3_name.rs b/pallets/pallet-web3-names/src/web3_name.rs index 37b393a16d..e83e221aa9 100644 --- a/pallets/pallet-web3-names/src/web3_name.rs +++ b/pallets/pallet-web3-names/src/web3_name.rs @@ -82,6 +82,27 @@ impl PartialEq for AsciiWeb3Name { } } +// FIXME: did not find a way to automatically implement this. +impl Eq for AsciiWeb3Name { + fn assert_receiver_is_total_eq(&self) { + self.0.assert_receiver_is_total_eq() + } +} + +// FIXME: did not find a way to automatically implement this. +impl PartialOrd for AsciiWeb3Name { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.as_slice().partial_cmp(other.as_slice()) + } +} + +// FIXME: did not find a way to automatically implement this. +impl Ord for AsciiWeb3Name { + fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + // FIXME: did not find a way to automatically implement this. impl Clone for AsciiWeb3Name { fn clone(&self) -> Self { @@ -89,6 +110,13 @@ impl Clone for AsciiWeb3Name { } } +// FIXME: did not find a way to automatically implement this. +impl Default for AsciiWeb3Name { + fn default() -> Self { + Self(BoundedVec::default(), PhantomData) + } +} + /// KILT web3 name ownership details. #[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, TypeInfo, MaxEncodedLen)] pub struct Web3NameOwnership { diff --git a/runtime-api/dip-provider/src/lib.rs b/runtime-api/dip-provider/src/lib.rs index 2cdcb534c1..b910f734c1 100644 --- a/runtime-api/dip-provider/src/lib.rs +++ b/runtime-api/dip-provider/src/lib.rs @@ -21,13 +21,11 @@ use parity_scale_codec::Codec; sp_api::decl_runtime_apis! { - pub trait DipProvider where - DidIdentifier: Codec, - KeyId: Codec, - KeyIds: Codec + IntoIterator, + pub trait DipProvider where + ProofRequest: Codec, Success: Codec, Error: Codec, { - fn generate_proof(identifier: DidIdentifier, keys: KeyIds) -> Result; + fn generate_proof(request: ProofRequest) -> Result; } } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 4b1e03c0a6..c650a681ef 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -27,7 +27,7 @@ ctype.workspace = true delegation = {workspace = true, optional = true} did.workspace = true kilt-dip-support.workspace = true -pallet-did-lookup = {workspace = true, optional = true} +pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true pallet-inflation = {workspace = true, optional = true} @@ -66,6 +66,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "ctype/runtime-benchmarks", "did/runtime-benchmarks", + "pallet-did-lookup/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", @@ -92,6 +93,7 @@ std = [ "log/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", "pallet-membership/std", @@ -119,7 +121,7 @@ try-runtime = [ "kilt-support/try-runtime", "pallet-authorship/try-runtime", "pallet-balances/try-runtime", - "pallet-did-lookup", + "pallet-did-lookup/try-runtime", "pallet-inflation", "pallet-membership/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index e954f008e7..cebbf104ce 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -17,26 +17,77 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::did_details::DidDetails; +use kilt_dip_support::{ + did::{CombineIdentityFrom, CombinedIdentityResult}, + merkle::RevealedWeb3Name, +}; +use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; -use sp_std::marker::PhantomData; +use sp_std::{marker::PhantomData, vec::Vec}; pub struct DidIdentityProvider(PhantomData); -impl IdentityProvider, ()> for DidIdentityProvider +impl IdentityProvider for DidIdentityProvider where T: did::Config, { // TODO: Proper error handling type Error = (); + type Success = DidDetails; - fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { + fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { match ( did::Pallet::::get_did(identifier), did::Pallet::::get_deleted_did(identifier), ) { - (Some(details), _) => Ok(Some((details, ()))), + (Some(details), _) => Ok(Some(details)), (_, Some(_)) => Ok(None), _ => Err(()), } } } + +pub type Web3OwnershipOf = + RevealedWeb3Name<::Web3Name, ::BlockNumber>; + +pub struct DidWeb3NameProvider(PhantomData); + +impl IdentityProvider for DidWeb3NameProvider +where + T: pallet_web3_names::Config, +{ + // TODO: Proper error handling + type Error = (); + type Success = Web3OwnershipOf; + + fn retrieve(identifier: &T::Web3NameOwner) -> Result, Self::Error> { + let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) else { return Ok(None) }; + let Some(details) = pallet_web3_names::Pallet::::owner(&web3_name) else { return Err(()) }; + Ok(Some(Web3OwnershipOf:: { + web3_name, + claimed_at: details.claimed_at, + })) + } +} + +pub struct DidLinkedAccountsProvider(PhantomData); + +impl IdentityProvider for DidLinkedAccountsProvider +where + T: pallet_did_lookup::Config, +{ + // TODO: Proper error handling + type Error = (); + type Success = Vec; + + fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { + Ok(Some( + pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier).collect(), + )) + } +} + +pub type LinkedDidInfoProviderOf = + CombineIdentityFrom, DidWeb3NameProvider, DidLinkedAccountsProvider>; +pub type LinkedDidInfoOf = + CombinedIdentityResult>, Option>, Option>>; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index f92cc96805..254380b65f 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -16,22 +16,30 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; +use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; -use kilt_dip_support::merkle::MerkleProof; +use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, MerkleProof}; +use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProofGenerator; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet, marker::PhantomData, vec::Vec}; +use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use kilt_dip_support::merkle::{ - DidKeyRelationship, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, ProofLeaf, -}; +use kilt_dip_support::merkle::{DidKeyRelationship, ProofLeaf}; + +use crate::{dip::did::LinkedDidInfoOf, DidIdentifier}; pub type BlindedValue = Vec; -pub type DidMerkleProofOf = - MerkleProof, ProofLeaf, ::BlockNumber>>; +pub type DidMerkleProofOf = MerkleProof< + Vec, + ProofLeaf< + KeyIdOf, + ::BlockNumber, + ::Web3Name, + LinkableAccountId, + >, +>; #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { @@ -41,9 +49,16 @@ pub struct CompleteMerkleProof { pub struct DidMerkleRootGenerator(PhantomData); +type ProofLeafOf = ProofLeaf< + KeyIdOf, + ::BlockNumber, + ::Web3Name, + LinkableAccountId, +>; + impl DidMerkleRootGenerator where - T: did::Config, + T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { // Calls the function in the `sp_trie` crate to generate the merkle root given // the provided `DidDetails`. @@ -56,66 +71,105 @@ where // A valid proof will contain a leaf with the key details for each reference // leaf, with multiple reference leaves potentially referring to the same // details leaf, as we already do with out `DidDetails` type. - fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { + fn calculate_root_with_db(identity: &LinkedDidInfoOf, db: &mut MemoryDB) -> Result { + // Fails if the DID details do not exist. + let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { return Err(()) }; let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); // Authentication key - let auth_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey( - identity.authentication_key, + // TODO: No panic + let auth_key_details = did_details + .public_keys + .get(&did_details.authentication_key) + .expect("Authentication key should be part of the public keys."); + let auth_leaf = ProofLeafOf::::DidKey( + DidKeyMerkleKey( + did_details.authentication_key, DidVerificationKeyRelationship::Authentication.into(), ), - KeyReferenceValue, + DidKeyMerkleValue(auth_key_details.clone()), ); trie_builder .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) .map_err(|_| ())?; // Attestation key, if present - if let Some(att_key_id) = identity.attestation_key { - let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), - KeyReferenceValue, + if let Some(att_key_id) = did_details.attestation_key { + let att_key_details = did_details + .public_keys + .get(&att_key_id) + .expect("Attestation key should be part of the public keys."); + let att_leaf = ProofLeafOf::::DidKey( + (att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into(), + att_key_details.clone().into(), ); trie_builder .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) .map_err(|_| ())?; }; // Delegation key, if present - if let Some(del_key_id) = identity.delegation_key { - let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), - KeyReferenceValue, + if let Some(del_key_id) = did_details.delegation_key { + let del_key_details = did_details + .public_keys + .get(&del_key_id) + .expect("Delegation key should be part of the public keys."); + let del_leaf = ProofLeafOf::::DidKey( + (del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into(), + del_key_details.clone().into(), ); trie_builder .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) .map_err(|_| ())?; }; // Key agreement keys - identity + did_details .key_agreement_keys .iter() .try_for_each(|id| -> Result<(), ()> { - let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(*id, DidKeyRelationship::Encryption), - KeyReferenceValue, + let key_agreement_details = did_details + .public_keys + .get(id) + .expect("Key agreement key should be part of the public keys."); + let enc_leaf = ProofLeafOf::::DidKey( + (*id, DidKeyRelationship::Encryption).into(), + key_agreement_details.clone().into(), ); trie_builder .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) .map_err(|_| ())?; Ok(()) })?; - // Public keys - identity - .public_keys - .iter() - .try_for_each(|(id, key_details)| -> Result<(), ()> { - let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*id), KeyDetailsValue(key_details.clone())); - trie_builder - .insert(key_leaf.encoded_key().as_slice(), key_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - Ok(()) - })?; + + // Linked accounts + if let Some(linked_accounts) = linked_accounts { + linked_accounts + .iter() + .try_for_each(|linked_account| -> Result<(), ()> { + let linked_account_leaf = ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); + trie_builder + .insert( + linked_account_leaf.encoded_key().as_slice(), + linked_account_leaf.encoded_value().as_slice(), + ) + .map_err(|_| ())?; + Ok(()) + })?; + } + + // Web3name, if present + if let Some(web3name_details) = web3_name { + let web3_name_leaf = ProofLeafOf::::Web3Name( + web3name_details.web3_name.clone().into(), + web3name_details.claimed_at.into(), + ); + trie_builder + .insert( + web3_name_leaf.encoded_key().as_slice(), + web3_name_leaf.encoded_value().as_slice(), + ) + .map_err(|_| ())?; + } + trie_builder.commit(); Ok(trie_builder.root().to_owned()) } @@ -126,76 +180,86 @@ where // generates a merkle proof which only reveals the details of the provided key // IDs. #[allow(clippy::result_unit_err)] - pub fn generate_proof<'a, K>( - identity: &DidDetails, - mut key_ids: K, + pub fn generate_proof<'a, K, A>( + identity: &LinkedDidInfoOf, + key_ids: K, + should_include_web3_name: bool, + account_ids: A, ) -> Result>, ()> where K: Iterator>, + A: Iterator, { + // Fails if the DID details do not exist. + let (Some(did_details), linked_web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { return Err(()) }; + let mut db = MemoryDB::default(); let root = Self::calculate_root_with_db(identity, &mut db)?; - #[allow(clippy::type_complexity)] - let leaves: BTreeSet, T::BlockNumber>> = - key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { - let key_details = identity.public_keys.get(key_id).ok_or(())?; - // Adds a key reference leaf for each relationship the key ID is part of. - if *key_id == identity.authentication_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), - KeyReferenceValue, - )); - } - if Some(*key_id) == identity.attestation_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::AssertionMethod.into()), - KeyReferenceValue, - )); - } - if Some(*key_id) == identity.delegation_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), - KeyReferenceValue, - )); + let mut leaves = key_ids + .map(|key_id| -> Result, ()> { + let key_details = did_details.public_keys.get(key_id).ok_or(())?; + // Create the merkle leaf key depending on the relationship of the key to the + // DID document. + let did_key_merkle_key: DidKeyMerkleKey> = if *key_id == did_details.authentication_key { + Ok((*key_id, DidVerificationKeyRelationship::Authentication.into()).into()) + } else if Some(*key_id) == did_details.attestation_key { + Ok((*key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into()) + } else if Some(*key_id) == did_details.delegation_key { + Ok((*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into()) + } else if did_details.key_agreement_keys.contains(key_id) { + Ok((*key_id, DidKeyRelationship::Encryption).into()) + } else { + Err(()) + }?; + Ok(ProofLeaf::DidKey(did_key_merkle_key, key_details.clone().into())) + }) + .chain(account_ids.map(|account_id| -> Result, ()> { + let Some(linked_accounts) = linked_accounts else { return Err(()) }; + if linked_accounts.contains(account_id) { + Ok(ProofLeaf::LinkedAccount(account_id.clone().into(), ().into())) + } else { + Err(()) } - if identity.key_agreement_keys.contains(key_id) { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidKeyRelationship::Encryption), - KeyReferenceValue, - )); - }; - // Then adds the actual key details to the merkle proof. - // If the same key is specified twice, the old key is simply replaced with a new - // key of the same value. - let key_details_leaf = - ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); - if !set.contains(&key_details_leaf) { - set.insert(key_details_leaf); - } - Ok(set) - })?; + })) + .collect::, _>>()?; + + match (should_include_web3_name, linked_web3_name) { + // If web3name should be included and it exists... + (true, Some(web3name_details)) => { + leaves.push(ProofLeaf::Web3Name( + web3name_details.web3_name.clone().into(), + web3name_details.claimed_at.into(), + )); + Ok(()) + } + // ...else if web3name should be included and it DOES NOT exist... + (true, None) => Err(()), + // ...else if web3name should NOT be included. + (false, _) => Ok(()), + }?; + let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; Ok(CompleteMerkleProof { root, proof: DidMerkleProofOf:: { blinded: proof, - revealed: leaves.into_iter().collect::>(), + revealed: leaves.into_iter().collect(), }, }) } } -impl IdentityProofGenerator> for DidMerkleRootGenerator +impl IdentityProofGenerator> for DidMerkleRootGenerator where - T: did::Config, + T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { // TODO: Proper error handling type Error = (); type Output = T::Hash; - fn generate_commitment(_identifier: &T::DidIdentifier, identity: &DidDetails) -> Result { + fn generate_commitment(_identifier: &DidIdentifier, identity: &LinkedDidInfoOf) -> Result { let mut db = MemoryDB::default(); Self::calculate_root_with_db(identity, &mut db) } From df804f150724ca0f90c84e92b70031db2d6ae4b5 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 29 May 2023 15:45:11 +0200 Subject: [PATCH 08/28] feat: configurable origin check for 'commit_identity' (#526) Identity information should be entirely under the control of the DID subject. Until now, it was not the case as anyone could have called the `commit_identity` extrinsic for any DID subject. This PR changes the origin check for the `commit_identity` extrinsic of provider pallet from `EnsureSigned` to a configurable origin check. The XCM integration tests have also been updated to use the `EitherOfDiverse, EnsureDidOrigin>;` implementation. Now, except for cases where democracy decides to do this operation, the DID subject has to agree to it by signing the operation, which will then be dispatched via the `submit_did_call` extrinsic of the DID pallet. The feature that any account can pay for the fees still applies, as that is the same also for DID-signed extrinsics. --- dip-template/runtimes/dip-provider/src/dip.rs | 6 +++++- dip-template/runtimes/xcm-tests/src/tests.rs | 4 ++-- pallets/pallet-dip-provider/src/lib.rs | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 97afed38f8..b8559c997c 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,13 +16,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use did::EnsureDidOrigin; use dip_support::IdentityDetailsAction; +use frame_support::traits::EitherOfDiverse; +use frame_system::EnsureRoot; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; +use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; #[derive(Encode, Decode)] enum ConsumerParachainCalls { @@ -53,6 +56,7 @@ impl TxBuilder for ConsumerParachainTxBuilder { } impl pallet_dip_provider::Config for Runtime { + type CommitOrigin = EitherOfDiverse, EnsureDidOrigin>; type Identifier = DidIdentifier; type IdentityProofDispatcher = XcmRouterDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 3577a3932a..62114f0fee 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -46,7 +46,7 @@ use dip_consumer_runtime_template::{ BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; -use dip_provider_runtime_template::{AccountId as ProviderAccountId, DipProvider, Runtime as ProviderRuntime}; +use dip_provider_runtime_template::{DipProvider, Runtime as ProviderRuntime}; #[test] fn commit_identity() { @@ -57,7 +57,7 @@ fn commit_identity() { // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { assert_ok!(DipProvider::commit_identity( - RawOrigin::Signed(ProviderAccountId::from([0u8; 32])).into(), + RawOrigin::Root.into(), did.clone(), Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index a970e22d50..cf2983fa2d 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -28,7 +28,7 @@ pub use crate::pallet::*; pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, weights::Weight}; + use frame_support::{pallet_prelude::*, traits::EnsureOrigin, weights::Weight}; use frame_system::pallet_prelude::*; use sp_std::{boxed::Box, fmt::Debug}; use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; @@ -44,6 +44,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + type CommitOrigin: EnsureOrigin; type Identifier: Parameter; type IdentityProofGenerator: IdentityProofGenerator< Self::Identifier, @@ -90,7 +91,7 @@ pub mod pallet { weight: Weight, ) -> DispatchResult { // TODO: Charge the dispatcher based on the destination weight configuration - ensure_signed(origin)?; + T::CommitOrigin::ensure_origin(origin)?; let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { From b92f628f827a5d9f744818f40d93ace0ceaafeca Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 15 Jun 2023 10:12:33 +0200 Subject: [PATCH 09/28] feat: better XCM fee management (#528) Fixes https://github.com/KILTprotocol/ticket/issues/2566. This PR changes the way XCM fees are handled for DIP. Before, the provider parachain sovereign account on the consumer chain was paying for the XCM fees, while now it is the sovereign account of the user that triggered the operation on the provider chain, to also pay for the fees on the receiver chain in whatever asset specified in the extrinsic. With XCM v3, it's not possible for a parachain to charge an interior location (e.g., with a `DescendOrigin` instruction), hence a new barrier and a new origin converter have been introduced, which are supposed to be used together to implement the feeing model DIP has envisioned. ## Relevant changes ### Reworked `XcmRouterIdentityDispatcher` The `XcmRouterIdentityDispatcher` type used to be part of the `pallet-dip-provider` pallet. Because it is really specific to KILT, it has been moved into the `kilt-dip-support` crate. Additionally, the router now packages the DIP XCM program with the following instructions: 1. `ExpectOrigin` with the provider parachain origin, making sure the same XCM message cannot be sent by regular users via the XCM pallet. 2. `DescendOrigin` with a new `AccountId32` representing the dispatcher account ID on the provider chain. 3. `WithdrawAsset` with the `MultiAsset` specified in the `commit_identity` extrinsic in the `pallet-dip-provider` pallet 4. `BuyExecution` with the max `Weight` specified in the `commit_identity` extrinsic in the `pallet-dip-provider` pallet 5. `Transact` with `OriginKind::Native` and the same weight limit as above 6. `RefundSurplus` 7. `DepositAsset` with any remaining assets in the holding registry back to the dispatcher's account on the consumer chain In the demo runtimes and tests, the `Account32Hash` location converter has been used to convert the account ID information coming from the `X2` junction containing the sibling provider parachain and the disptacher account. ### Configurable origin for the `pallet_dip_provider::commit_identity` extrinsic The `CommitOrigin` origin was already configurable, but now its `Success` result is required to implement the `SubmitterInfo` trait, which returns the `AccountId` of the tx submitter, so that the information can be used to compose the XCM message that will be sent to destination (specifically it is included in the `DescendOrigin` and `DepositAsset` instructions). The trait has been implemented for the `AccountId32` type, which is returned by the `EnsureSigned` origin check (which could be used for tests), and for the `DidRawOrigin` type, which is returned by the `EnsureDidOrigin` origin check (which has been used in the demo runtimes). ### The `AllowParachainProviderAsSubaccount` barrier For DIP to implement the feeing model devised here, where the user pays for the fees on both chain, a new barrier + origin converter must be integrated into the XCM configuration. The `AllowParachainProviderAsSubaccount` barrier verifies that 1. the origin of the XCM is a parachain with an ID that is returned by the `ProviderParaId` generic type, and 2. the order of the instructions in the XCM program matches exactly what is expected in the DIP flow. If either the origin or the set of instructions is different, the barrier will return an error. To extend existing barrier, there are two additional types: `OkOrElseCheckForParachainProvider` which tries to match the incoming XCM message against the new `AllowParachainProviderAsSubaccount` barrier if none of the previous barriers have succeeded (explicit approval), and `ErrOrElseCheckForParachainProvider` which tries to match the XCM message against the new barrier only if none of the previous barriers have failed (explicit rejection). ### The `AccountIdJunctionAsParachain` origin converter Since there is no way to convert an `X2` junction of parachain + sub-account, the `AccountIdJunctionAsParachain` does just that. It takes the `X2` junction of a *sibling* parachain + sub-account, if the parachain matches the ID given by the `ProviderParaId` type, and converts it into a `ParachainOrigin`. **The origin converter and the barrier are meant to be used together, otherwise configuring only one or the other opens the way to security issues on the consumer chain!** --- Cargo.lock | 8 + crates/kilt-dip-support/Cargo.toml | 10 +- crates/kilt-dip-support/src/lib.rs | 1 + crates/kilt-dip-support/src/xcm.rs | 289 ++++++++++++++++++ .../runtimes/dip-consumer/src/xcm_config.rs | 17 +- dip-template/runtimes/dip-provider/Cargo.toml | 3 + dip-template/runtimes/dip-provider/src/dip.rs | 14 +- .../runtimes/dip-provider/src/xcm_config.rs | 2 +- dip-template/runtimes/xcm-tests/Cargo.toml | 3 + dip-template/runtimes/xcm-tests/src/para.rs | 52 +++- dip-template/runtimes/xcm-tests/src/tests.rs | 138 ++++++++- pallets/pallet-dip-provider/Cargo.toml | 3 + pallets/pallet-dip-provider/src/lib.rs | 23 +- pallets/pallet-dip-provider/src/traits.rs | 89 ++---- runtimes/common/src/xcm_config.rs | 2 +- runtimes/peregrine/src/xcm_config.rs | 2 +- runtimes/spiritnet/src/xcm_config.rs | 2 +- 17 files changed, 553 insertions(+), 105 deletions(-) create mode 100644 crates/kilt-dip-support/src/xcm.rs diff --git a/Cargo.lock b/Cargo.lock index d033efc860..759e8c19fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2450,6 +2450,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-dip-support", "kilt-runtime-api-dip-provider", "pallet-aura", "pallet-authorship", @@ -2502,12 +2503,14 @@ dependencies = [ "did", "dip-consumer-runtime-template", "dip-provider-runtime-template", + "dip-support", "frame-support", "frame-system", "kilt-dip-support", "kilt-support", "pallet-balances", "pallet-did-lookup", + "pallet-dip-provider", "pallet-web3-names", "parachain-info", "parity-scale-codec", @@ -2522,6 +2525,7 @@ dependencies = [ "sp-runtime", "sp-std", "xcm", + "xcm-builder", "xcm-emulator", "xcm-executor", ] @@ -4221,6 +4225,7 @@ name = "kilt-dip-support" version = "1.11.0-dev" dependencies = [ "did", + "dip-support", "frame-support", "frame-system", "pallet-dip-consumer", @@ -4231,6 +4236,8 @@ dependencies = [ "sp-runtime", "sp-std", "sp-trie", + "xcm", + "xcm-executor", ] [[package]] @@ -6281,6 +6288,7 @@ dependencies = [ name = "pallet-dip-provider" version = "1.11.0-dev" dependencies = [ + "did", "dip-support", "frame-support", "frame-system", diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index e06e17664e..b417b44cba 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true [dependencies] # Internal dependencies did.workspace = true +dip-support.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true @@ -28,10 +29,15 @@ sp-core.workspace = true sp-trie.workspace = true sp-std.workspace = true +# Polkadot dependencies +xcm.workspace = true +xcm-executor.workspace = true + [features] default = ["std"] std = [ "did/std", + "dip-support/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", "parity-scale-codec/std", @@ -41,6 +47,8 @@ std = [ "sp-runtime/std", "sp-core/std", "sp-trie/std", - "sp-std/std" + "sp-std/std", + "xcm-executor/std", + "xcm/std" ] runtime-benchmarks = [] diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 103a5c504f..f4c1a05e4a 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -28,6 +28,7 @@ use crate::did::MerkleLeavesAndDidSignature; pub mod did; pub mod merkle; pub mod traits; +pub mod xcm; /// A type that chains a Merkle proof verification with a DID signature /// verification. The required input of this type is a tuple (A, B) where A is diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs new file mode 100644 index 0000000000..8a84459242 --- /dev/null +++ b/crates/kilt-dip-support/src/xcm.rs @@ -0,0 +1,289 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use dip_support::IdentityDetailsAction; +use pallet_dip_provider::traits::{IdentityProofDispatcher, TxBuilder}; +use parity_scale_codec::Encode; +use sp_core::Get; +use sp_std::marker::PhantomData; +use xcm::v3::{ + Instruction::{BuyExecution, DepositAsset, DescendOrigin, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, + InteriorMultiLocation, + Junction::AccountId32, + Junctions::{Here, X1}, + MultiAsset, + MultiAssetFilter::Wild, + MultiAssets, MultiLocation, OriginKind, SendError, SendXcm, Weight, + WeightLimit::Limited, + WildMultiAsset::All, + Xcm, +}; + +// Dispatcher using a type implementing the `SendXcm` trait. +// It properly encodes the `Transact` operation, then delegates everything else +// to the sender, similarly to what the XCM pallet's `send` extrinsic does. +pub struct XcmRouterIdentityDispatcher( + PhantomData<(Router, UniversalLocationProvider)>, +); + +impl + IdentityProofDispatcher + for XcmRouterIdentityDispatcher +where + Router: SendXcm, + UniversalLocationProvider: Get, + Identifier: Encode, + ProofOutput: Encode, + AccountId: Into<[u8; 32]> + Clone, +{ + type PreDispatchOutput = Router::Ticket; + type Error = SendError; + + fn pre_dispatch>( + action: IdentityDetailsAction, + source: AccountId, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { + // TODO: Replace with proper error handling + let dest_tx = Builder::build(destination, action) + .map_err(|_| ()) + .expect("Failed to build call"); + + // TODO: Set an error handler and an appendix to refund any leftover funds to + // the provider parachain sovereign account. + let operation = [[ + ExpectOrigin(Some( + Here.into_location() + .reanchored(&destination, UniversalLocationProvider::get()) + .unwrap(), + )), + DescendOrigin(X1(AccountId32 { + network: None, + id: source.clone().into(), + })), + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + weight_limit: Limited(weight), + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { + parents: 1, + // Re-anchor the same account junction as seen from the destination. + // TODO: Error handling + interior: Here + .into_location() + .reanchored(&destination, UniversalLocationProvider::get()) + .unwrap() + .pushed_with_interior(AccountId32 { + network: None, + id: source.into(), + }) + .unwrap() + .interior, + }, + }, + ]] + .concat(); + // TODO: Restructure the trait to be able to inject the [Instruction] provider, + // and unit test that. + debug_assert!(barriers::check_expected_dip_instruction_order(&operation).is_ok()); + let op = Xcm(operation); + Router::validate(&mut Some(destination), &mut Some(op)) + } + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Router::deliver(pre_output).map(|_| ()) + } +} + +pub mod barriers { + use super::*; + + use frame_support::ensure; + use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; + use xcm_executor::traits::ShouldExecute; + + // Must match the order of instructions as produced by the provider's + // implementation of the `IdentityProofDispatcher` trait. + pub(crate) fn check_expected_dip_instruction_order( + instructions: &[Instruction], + ) -> Result<(), ()> { + let mut iter = instructions.iter(); + match ( + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + ) { + ( + // A first instruction different than `DescendOrigin` is needed to distinguish between user-triggered + // and parachain-triggered XCM messages, since also the XCM pallet always preprends user-created XCM + // messages with a `DescendOrigin` instruction. + Some(ExpectOrigin(..)), + // Go down to user level to charge them for the XCM fees. + Some(DescendOrigin(X1(AccountId32 { .. }))), + // Expect the user to first withdraw an asset to pay for the fees. + Some(WithdrawAsset { .. }), + // Buy execution time. + Some(BuyExecution { .. }), + // Although this is irrelevant since `origin_kind` can also be specified by a user, we use + // `OriginKind::Native` here to make clear this is a parachain-dispatched XCM message. + Some(Transact { + origin_kind: OriginKind::Native, + .. + }), + // Any unused weight is refunded. + Some(RefundSurplus), + // Any unused assets are refunded back into the user's account. + Some(DepositAsset { .. }), + // No more instructions are allowed. + None, + ) => Ok(()), + _ => Err(()), + } + } + + // Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees + // from their balance, and then carry on with a `Transact`. + // Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` + // origin converter. + pub struct AllowParachainProviderAsSubaccount(PhantomData); + + impl ShouldExecute for AllowParachainProviderAsSubaccount + where + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + #[cfg(feature = "std")] + println!( + "AllowParachainProviderAsSubaccount::should_execute(origin = {:?}, instructions = {:?}", + origin, instructions + ); + // Ensure that the origin is a parachain allowed to act as identity provider. + ensure!( + *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), + () + ); + check_expected_dip_instruction_order(instructions) + } + } + + // Decorate an existing barrier to add one more check in case all the previous + // barriers fail. + pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + + impl ShouldExecute for OkOrElseCheckForParachainProvider + where + Barrier: ShouldExecute, + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + }) + } + } + + // Decorate an existing barrier to check for the provider parachain origin only + // in case none of the previous barriers fail. + pub struct ErrOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + + impl ShouldExecute for ErrOrElseCheckForParachainProvider + where + Barrier: ShouldExecute, + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + } + } +} + +pub mod origins { + use super::*; + + use xcm::v3::{Junction::Parachain, Junctions::X2}; + use xcm_executor::traits::ConvertOrigin; + + pub struct AccountIdJunctionAsParachain( + PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, + ); + + impl ConvertOrigin + for AccountIdJunctionAsParachain + where + ProviderParaId: Get, + ParachainOrigin: From, + RuntimeOrigin: From, + { + fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { + let origin = origin.into(); + let provider_para_id = ProviderParaId::get(); + match (kind, origin) { + ( + OriginKind::Native, + MultiLocation { + parents: 1, + interior: X2(Parachain(para_id), AccountId32 { .. }), + }, + ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), + _ => Err(origin), + } + } + } +} diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs index bb7775be4f..83d2918888 100644 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -19,16 +19,16 @@ use cumulus_primitives_utility::ParentAsUmp; use frame_support::{ parameter_types, - traits::{ConstU32, Contains, Everything, Nothing}, + traits::{ConstU32, Contains, Nothing}, weights::{IdentityFee, Weight}, }; use frame_system::EnsureRoot; +use kilt_dip_support::xcm::{barriers::OkOrElseCheckForParachainProvider, origins::AccountIdJunctionAsParachain}; use pallet_xcm::TestWeightInfo; -use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ - AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedToAccountId32, UsingComponents, + Account32Hash, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + SignedToAccountId32, UsingComponents, }; use xcm_executor::XcmExecutor; @@ -39,12 +39,13 @@ use crate::{ parameter_types! { pub HereLocation: MultiLocation = MultiLocation::here(); + pub NoneNetworkId: Option = None; pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); } -pub type Barrier = AllowTopLevelPaidExecutionFrom; -pub type AssetTransactorLocationConverter = SiblingParachainConvertsVia; +pub type Barrier = OkOrElseCheckForParachainProvider, ConstU32<2_000>>; +pub type AssetTransactorLocationConverter = Account32Hash; pub type LocalAssetTransactor = CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; pub type XcmRouter = (ParentAsUmp, XcmpQueue); @@ -74,7 +75,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = (); type MaxAssetsIntoHolding = ConstU32<64>; type MessageExporter = (); - type OriginConverter = SiblingParachainAsNative; + type OriginConverter = AccountIdJunctionAsParachain, cumulus_pallet_xcm::Origin, RuntimeOrigin>; type PalletInstancesInfo = AllPalletsWithSystem; type ResponseHandler = (); type RuntimeCall = RuntimeCall; diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index ce1a7582bb..14fdeb6257 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true dip-support.workspace = true +kilt-dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true @@ -78,6 +79,7 @@ std = [ "scale-info/std", "did/std", "dip-support/std", + "kilt-dip-support/std", "kilt-runtime-api-dip-provider/std", "pallet-did-lookup/std", "pallet-dip-provider/std", @@ -123,6 +125,7 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", "pallet-web3-names/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index b8559c997c..59b6aedade 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,16 +16,15 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::EnsureDidOrigin; +use did::{DidRawOrigin, EnsureDidOrigin}; use dip_support::IdentityDetailsAction; -use frame_support::traits::EitherOfDiverse; -use frame_system::EnsureRoot; -use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; +use kilt_dip_support::xcm::XcmRouterIdentityDispatcher; +use pallet_dip_provider::traits::TxBuilder; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; +use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, UniversalLocation, XcmRouter}; #[derive(Encode, Decode)] enum ConsumerParachainCalls { @@ -56,9 +55,10 @@ impl TxBuilder for ConsumerParachainTxBuilder { } impl pallet_dip_provider::Config for Runtime { - type CommitOrigin = EitherOfDiverse, EnsureDidOrigin>; + type CommitOriginCheck = EnsureDidOrigin; + type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; - type IdentityProofDispatcher = XcmRouterDispatcher; + type IdentityProofDispatcher = XcmRouterIdentityDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; type IdentityProvider = LinkedDidInfoProviderOf; type ProofOutput = Hash; diff --git a/dip-template/runtimes/dip-provider/src/xcm_config.rs b/dip-template/runtimes/dip-provider/src/xcm_config.rs index d73c126189..df62925d53 100644 --- a/dip-template/runtimes/dip-provider/src/xcm_config.rs +++ b/dip-template/runtimes/dip-provider/src/xcm_config.rs @@ -24,7 +24,7 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::TestWeightInfo; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; use xcm_executor::XcmExecutor; diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index ae3b735c77..d922974440 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -15,12 +15,14 @@ cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } dip-consumer-runtime-template = { workspace = true, features = ["std"] } dip-provider-runtime-template = { workspace = true, features = ["std"] } +dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } kilt-dip-support = { workspace = true, features = ["std"] } kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-did-lookup = { workspace = true, features = ["std"] } +pallet-dip-provider = { workspace = true, features = ["std"] } pallet-web3-names = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } parity-scale-codec = {workspace = true, features = ["std", "derive"]} @@ -35,6 +37,7 @@ sp-io = { workspace = true, features = ["std"] } sp-runtime = { workspace = true, features = ["std"] } sp-std = { workspace = true, features = ["std"] } xcm = { workspace = true, features = ["std"] } +xcm-builder = { workspace = true, features = ["std"] } xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } xcm-executor = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index c671ae0c83..a24076fe04 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -25,7 +25,7 @@ pub(super) mod provider { pub(crate) use dip_provider_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; - use dip_provider_runtime_template::{AccountId, Balance, BlockNumber, System, Web3Name}; + use dip_provider_runtime_template::{AccountId, Balance, BlockNumber, System, Web3Name, UNIT}; use kilt_support::deposit::Deposit; use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectionRecord}; use pallet_web3_names::web3_name::Web3NameOwnership; @@ -38,6 +38,8 @@ pub(super) mod provider { use super::*; pub const PARA_ID: u32 = 2_000; + pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([190u8; 32]); + const INITIAL_BALANCE: Balance = 100_000 * UNIT; pub(crate) fn did_auth_key() -> ed25519::Pair { ed25519::Pair::from_seed(&[200u8; 32]) @@ -81,6 +83,12 @@ pub(super) mod provider { >::assimilate_storage(¶chain_info_config, &mut t) .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(DISPATCHER_ACCOUNT, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = TestExternalities::new(t); let did: DidIdentifier = did_auth_key().public().into(); let details = generate_did_details(); @@ -137,7 +145,11 @@ pub(super) mod consumer { }; use dip_consumer_runtime_template::System; - use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; + use xcm::v3::{ + Junction::{AccountId32, Parachain}, + Junctions::X2, + ParentThen, + }; use xcm_executor::traits::Convert; use super::*; @@ -146,9 +158,18 @@ pub(super) mod consumer { pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([90u8; 32]); const INITIAL_BALANCE: Balance = 100_000 * UNIT; - pub(crate) fn provider_parachain_account() -> AccountId { - AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(provider::PARA_ID))).into()) - .expect("Conversion of account from provider parachain to consumer parachain should not fail.") + pub(crate) fn provider_dispatcher_account_on_consumer() -> AccountId { + AssetTransactorLocationConverter::convert( + ParentThen(X2( + Parachain(provider::PARA_ID), + AccountId32 { + network: None, + id: provider::DISPATCHER_ACCOUNT.into(), + }, + )) + .into(), + ) + .expect("Conversion of account from provider parachain to consumer parachain should not fail.") } pub(crate) fn para_ext() -> TestExternalities { @@ -165,7 +186,7 @@ pub(super) mod consumer { pallet_balances::GenesisConfig:: { balances: vec![ - (provider_parachain_account(), INITIAL_BALANCE), + (provider_dispatcher_account_on_consumer(), INITIAL_BALANCE), (DISPATCHER_ACCOUNT, INITIAL_BALANCE), ], } @@ -178,4 +199,23 @@ pub(super) mod consumer { }); ext } + + #[cfg(test)] + pub(crate) use test_utils::*; + + #[cfg(test)] + mod test_utils { + use super::*; + + use polkadot_parachain::primitives::Sibling; + use xcm::v3::Junctions::X1; + use xcm_builder::SiblingParachainConvertsVia; + + pub(crate) fn provider_parachain_account_on_consumer() -> AccountId { + SiblingParachainConvertsVia::::convert( + ParentThen(X1(Parachain(provider::PARA_ID))).into(), + ) + .expect("Conversion of account from provider parachain to consumer parachain should not fail.") + } + } } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 62114f0fee..c44e45dd33 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -18,7 +18,8 @@ use super::*; -use did::{Did, DidSignature}; +use did::{Did, DidRawOrigin, DidSignature}; +use dip_support::IdentityDetailsAction; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use kilt_dip_support::{ @@ -26,6 +27,7 @@ use kilt_dip_support::{ merkle::MerkleProof, }; use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectedAccounts}; +use pallet_dip_provider::traits::TxBuilder; use pallet_web3_names::{Names, Owner}; use parity_scale_codec::Encode; use runtime_common::dip::{ @@ -34,34 +36,59 @@ use runtime_common::dip::{ }; use sp_core::Pair; use sp_runtime::traits::Zero; -use xcm::latest::{ - Junction::Parachain, - Junctions::{Here, X1}, - ParentThen, +use xcm::{ + v3::{ + Instruction::{BuyExecution, DepositAsset, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, + Junction::{AccountId32, Parachain}, + Junctions::{Here, X1}, + MultiAsset, + MultiAssetFilter::Wild, + MultiLocation, OriginKind, ParentThen, + WeightLimit::Limited, + WildMultiAsset::All, + Xcm, + }, + VersionedXcm, }; use xcm_emulator::TestExt; use cumulus_pallet_xcmp_queue::Event as XcmpEvent; use dip_consumer_runtime_template::{ - BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, - RuntimeEvent, System, + Balances, BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, + RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, +}; +use dip_provider_runtime_template::{ + ConsumerParachainTxBuilder, DipProvider, PolkadotXcm as ProviderXcmPallet, Runtime as ProviderRuntime, + UniversalLocation, }; -use dip_provider_runtime_template::{DipProvider, Runtime as ProviderRuntime}; #[test] fn commit_identity() { Network::reset(); let did: DidIdentifier = para::provider::did_auth_key().public().into(); + let consumer_location: MultiLocation = ParentThen(X1(Parachain(para::consumer::PARA_ID))).into(); + let asset: MultiAsset = (Here, 1_000_000_000).into(); + let weight = Weight::from_ref_time(4_000); + let provider_parachain_on_consumer_parachain_balance_before = ConsumerParachain::execute_with(|| { + Balances::free_balance(para::consumer::provider_parachain_account_on_consumer()) + }); + let dispatcher_on_consumer_parachain_balance_before = ConsumerParachain::execute_with(|| { + Balances::free_balance(para::consumer::provider_dispatcher_account_on_consumer()) + }); // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { assert_ok!(DipProvider::commit_identity( - RawOrigin::Root.into(), + DidRawOrigin { + id: did.clone(), + submitter: para::provider::DISPATCHER_ACCOUNT + } + .into(), did.clone(), - Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), - Box::new((Here, 1_000_000_000).into()), - Weight::from_ref_time(4_000), + Box::new(consumer_location.into_versioned()), + Box::new(asset.into()), + weight, )); }); // 2. Verify that the commitment has made it to the DIP consumer. @@ -77,6 +104,19 @@ fn commit_identity() { ))); // 2.2 Verify the proof digest was stored correctly. assert!(DipConsumer::identity_proofs(&did).is_some()); + // 2.3 Verify that the provider parachain sovereign account balance has not + // changed. + let provider_parachain_on_consumer_parachain_balance_after = + Balances::free_balance(para::consumer::provider_parachain_account_on_consumer()); + assert_eq!( + provider_parachain_on_consumer_parachain_balance_before, + provider_parachain_on_consumer_parachain_balance_after + ); + // 2.4 Verify that the dispatcher's account balance on the consumer parachain + // has decreased. + let dispatcher_on_consumer_parachain_balance_after = + Balances::free_balance(para::consumer::provider_dispatcher_account_on_consumer()); + assert!(dispatcher_on_consumer_parachain_balance_after < dispatcher_on_consumer_parachain_balance_before); }); // 3. Call an extrinsic on the consumer chain with a valid proof and signature let did_details = ProviderParachain::execute_with(|| { @@ -169,3 +209,77 @@ fn commit_identity() { assert_eq!(details, Some(1u128)); }); } + +#[test] +fn user_generated_commit_identity() { + Network::reset(); + + let did: DidIdentifier = para::provider::did_auth_key().public().into(); + let consumer_location: MultiLocation = ParentThen(X1(Parachain(para::consumer::PARA_ID))).into(); + let asset: MultiAsset = (Here, 1_000_000_000).into(); + let weight = Weight::from_ref_time(4_000); + let dest_tx = ConsumerParachainTxBuilder::build(consumer_location, IdentityDetailsAction::Deleted(did.clone())) + .expect("Provider Tx builder should not fail to create the encoded `Transact` call."); + let message = ProviderParachain::execute_with(|| { + Xcm::<()>(vec![ + ExpectOrigin(Some( + Here.into_location() + .reanchored(&consumer_location, UniversalLocation::get()) + .unwrap(), + )), + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + weight_limit: Limited(weight), + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { + parents: 1, + interior: Here + .into_location() + .reanchored(&consumer_location, UniversalLocation::get()) + .unwrap() + .pushed_with_interior(AccountId32 { + network: None, + id: para::provider::DISPATCHER_ACCOUNT.into(), + }) + .unwrap() + .interior, + }, + }, + ]) + }); + // 1. Send identity commitment from DIP provider to DIP consumer via a + // user-dispatched XCM call using the XCM pallet (no parachain origin). + ProviderParachain::execute_with(|| { + assert_ok!(ProviderXcmPallet::send( + RawOrigin::Signed(para::provider::DISPATCHER_ACCOUNT).into(), + Box::new(consumer_location.into()), + Box::new(VersionedXcm::from(message)) + )); + }); + // 2. Verify that the commitment has NOT made it to the DIP consumer and must + // have failed, since this was a user-generated XCM message on the provider + // chain using the XCM pallet. + ConsumerParachain::execute_with(|| { + // 2.1 Verify that there was an XCM error. + println!("{:?}", System::events()); + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XcmpQueue(XcmpEvent::Fail { + error: _, + message_hash: _, + weight: _ + }) + ))); + // 2.2 Verify there is no storage entry in the consumer pallet. + assert!(DipConsumer::identity_proofs(&did).is_none()); + }); +} diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 6283ad284a..e87da12329 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -14,6 +14,7 @@ version.workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +did.workspace = true dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true @@ -25,6 +26,7 @@ xcm.workspace = true [features] default = ["std"] std = [ + "did/std", "dip-support/std", "frame-support/std", "frame-system/std", @@ -34,6 +36,7 @@ std = [ "xcm/std", ] runtime-benchmarks = [ + "did/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks" ] diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index cf2983fa2d..34ca55f400 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -35,7 +35,7 @@ pub mod pallet { use dip_support::IdentityDetailsAction; - use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; + use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, SubmitterInfo, TxBuilder}; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; pub type IdentityProofActionOf = IdentityDetailsAction<::Identifier, ::ProofOutput>; @@ -44,14 +44,15 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type CommitOrigin: EnsureOrigin; + type CommitOriginCheck: EnsureOrigin; + type CommitOrigin: SubmitterInfo; type Identifier: Parameter; type IdentityProofGenerator: IdentityProofGenerator< Self::Identifier, IdentityOf, Output = Self::ProofOutput, >; - type IdentityProofDispatcher: IdentityProofDispatcher; + type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; type ProofOutput: Clone + Eq + Debug; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -90,8 +91,7 @@ pub mod pallet { asset: Box, weight: Weight, ) -> DispatchResult { - // TODO: Charge the dispatcher based on the destination weight configuration - T::CommitOrigin::ensure_origin(origin)?; + let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.submitter())?; let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { @@ -107,12 +107,17 @@ pub mod pallet { let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - let (ticket, _) = - T::IdentityProofDispatcher::pre_dispatch::(action.clone(), asset, weight, destination) - .map_err(|_| Error::::Predispatch)?; + let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( + action.clone(), + dispatcher, + asset, + weight, + destination, + ) + .map_err(|_| Error::::Predispatch)?; // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the - // fee, in addition to the cost on the target chain. + // fee. T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 12784472e4..f5a8836d1f 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,6 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use did::DidRawOrigin; use dip_support::IdentityDetailsAction; use xcm::{latest::prelude::*, DoubleEncoded}; @@ -53,15 +54,14 @@ pub mod identity_dispatch { use super::*; use frame_support::weights::Weight; - use parity_scale_codec::Encode; - use sp_std::{marker::PhantomData, vec}; - pub trait IdentityProofDispatcher { + pub trait IdentityProofDispatcher { type PreDispatchOutput; type Error; fn pre_dispatch>( action: IdentityDetailsAction, + source: AccountId, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -73,14 +73,15 @@ pub mod identity_dispatch { // Returns `Ok` without doing anything. pub struct NullIdentityProofDispatcher; - impl IdentityProofDispatcher - for NullIdentityProofDispatcher + impl + IdentityProofDispatcher for NullIdentityProofDispatcher { type PreDispatchOutput = (); type Error = (); fn pre_dispatch<_B>( _action: IdentityDetailsAction, + _source: AccountId, _asset: MultiAsset, _weight: Weight, _destination: MultiLocation, @@ -92,59 +93,6 @@ pub mod identity_dispatch { Ok(()) } } - - // Dispatcher using a type implementing the `SendXcm` trait. - // It properly encodes the `Transact` operation, then delegates everything else - // to the sender, similarly to what the XCM pallet's `send` extrinsic does. - pub struct XcmRouterDispatcher( - PhantomData<(Router, Identifier, ProofOutput, Details)>, - ); - - impl IdentityProofDispatcher - for XcmRouterDispatcher - where - Router: SendXcm, - Identifier: Encode, - ProofOutput: Encode, - { - type PreDispatchOutput = Router::Ticket; - type Error = SendError; - - fn pre_dispatch>( - action: IdentityDetailsAction, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { - // TODO: Replace with proper error handling - let dest_tx = Builder::build(destination, action) - .map_err(|_| ()) - .expect("Failed to build call"); - - // TODO: Set an error handler and an appendix to refund any leftover funds to - // the provider parachain sovereign account. - let operation = [vec![ - WithdrawAsset(asset.clone().into()), - BuyExecution { - fees: asset, - // TODO: Configurable weight limit? - weight_limit: Unlimited, - }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: weight, - call: dest_tx, - }, - ]] - .concat(); - let op = Xcm(operation); - Router::validate(&mut Some(destination), &mut Some(op)) - } - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Router::deliver(pre_output).map(|_| ()) - } - } } pub use identity_provision::*; @@ -196,3 +144,28 @@ pub trait TxBuilder { action: IdentityDetailsAction, ) -> Result, Self::Error>; } + +pub trait SubmitterInfo { + type Submitter; + + fn submitter(&self) -> Self::Submitter; +} + +impl SubmitterInfo for frame_support::sp_runtime::AccountId32 { + type Submitter = Self; + + fn submitter(&self) -> Self::Submitter { + self.clone() + } +} + +impl SubmitterInfo for DidRawOrigin +where + AccountId: Clone, +{ + type Submitter = AccountId; + + fn submitter(&self) -> Self::Submitter { + self.submitter.clone() + } +} diff --git a/runtimes/common/src/xcm_config.rs b/runtimes/common/src/xcm_config.rs index 51eed3b5f3..3ac0affa7b 100644 --- a/runtimes/common/src/xcm_config.rs +++ b/runtimes/common/src/xcm_config.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use frame_support::{log, match_types, parameter_types, weights::Weight}; use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{AccountId32Aliases, CurrencyAdapter, IsConcrete, ParentIsPreset, SiblingParachainConvertsVia}; use xcm_executor::traits::ShouldExecute; diff --git a/runtimes/peregrine/src/xcm_config.rs b/runtimes/peregrine/src/xcm_config.rs index a21d4e1049..4691ccedce 100644 --- a/runtimes/peregrine/src/xcm_config.rs +++ b/runtimes/peregrine/src/xcm_config.rs @@ -27,7 +27,7 @@ use frame_support::{ }; use pallet_xcm::XcmPassthrough; use sp_core::ConstU32; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, RelayChainAsNative, SiblingParachainAsNative, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, UsingComponents, WithComputedOrigin, diff --git a/runtimes/spiritnet/src/xcm_config.rs b/runtimes/spiritnet/src/xcm_config.rs index e399457755..840a2e48d0 100644 --- a/runtimes/spiritnet/src/xcm_config.rs +++ b/runtimes/spiritnet/src/xcm_config.rs @@ -27,7 +27,7 @@ use frame_support::{ }; use pallet_xcm::XcmPassthrough; use sp_core::ConstU32; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, RelayChainAsNative, SiblingParachainAsNative, SignedAccountId32AsNative, SignedToAccountId32, UsingComponents, WithComputedOrigin, From b9b536a28123ab096acf02b92e5d646247b16df4 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 20 Sep 2023 14:11:27 +0100 Subject: [PATCH 10/28] feat: replace XCM with state proofs (#543) Fixes https://github.com/KILTprotocol/ticket/issues/2730. This PR introduces a new way of doing cross-chain identity bridging for the same consensus system: state proofs. Few points make this solution better than the previous XCM-based approach: 1. No need to anchor the commitment to each destination via XCM. The commitment happens once on the provider chain and can be used everywhere else. 2. No need to establish HRMP channels between the provider and the relying party chain. 3. No outdated information. State proofs guarantee that always the latest value of the commitment is used by the relying party. Of course the identity info can still change without the commitment be updated, but this is easily solvable by batching the commitment update together with the operations where the identity bits change. Few TODOs are present, and they will be addressed in next PRs. ## Main changes * The overarching `MerkleProofAndDidSignatureVerifier` has been renamed to `DipSiblingProviderStateProofVerifier`, with a bunch of additional generics to make it agnostic over who the provider is, and also who the relaychain is (e.g., Polkadot, Kusama, Rococo). * The `process_identity_action` has been completely removed from the `pallet-dip-consumer` pallet, since XCM is not required anymore * Identity commitments are now stored on the **provider** chain rather than the relying party chain. * The `commit_identity` extrinsic also only has the identifier now, without the need to specify `destination`, `asset` and `weight` since an identity commitment is now usable everywhere by design. * A new pallet `pallet-relay-store` which is meant to be used in conjunction with the `pallet-dip-consumer` and that stores the last N relay chain state roots, against which state proofs can be verified. Relay chain state roots are stored in the `on_finalize`, before they are cleaned by the `on_finalize` of the `cumulus-pallet-parachain-system`. ### Additions from the relaychain PR (https://github.com/KILTprotocol/kilt-node/pull/553) * A new `ChildParachainDipStateProof` and `DipChildProviderStateProofVerifier` to be deployed on the relaychain to start accepting DIP operations * `SiblingParachainHeadProofVerifier` renamed to `ParachainHeadProofVerifier` * `KiltDipCommitmentsForDipProviderPallet` does not exist anymore, and it's been replaced by `ProviderParachainStateInfoViaProviderPallet`, which depends on the provider `Runtime` enum * A new `HistoricalBlockRegistry` trait to be implemented by the relaychain to provide historic block hashes against which DIP proofs can be verified * The `postit` pallet has been ported over from the Decoded demo branch, and used instead of the lookup pallet to showcase DIP capabilities ## How to switch - Remove all the XCM-related pallets from both provider and consumer runtime templates - Add the `pallet-relay-store` pallet to the consumer runtime template, and configure all traits as needed --- Cargo.lock | 65 +-- Cargo.toml | 8 +- crates/kilt-dip-support/Cargo.toml | 31 +- crates/kilt-dip-support/src/did.rs | 336 +++---------- crates/kilt-dip-support/src/lib.rs | 457 ++++++++++++++++-- crates/kilt-dip-support/src/merkle.rs | 127 ++--- crates/kilt-dip-support/src/state_proofs.rs | 341 +++++++++++++ crates/kilt-dip-support/src/traits.rs | 103 +++- crates/kilt-dip-support/src/utils.rs | 114 +++++ crates/kilt-dip-support/src/xcm.rs | 289 ----------- .../nodes/dip-consumer/src/chain_spec.rs | 2 - .../nodes/dip-provider/src/chain_spec.rs | 1 - .../pallets/pallet-postit}/Cargo.toml | 23 +- dip-template/pallets/pallet-postit/src/lib.rs | 185 +++++++ .../pallets/pallet-postit/src/post.rs | 55 +++ .../pallets/pallet-postit/src/traits.rs | 23 + dip-template/runtimes/dip-consumer/Cargo.toml | 23 +- dip-template/runtimes/dip-consumer/src/dip.rs | 102 ++-- dip-template/runtimes/dip-consumer/src/lib.rs | 61 +-- .../dip-consumer/src/origin_adapter.rs | 59 +++ .../runtimes/dip-consumer/src/xcm_config.rs | 153 ------ dip-template/runtimes/dip-provider/Cargo.toml | 20 +- dip-template/runtimes/dip-provider/src/dip.rs | 41 +- dip-template/runtimes/dip-provider/src/lib.rs | 35 +- .../runtimes/dip-provider/src/xcm_config.rs | 133 ----- pallets/pallet-dip-consumer/Cargo.toml | 6 +- pallets/pallet-dip-consumer/src/lib.rs | 96 +--- pallets/pallet-dip-consumer/src/traits.rs | 18 +- pallets/pallet-dip-provider/Cargo.toml | 4 - pallets/pallet-dip-provider/src/lib.rs | 91 ++-- pallets/pallet-dip-provider/src/traits.rs | 71 +-- pallets/pallet-relay-store/Cargo.toml | 42 ++ pallets/pallet-relay-store/src/lib.rs | 109 +++++ .../pallet-relay-store/src/relay.rs | 13 +- runtimes/common/Cargo.toml | 1 + runtimes/common/src/dip/did.rs | 2 +- runtimes/common/src/dip/merkle.rs | 26 +- 37 files changed, 1808 insertions(+), 1458 deletions(-) create mode 100644 crates/kilt-dip-support/src/state_proofs.rs create mode 100644 crates/kilt-dip-support/src/utils.rs delete mode 100644 crates/kilt-dip-support/src/xcm.rs rename {crates/dip-support => dip-template/pallets/pallet-postit}/Cargo.toml (50%) create mode 100644 dip-template/pallets/pallet-postit/src/lib.rs create mode 100644 dip-template/pallets/pallet-postit/src/post.rs create mode 100644 dip-template/pallets/pallet-postit/src/traits.rs create mode 100644 dip-template/runtimes/dip-consumer/src/origin_adapter.rs delete mode 100644 dip-template/runtimes/dip-consumer/src/xcm_config.rs delete mode 100644 dip-template/runtimes/dip-provider/src/xcm_config.rs create mode 100644 pallets/pallet-relay-store/Cargo.toml create mode 100644 pallets/pallet-relay-store/src/lib.rs rename crates/dip-support/src/lib.rs => pallets/pallet-relay-store/src/relay.rs (73%) diff --git a/Cargo.lock b/Cargo.lock index 96fe117ef3..258fca0c15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2381,14 +2381,11 @@ dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", "dip-provider-runtime-template", - "dip-support", "frame-executive", "frame-support", "frame-system", @@ -2400,13 +2397,14 @@ dependencies = [ "pallet-collator-selection", "pallet-did-lookup", "pallet-dip-consumer", + "pallet-postit", + "pallet-relay-store", "pallet-session", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", - "pallet-xcm", "parachain-info", "parity-scale-codec", "polkadot-parachain", @@ -2424,9 +2422,6 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "xcm", - "xcm-builder", - "xcm-executor", ] [[package]] @@ -2486,13 +2481,10 @@ dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", - "dip-support", "frame-executive", "frame-support", "frame-system", @@ -2511,7 +2503,6 @@ dependencies = [ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-web3-names", - "pallet-xcm", "parachain-info", "parity-scale-codec", "runtime-common", @@ -2528,18 +2519,6 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "xcm", - "xcm-builder", - "xcm-executor", -] - -[[package]] -name = "dip-support" -version = "1.12.0-dev" -dependencies = [ - "frame-support", - "parity-scale-codec", - "scale-info", ] [[package]] @@ -4402,16 +4381,22 @@ dependencies = [ name = "kilt-dip-support" version = "1.12.0-dev" dependencies = [ + "cumulus-pallet-parachain-system", "did", - "dip-support", "frame-support", "frame-system", + "hash-db", + "hex-literal 0.3.4", "pallet-dip-consumer", "pallet-dip-provider", + "pallet-relay-store", "parity-scale-codec", + "rococo-runtime", "scale-info", "sp-core", + "sp-io", "sp-runtime", + "sp-state-machine", "sp-std", "sp-trie", "xcm", @@ -6429,13 +6414,12 @@ dependencies = [ name = "pallet-dip-consumer" version = "1.12.0-dev" dependencies = [ - "cumulus-pallet-xcm", - "dip-support", "frame-support", "frame-system", "kilt-support", "parity-scale-codec", "scale-info", + "sp-core", "sp-std", ] @@ -6444,13 +6428,11 @@ name = "pallet-dip-provider" version = "1.12.0-dev" dependencies = [ "did", - "dip-support", "frame-support", "frame-system", "parity-scale-codec", "scale-info", "sp-std", - "xcm", ] [[package]] @@ -6791,6 +6773,18 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-postit" +version = "1.12.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-preimage" version = "4.0.0-dev" @@ -6875,6 +6869,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-relay-store" +version = "1.12.0-dev" +dependencies = [ + "cumulus-pallet-parachain-system", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 26068c6f9f..cf103d157b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ version = "1.12.0-dev" members = [ "crates/*", "dip-template/nodes/*", + "dip-template/pallets/*", "dip-template/runtimes/*", "nodes/*", "pallets/*", @@ -35,13 +36,14 @@ clap = "4.1.6" env_logger = "0.10.0" fluent-uri = { version = "0.1.4", default-features = false } futures = {version = "0.3.21", default-features = false} +hash-db = { version = "0.16.0", default-features = false } hex = {version = "0.4.0", default-features = false} hex-literal = "0.3.4" jsonrpsee = "0.16.2" libsecp256k1 = {version = "0.7", default-features = false} log = "0.4.17" parity-scale-codec = {version = "3.1.5", default-features = false} -scale-info = {version = "2.1.1", default-features = false} +scale-info = {version = "2.7.0", default-features = false} serde = "1.0.144" serde_json = "1.0.85" sha3 = {version = "0.10.0", default-features = false} @@ -57,12 +59,12 @@ pallet-dip-consumer = {path = "pallets/pallet-dip-consumer", default-features = pallet-dip-provider = {path = "pallets/pallet-dip-provider", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "pallets/pallet-inflation", default-features = false} +pallet-relay-store = {path = "pallets/pallet-relay-store", default-features = false} pallet-web3-names = {path = "pallets/pallet-web3-names", default-features = false} parachain-staking = {path = "pallets/parachain-staking", default-features = false} public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) -dip-support = {path = "crates/dip-support", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} kilt-support = {path = "support", default-features = false} @@ -71,6 +73,7 @@ runtime-common = {path = "runtimes/common", default-features = false} # Templates dip-consumer-runtime-template = {path = "dip-template/runtimes/dip-consumer", default-features = false} dip-provider-runtime-template = {path = "dip-template/runtimes/dip-provider", default-features = false} +pallet-postit = {path = "dip-template/pallets/pallet-postit", default-features = false} # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} @@ -138,6 +141,7 @@ sp-offchain = {git = "https://github.com/paritytech/substrate", default-features sp-runtime = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-session = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-staking = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} +sp-state-machine = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-std = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-transaction-pool = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-trie = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index 61b9c8aaeb..49893b6fd6 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -11,11 +11,14 @@ repository.workspace = true version.workspace = true [dependencies] +# External dependencies +hash-db.workspace = true + # Internal dependencies did.workspace = true -dip-support.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true +pallet-relay-store.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} @@ -26,31 +29,47 @@ frame-system.workspace = true frame-support.workspace = true sp-runtime.workspace = true sp-core.workspace = true -sp-trie.workspace = true +sp-io.workspace = true +sp-state-machine.workspace = true sp-std.workspace = true +sp-trie.workspace = true # Polkadot dependencies +rococo-runtime.workspace = true xcm.workspace = true xcm-executor.workspace = true +# Cumulus dependencies +cumulus-pallet-parachain-system.workspace = true + +[dev-dependencies] +hex-literal.workspace = true +sp-io = { workspace = true, features = ["std"] } + [features] default = ["std"] std = [ + "hash-db/std", "did/std", - "dip-support/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", + "pallet-relay-store/std", "parity-scale-codec/std", "scale-info/std", "frame-system/std", "frame-support/std", "sp-runtime/std", "sp-core/std", - "sp-trie/std", + "sp-io/std", + "sp-state-machine/std", "sp-std/std", + "sp-trie/std", + "rococo-runtime/std", + "xcm/std", "xcm-executor/std", - "xcm/std" + "cumulus-pallet-parachain-system/std", ] runtime-benchmarks = [ - "pallet-dip-consumer/runtime-benchmarks" + "pallet-dip-consumer/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks" ] diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index c1d37be9a4..7d17b2a8e9 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -21,308 +21,126 @@ use did::{ DidSignature, DidVerificationKeyRelationship, }; use frame_support::ensure; -use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; -use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_core::{ConstU64, Get, RuntimeDebug}; +use sp_core::RuntimeDebug; use sp_runtime::traits::CheckedSub; use sp_std::marker::PhantomData; use crate::{ merkle::RevealedDidKey, - traits::{Bump, DidDipOriginFilter}, + traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, }; #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct TimeBoundDidSignature { - pub signature: DidSignature, - pub block_number: BlockNumber, +pub(crate) struct RevealedDidKeysAndSignature { + pub merkle_leaves: RevealedDidKeys, + pub did_signature: TimeBoundDidSignature, } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct MerkleLeavesAndDidSignature { - pub merkle_leaves: MerkleLeaves, - pub did_signature: TimeBoundDidSignature, +pub struct TimeBoundDidSignature { + pub signature: DidSignature, + pub block_number: BlockNumber, } -/// A type that verifies a DID signature over some DID keys revealed by a -/// previously-verified Merkle proof. It requires the `Details` type to -/// implement the `Bump` trait to avoid replay attacks. The basic verification -/// logic verifies that the signature has been generated over the encoded tuple -/// (call, identity details, submitter_address, submission_block_number, -/// genesis_hash). Additional details can be added to the end of the tuple by -/// providing a `SignedExtraProvider`. -pub struct MerkleRevealedDidSignatureVerifier< - KeyId, - BlockNumber, - Digest, - Details, - AccountId, +pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - const SIGNATURE_VALIDITY: u64, - GenesisHashProvider, - Hash, - SignedExtraProvider = (), - SignedExtra = (), + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, >( #[allow(clippy::type_complexity)] PhantomData<( - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - ConstU64, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, )>, ); impl< Call, - Subject, - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - const SIGNATURE_VALIDITY: u64, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, - > IdentityProofVerifier - for MerkleRevealedDidSignatureVerifier< - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, + > + RevealedDidKeysSignatureAndCallVerifier< + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - SIGNATURE_VALIDITY, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, > where - AccountId: Encode, - BlockNumber: Encode + CheckedSub + Into + PartialOrd + sp_std::fmt::Debug, Call: Encode, - Digest: Encode, - Details: Bump + Encode, - MerkleProofEntries: AsRef<[RevealedDidKey]>, - BlockNumberProvider: Get, - GenesisHashProvider: Get, - Hash: Encode, - SignedExtraProvider: Get, - SignedExtra: Encode, + Submitter: Encode, + ContextProvider: DidSignatureVerifierContext, + ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + ContextProvider::Hash: Encode, + ContextProvider::SignedExtra: Encode, + DidLocalDetails: Bump + Default + Encode, + MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, + CallVerifier: DipCallOriginFilter, { - // TODO: Error handling - type Error = (); - /// The proof must be a list of Merkle leaves that have been previously - /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = MerkleLeavesAndDidSignature; - /// The `Details` that are part of the identity details must implement the - /// `Bump` trait. - type IdentityDetails = IdentityDetails; - /// The type of the submitter's accounts. - type Submitter = AccountId; - /// Successful verifications return the verification key used to validate - /// the provided signature and its relationship to the DID subject. - type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); - - fn verify_proof_for_call_against_details( + #[allow(clippy::result_unit_err)] + pub(crate) fn verify_did_signature_for_call( call: &Call, - _subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { - let block_number = BlockNumberProvider::get(); - let is_signature_fresh = - if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.did_signature.block_number) { - // False if the signature is too old. - blocks_ago_from_now.into() <= SIGNATURE_VALIDITY - } else { - // Signature generated at a future time, not possible to verify. - false - }; - + submitter: &Submitter, + local_details: &mut Option, + merkle_revealed_did_signature: RevealedDidKeysAndSignature, + ) -> Result<(DidVerificationKey, DidVerificationKeyRelationship), ()> { + let block_number = ContextProvider::block_number(); + let is_signature_fresh = if let Some(blocks_ago_from_now) = + block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) + { + // False if the signature is too old. + blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + } else { + // Signature generated at a future time, not possible to verify. + false + }; ensure!(is_signature_fresh, ()); let encoded_payload = ( call, - &identity_details.details, + &local_details, submitter, - &proof.did_signature.block_number, - GenesisHashProvider::get(), - SignedExtraProvider::get(), + &merkle_revealed_did_signature.did_signature.block_number, + ContextProvider::genesis_hash(), + ContextProvider::signed_extra(), ) .encode(); // Only consider verification keys from the set of revealed keys. - let mut proof_verification_keys = proof.merkle_leaves.as_ref().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | { - let DidPublicKey::PublicVerificationKey(key) = key else { return None }; - Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) - }); + let mut proof_verification_keys = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { + relationship, details: DidPublicKeyDetails { key, .. }, .. } | { + let DidPublicKey::PublicVerificationKey(key) = key else { return None }; + Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) }); let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { verification_key - .verify_signature(&encoded_payload, &proof.did_signature.signature) + .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) .is_ok() }); let Some((key, relationship)) = valid_signing_key else { return Err(()) }; - identity_details.details.bump(); + if let Some(details) = local_details { + details.bump(); + } else { + *local_details = Some(DidLocalDetails::default()); + }; + CallVerifier::check_call_origin_info(call, &(key.clone(), relationship)).map_err(|_| ())?; Ok((key.clone(), relationship)) } } - -/// A type that chains a DID signature verification, as provided by -/// `MerkleRevealedDidSignatureVerifier`, and a call filtering logic based on -/// the type of key used in the signature. -/// Verification bails out early in case of invalid DID signatures. Otherwise, -/// the retrieved key and its relationship is passed to the call verifier to do -/// some additional lookups on the call. -/// The `CallVerifier` only performs internal checks, while all input and output -/// types are taken from the provided `DidSignatureVerifier` type. -pub struct DidSignatureAndCallVerifier( - PhantomData<(DidSignatureVerifier, CallVerifier)>, -); - -impl IdentityProofVerifier - for DidSignatureAndCallVerifier -where - DidSignatureVerifier: IdentityProofVerifier, - CallVerifier: DidDipOriginFilter, -{ - // FIXME: Better error handling - type Error = (); - /// The input proof is the same accepted by the `DidSignatureVerifier`. - type Proof = DidSignatureVerifier::Proof; - /// The identity details are the same accepted by the - /// `DidSignatureVerifier`. - type IdentityDetails = DidSignatureVerifier::IdentityDetails; - /// The submitter address is the same accepted by the - /// `DidSignatureVerifier`. - type Submitter = DidSignatureVerifier::Submitter; - /// The verification result is the same accepted by the - /// `DidSignatureVerifier`. - type VerificationResult = DidSignatureVerifier::VerificationResult; - - fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { - let did_signing_key = DidSignatureVerifier::verify_proof_for_call_against_details( - call, - subject, - submitter, - identity_details, - proof, - ) - .map_err(|_| ())?; - CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?; - Ok(did_signing_key) - } -} - -pub struct CombinedIdentityResult { - pub a: OutputA, - pub b: OutputB, - pub c: OutputC, -} - -impl From<(OutputA, OutputB, OutputC)> - for CombinedIdentityResult -{ - fn from(value: (OutputA, OutputB, OutputC)) -> Self { - Self { - a: value.0, - b: value.1, - c: value.2, - } - } -} - -impl CombinedIdentityResult -where - OutputB: Default, - OutputC: Default, -{ - pub fn from_a(a: OutputA) -> Self { - Self { - a, - b: OutputB::default(), - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputC: Default, -{ - pub fn from_b(b: OutputB) -> Self { - Self { - a: OutputA::default(), - b, - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputB: Default, -{ - pub fn from_c(c: OutputC) -> Self { - Self { - a: OutputA::default(), - b: OutputB::default(), - c, - } - } -} - -pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); - -impl IdentityProvider for CombineIdentityFrom -where - A: IdentityProvider, - B: IdentityProvider, - C: IdentityProvider, -{ - // TODO: Proper error handling - type Error = (); - type Success = CombinedIdentityResult, Option, Option>; - - fn retrieve(identifier: &Identifier) -> Result, Self::Error> { - match ( - A::retrieve(identifier), - B::retrieve(identifier), - C::retrieve(identifier), - ) { - // If no details is returned, return None for the whole result - (Ok(None), Ok(None), Ok(None)) => Ok(None), - // Otherwise, return `Some` or `None` depending on each result - (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { - a: ok_a, - b: ok_b, - c: ok_c, - })), - // If any of them returns an `Err`, return an `Err` - _ => Err(()), - } - } -} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index f4c1a05e4a..c14037fd3b 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -20,77 +20,440 @@ #![cfg_attr(not(feature = "std"), no_std)] +use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; +use scale_info::TypeInfo; +use sp_core::{Get, RuntimeDebug, U256}; +use sp_runtime::{ + generic::Header, + traits::{AtLeast32BitUnsigned, CheckedSub, Hash, MaybeDisplay, Member, SimpleBitOps}, +}; +use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; + +use ::did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_std::marker::PhantomData; -use crate::did::MerkleLeavesAndDidSignature; +use crate::{ + did::{RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, TimeBoundDidSignature}, + merkle::{DidMerkleProof, DidMerkleProofVerifier, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, + traits::{ + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, + RelayChainStorageInfo, + }, + utils::OutputOf, +}; pub mod did; pub mod merkle; +pub mod state_proofs; pub mod traits; -pub mod xcm; - -/// A type that chains a Merkle proof verification with a DID signature -/// verification. The required input of this type is a tuple (A, B) where A is -/// the type of input required by the `MerkleProofVerifier` and B is a -/// `DidSignature`. -/// The successful output of this type is the output type of the -/// `MerkleProofVerifier`, meaning that DID signature verification happens -/// internally and does not transform the result in any way. -pub struct MerkleProofAndDidSignatureVerifier( - PhantomData<(BlockNumber, MerkleProofVerifier, DidSignatureVerifier)>, +pub mod utils; + +pub use state_proofs::relay_chain::RococoStateRootsViaRelayStorePallet; + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +pub struct SiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, +> { + para_state_root: ParachainRootStateProof, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, +} + +#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] +pub struct ParachainRootStateProof { + relay_block_height: RelayBlockHeight, + proof: Vec>, +} + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +pub struct DipMerkleProofAndDidSignature { + leaves: DidMerkleProof, + signature: TimeBoundDidSignature, +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, ); -impl IdentityProofVerifier - for MerkleProofAndDidSignatureVerifier -where - BlockNumber: Clone, - MerkleProofVerifier: IdentityProofVerifier, - // TODO: get rid of this if possible - MerkleProofVerifier::VerificationResult: Clone, - DidSignatureVerifier: IdentityProofVerifier< +impl< Call, Subject, - Proof = MerkleLeavesAndDidSignature, - IdentityDetails = MerkleProofVerifier::IdentityDetails, - Submitter = MerkleProofVerifier::Submitter, - >, + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, + OutputOf: Ord, + RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::Key: AsRef<[u8]>, + + SiblingProviderParachainId: Get, + + SiblingProviderStateInfo: + traits::ProviderParachainStateInfo, + OutputOf: Ord + From>, + SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::Commitment: Decode, + SiblingProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: DidSignatureVerifierContext, + LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, { - // FIXME: Better error handling type Error = (); - // FIXME: Better type declaration - type Proof = MerkleLeavesAndDidSignature; - type IdentityDetails = DidSignatureVerifier::IdentityDetails; - type Submitter = MerkleProofVerifier::Submitter; - type VerificationResult = MerkleProofVerifier::VerificationResult; + type IdentityDetails = LocalDidDetails; + type Proof = SiblingParachainDipStateProof< + RelayChainStateInfo::BlockNumber, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + LocalContextProvider::BlockNumber, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; fn verify_proof_for_call_against_details( call: &Call, subject: &Subject, submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, + identity_details: &mut Option, + proof: Self::Proof, ) -> Result { - let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_details( + // 1. Verify relay chain proof. + let provider_parachain_header = ParachainHeadProofVerifier::::verify_proof_for_parachain( + &SiblingProviderParachainId::get(), + &proof.para_state_root.relay_block_height, + proof.para_state_root.proof, + )?; + + // 2. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + )?; + + // 3. Verify DIP merkle proof. + let proof_leaves = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + + // 4. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( call, - subject, submitter, identity_details, - &proof.merkle_leaves, - ) - .map_err(|_| ())?; - DidSignatureVerifier::verify_proof_for_call_against_details( + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + )?; + + Ok(proof_leaves) + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct ChildParachainDipStateProof< + ParentBlockHeight: Copy + Into + TryFrom, + ParentBlockHasher: Hash, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, +> { + para_state_root: ParachainRootStateProof, + relay_header: Header, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, +} + +pub struct DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, +); + +impl< + Call, + Subject, + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainInfo: RelayChainStorageInfo + + HistoricalBlockRegistry< + BlockNumber = ::BlockNumber, + Hasher = ::Hasher, + >, + OutputOf<::Hasher>: + Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::BlockNumber: Copy + + Into + + TryFrom + + HasCompact + + Member + + sp_std::hash::Hash + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec, + RelayChainInfo::Key: AsRef<[u8]>, + + ChildProviderParachainId: Get, + + ChildProviderStateInfo: ProviderParachainStateInfo, + OutputOf: Ord + From::Hasher>>, + ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::Commitment: Decode, + ChildProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: + DidSignatureVerifierContext::BlockNumber>, + LocalContextProvider::BlockNumber: CheckedSub + From, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, +{ + type Error = (); + type IdentityDetails = LocalDidDetails; + type Proof = ChildParachainDipStateProof< + ::BlockNumber, + ::Hasher, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + // 1. Retrieve block hash from provider at the proof height + let block_hash_at_height = + RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height).ok_or(())?; + + // 1.1 Verify that the provided header hashes to the same block has retrieved + if block_hash_at_height != proof.relay_header.hash() { + return Err(()); + } + // 1.2 If so, extract the state root from the header + let state_root_at_height = proof.relay_header.state_root; + + // FIXME: Compilation error + // 2. Verify relay chain proof + let provider_parachain_header = + ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( + &ChildProviderParachainId::get(), + &state_root_at_height, + proof.para_state_root.proof, + )?; + + // 3. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + )?; + + // 4. Verify DIP merkle proof. + let proof_leaves = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + + // 5. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( call, - subject, submitter, identity_details, - // FIXME: Remove `clone()` requirement - &MerkleLeavesAndDidSignature { - merkle_leaves: merkle_proof_verification.clone(), - did_signature: proof.did_signature.clone(), + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, }, - ) - .map_err(|_| ())?; - Ok(merkle_proof_verification) + )?; + Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 01722d5555..66bebe90eb 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -18,18 +18,15 @@ use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::{traits::ConstU32, RuntimeDebug}; -use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -pub type BlindedValue = Vec; - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct MerkleProof { - pub blinded: BlindedValue, +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, Default, TypeInfo)] +pub struct DidMerkleProof { + pub blinded: BlindedValues, // TODO: Probably replace with a different data structure for better lookup capabilities pub revealed: Vec, } @@ -86,7 +83,7 @@ impl From for Web3NameMerkleKey { } } #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct Web3NameMerkleValue(BlockNumber); +pub struct Web3NameMerkleValue(pub BlockNumber); impl From for Web3NameMerkleValue { fn from(value: BlockNumber) -> Self { @@ -113,7 +110,7 @@ impl From<()> for LinkedAccountMerkleValue { } #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { +pub enum RevealedDidMerkleProofLeaf { // The key and value for the leaves of a merkle proof that contain a reference // (by ID) to the key details, provided in a separate leaf. DidKey(DidKeyMerkleKey, DidKeyMerkleValue), @@ -121,7 +118,8 @@ pub enum ProofLeaf { LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), } -impl ProofLeaf +impl + RevealedDidMerkleProofLeaf where KeyId: Encode, Web3Name: Encode, @@ -129,22 +127,23 @@ where { pub fn encoded_key(&self) -> Vec { match self { - ProofLeaf::DidKey(key, _) => key.encode(), - ProofLeaf::Web3Name(key, _) => key.encode(), - ProofLeaf::LinkedAccount(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::DidKey(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::Web3Name(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(key, _) => key.encode(), } } } -impl ProofLeaf +impl + RevealedDidMerkleProofLeaf where BlockNumber: Encode, { pub fn encoded_value(&self) -> Vec { match self { - ProofLeaf::DidKey(_, value) => value.encode(), - ProofLeaf::Web3Name(_, value) => value.encode(), - ProofLeaf::LinkedAccount(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::DidKey(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::Web3Name(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(_, value) => value.encode(), } } } @@ -163,7 +162,7 @@ pub struct RevealedWeb3Name { } #[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] -pub struct VerificationResult< +pub struct RevealedDidMerkleProofLeaves< KeyId, BlockNumber, Web3Name, @@ -183,8 +182,8 @@ impl< LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > AsRef<[RevealedDidKey]> - for VerificationResult< + > sp_std::borrow::Borrow<[RevealedDidKey]> + for RevealedDidMerkleProofLeaves< KeyId, BlockNumber, Web3Name, @@ -193,91 +192,63 @@ impl< MAX_REVEALED_ACCOUNTS_COUNT, > { - fn as_ref(&self) -> &[RevealedDidKey] { - self.did_keys.as_ref() + fn borrow(&self) -> &[RevealedDidKey] { + self.did_keys.borrow() } } /// A type that verifies a Merkle proof that reveals some leaves representing /// keys in a DID Document. /// Can also be used on its own, without any DID signature verification. -pub struct DidMerkleProofVerifier< +pub(crate) struct DidMerkleProofVerifier< Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - Hasher, - AccountId, - KeyId, - BlockNumber, - Details, - Web3Name, - LinkedAccountId, - ConstU32, - ConstU32, - )>, -); +>(#[allow(clippy::type_complexity)] PhantomData<(Hasher, KeyId, BlockNumber, Web3Name, LinkedAccountId)>); impl< - Call, - Subject, Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > IdentityProofVerifier - for DidMerkleProofVerifier< + > + DidMerkleProofVerifier< Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, > where - // TODO: Remove `Debug` bound - BlockNumber: Encode + Clone + Debug, + BlockNumber: Encode + Clone, Hasher: sp_core::Hasher, - KeyId: Encode + Clone + Ord + Into, + KeyId: Encode + Clone, LinkedAccountId: Encode + Clone, Web3Name: Encode + Clone, { - // TODO: Proper error handling - type Error = (); - type Proof = MerkleProof>, ProofLeaf>; - type IdentityDetails = IdentityDetails; - type Submitter = AccountId; - type VerificationResult = VerificationResult< - KeyId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - _call: &Call, - _subject: &Subject, - _submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { + #[allow(clippy::result_unit_err)] + pub(crate) fn verify_dip_merkle_proof( + identity_commitment: &Hasher::Out, + proof: DidMerkleProof>, RevealedDidMerkleProofLeaf>, + ) -> Result< + RevealedDidMerkleProofLeaves< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >, + (), + > { // TODO: more efficient by removing cloning and/or collecting. // Did not find another way of mapping a Vec<(Vec, Vec)> to a // Vec<(Vec, Option>)>. @@ -286,12 +257,8 @@ impl< .iter() .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>( - &identity_details.digest.clone().into(), - &proof.blinded, - &proof_leaves, - ) - .map_err(|_| ())?; + verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves) + .map_err(|_| ())?; // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. @@ -307,7 +274,7 @@ impl< BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), ), |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { - ProofLeaf::DidKey(key_id, key_value) => { + RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { keys.try_push(RevealedDidKey { // TODO: Avoid cloning if possible id: key_id.0.clone(), @@ -318,7 +285,7 @@ impl< Ok::<_, ()>((keys, web3_name, linked_accounts)) } // TODO: Avoid cloning if possible - ProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( + RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( keys, Some(RevealedWeb3Name { web3_name: revealed_web3_name.0.clone(), @@ -326,14 +293,14 @@ impl< }), linked_accounts, )), - ProofLeaf::LinkedAccount(account_id, _) => { + RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { linked_accounts.try_push(account_id.0.clone()).map_err(|_| ())?; Ok::<_, ()>((keys, web3_name, linked_accounts)) } }, )?; - Ok(VerificationResult { + Ok(RevealedDidMerkleProofLeaves { did_keys, web3_name, linked_accounts, diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs new file mode 100644 index 0000000000..0e8bf02562 --- /dev/null +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -0,0 +1,341 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use parity_scale_codec::{Decode, Encode, HasCompact}; +use sp_core::{storage::StorageKey, U256}; +use sp_runtime::generic::Header; +use sp_std::{marker::PhantomData, vec::Vec}; +use sp_trie::StorageProof; + +use crate::utils::OutputOf; + +use substrate_no_std_port::read_proof_check; + +// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 +// Needs to be replaced with its runtime-friendly version when available, or be +// kept up-to-date with upstream. +mod substrate_no_std_port { + use super::*; + + use hash_db::EMPTY_PREFIX; + use parity_scale_codec::Codec; + use sp_core::Hasher; + use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; + use sp_std::collections::btree_map::BTreeMap; + use sp_trie::{HashDBT, MemoryDB}; + + pub(super) fn read_proof_check( + root: H::Out, + proof: StorageProof, + keys: I, + ) -> Result, Option>>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = BTreeMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) + } + + fn read_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + key: &[u8], + ) -> Result>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + { + proving_backend.storage(key).map_err(|_| ()) + } + + fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> + where + H: Hasher, + H::Out: Codec, + { + let db = proof.into_memory_db(); + + if db.contains(&root, EMPTY_PREFIX) { + Ok(TrieBackendBuilder::new(db, root).build()) + } else { + Err(()) + } + } +} + +pub(super) mod relay_chain { + use super::*; + + use sp_runtime::traits::BlakeTwo256; + + use crate::traits::{RelayChainStateInfo, RelayChainStorageInfo}; + + pub struct ParachainHeadProofVerifier(PhantomData); + + // Uses the provided `root` to verify the proof. + impl ParachainHeadProofVerifier + where + RelayChainState: RelayChainStorageInfo, + OutputOf: Ord, + RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainState::Key: AsRef<[u8]>, + { + pub fn verify_proof_for_parachain_with_root( + para_id: &RelayChainState::ParaId, + root: &OutputOf<::Hasher>, + proof: impl IntoIterator>, + ) -> Result, ()> { + let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) + .map_err(|_| ())?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); + } + let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { return Err(()) }; + // TODO: Figure out why RPC call returns 2 bytes in front which we don't need + let mut unwrapped_head = &encoded_head[2..]; + Header::decode(&mut unwrapped_head).map_err(|_| ()) + } + } + + // Relies on the `RelayChainState::state_root_for_block` to retrieve the state + // root for the given block. + impl ParachainHeadProofVerifier + where + RelayChainState: RelayChainStateInfo, + OutputOf: Ord, + RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainState::Key: AsRef<[u8]>, + { + #[allow(clippy::result_unit_err)] + pub fn verify_proof_for_parachain( + para_id: &RelayChainState::ParaId, + relay_height: &RelayChainState::BlockNumber, + proof: impl IntoIterator>, + ) -> Result, ()> { + let relay_state_root = RelayChainState::state_root_for_block(relay_height).ok_or(())?; + Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) + } + } + + pub struct RococoStateRootsViaRelayStorePallet(PhantomData); + + impl RelayChainStorageInfo for RococoStateRootsViaRelayStorePallet + where + Runtime: pallet_relay_store::Config, + { + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // TODO: It's not possible to access the runtime definition from here. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } + } + + impl RelayChainStateInfo for RococoStateRootsViaRelayStorePallet + where + Runtime: pallet_relay_store::Config, + { + fn state_root_for_block(block_height: &Self::BlockNumber) -> Option> { + pallet_relay_store::Pallet::::latest_relay_head_for_block(block_height) + .map(|relay_header| relay_header.relay_parent_storage_root) + } + } + + #[cfg(test)] + mod polkadot_parachain_head_proof_verifier_tests { + use super::*; + + use hex_literal::hex; + use sp_runtime::traits::BlakeTwo256; + + // Polkadot block n: 16_363_919, + // hash 0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39 + struct StaticPolkadotInfoProvider; + + impl RelayChainStorageInfo for StaticPolkadotInfoProvider { + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // Adapted from https://github.com/polytope-labs/substrate-ismp/blob/7fb09da6c7b818a98c25c962fee0ddde8e737306/parachain/src/consensus.rs#L369 + // Used for testing. In production this would be generated from the relay + // runtime definition of the `paras` storage map. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } + } + + impl RelayChainStateInfo for StaticPolkadotInfoProvider { + fn state_root_for_block(_block_height: &Self::BlockNumber) -> Option> { + Some(hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into()) + } + } + + #[test] + fn test_spiritnet_head_proof() { + // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") + let spiritnet_head_proof_at_block = [ + hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), + hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), + hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), + hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), + hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), + ].to_vec(); + // As of query paras::heads(2_086) at block + // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" + // (16_363_919) which results in the key + // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" + // + let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); + let returned_head = ParachainHeadProofVerifier::::verify_proof_for_parachain( + &2_086, + &16_363_919, + spiritnet_head_proof_at_block, + ) + .expect("Parachain head proof verification should not fail."); + assert!(returned_head.encode() == expected_spiritnet_head_at_block, "Parachain head returned from the state proof verification should not be different than the pre-computed one."); + } + } +} + +pub(super) mod parachain { + use super::*; + + use crate::traits::ProviderParachainStateInfo; + + pub struct DipIdentityCommitmentProofVerifier(PhantomData); + + impl DipIdentityCommitmentProofVerifier + where + ParaInfo: ProviderParachainStateInfo, + OutputOf: Ord, + ParaInfo::Commitment: Decode, + ParaInfo::Key: AsRef<[u8]>, + { + #[allow(clippy::result_unit_err)] + pub fn verify_proof_for_identifier( + identifier: &ParaInfo::Identifier, + state_root: OutputOf, + proof: impl IntoIterator>, + ) -> Result { + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = read_proof_check::( + state_root, + storage_proof, + [&dip_commitment_storage_key].iter(), + ) + .map_err(|_| ())?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); + } + let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { return Err(()) }; + ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).map_err(|_| ()) + } + } + + #[cfg(test)] + mod spiritnet_test_event_count_value { + use super::*; + + use hex_literal::hex; + use sp_core::H256; + use sp_runtime::traits::BlakeTwo256; + + // Spiritnet block n: 4_184_668, + // hash 0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5 + struct StaticSpiritnetInfoProvider; + + // We use the `system::eventCount()` storage entry as a unit test here. + impl ProviderParachainStateInfo for StaticSpiritnetInfoProvider { + type BlockNumber = u32; + // The type of the `eventCount()` storage entry. + type Commitment = u32; + type Hasher = BlakeTwo256; + // Irrelevant for this test here + type Identifier = (); + type Key = StorageKey; + + fn dip_subject_storage_key(_identifier: &Self::Identifier) -> Self::Key { + // system::eventCount() raw storage key + let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); + StorageKey(storage_key) + } + } + + #[test] + fn test_spiritnet_event_count() { + // As of RPC state_getReadProof(" + // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") + let spiritnet_event_count_proof_at_block = [ + hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), + hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), + hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), + hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), + ].to_vec(); + let spiritnet_state_root: H256 = + hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); + // As of query system::eventCount() at block + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which + // results in the key + // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" + let expected_event_count_at_block = 5; + let returned_event_count = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + &(), + spiritnet_state_root, + spiritnet_event_count_proof_at_block, + ) + .unwrap(); + assert!(returned_event_count == expected_event_count_at_block, "Spiritnet event count returned from the state proof verification should not be different than the pre-computed one."); + } + } +} diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index 66417991e8..2477d82a4f 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -16,10 +16,12 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use sp_core::Get; +use sp_core::storage::StorageKey; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; +use crate::utils::OutputOf; + // TODO: Switch to the `Incrementable` trait once it's added to the root of // `frame_support`. /// A trait for "bumpable" types, i.e., types that have some notion of order of @@ -41,7 +43,7 @@ where /// A trait for types that implement some sort of access control logic on the /// provided input `Call` type. -pub trait DidDipOriginFilter { +pub trait DipCallOriginFilter { /// The error type for cases where the checks fail. type Error; /// The type of additional information required by the type to perform the @@ -53,25 +55,106 @@ pub trait DidDipOriginFilter { fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } -pub struct GenesisProvider(PhantomData); +pub trait RelayChainStorageInfo { + type BlockNumber; + type Hasher: sp_runtime::traits::Hash; + type Key; + type ParaId; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key; +} + +pub trait RelayChainStateInfo: RelayChainStorageInfo { + fn state_root_for_block(block_height: &Self::BlockNumber) -> Option>; +} + +pub trait ProviderParachainStateInfo { + type BlockNumber; + type Commitment; + type Key; + type Hasher: sp_runtime::traits::Hash; + type Identifier; + + fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key; +} + +pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); -impl Get for GenesisProvider +impl ProviderParachainStateInfo for ProviderParachainStateInfoViaProviderPallet +where + T: pallet_dip_provider::Config, +{ + type BlockNumber = T::BlockNumber; + type Commitment = T::IdentityCommitment; + type Hasher = T::Hashing; + type Identifier = T::Identifier; + type Key = StorageKey; + + fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key { + StorageKey(pallet_dip_provider::IdentityCommitments::::hashed_key_for( + identifier, + )) + } +} + +pub trait DidSignatureVerifierContext { + const SIGNATURE_VALIDITY: u16; + + type BlockNumber; + type Hash; + type SignedExtra; + + fn block_number() -> Self::BlockNumber; + fn genesis_hash() -> Self::Hash; + fn signed_extra() -> Self::SignedExtra; +} + +pub struct FrameSystemDidSignatureContext(PhantomData); + +impl DidSignatureVerifierContext + for FrameSystemDidSignatureContext where T: frame_system::Config, - T::BlockNumber: Zero, { - fn get() -> T::Hash { + const SIGNATURE_VALIDITY: u16 = SIGNATURE_VALIDITY; + + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type SignedExtra = (); + + fn block_number() -> Self::BlockNumber { + frame_system::Pallet::::block_number() + } + + fn genesis_hash() -> Self::Hash { frame_system::Pallet::::block_hash(T::BlockNumber::zero()) } + + fn signed_extra() -> Self::SignedExtra {} } -pub struct BlockNumberProvider(PhantomData); +pub trait HistoricalBlockRegistry { + type BlockNumber; + type Hasher: sp_runtime::traits::Hash; + + fn block_hash_for(block: &Self::BlockNumber) -> Option>; +} -impl Get for BlockNumberProvider +impl HistoricalBlockRegistry for T where T: frame_system::Config, { - fn get() -> T::BlockNumber { - frame_system::Pallet::::block_number() + type BlockNumber = T::BlockNumber; + type Hasher = T::Hashing; + + fn block_hash_for(block: &Self::BlockNumber) -> Option> { + let retrieved_block = frame_system::Pallet::::block_hash(block); + let default_block_hash_value = ::default(); + + if retrieved_block == default_block_hash_value { + None + } else { + Some(retrieved_block) + } } } diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs new file mode 100644 index 0000000000..85fa1334f9 --- /dev/null +++ b/crates/kilt-dip-support/src/utils.rs @@ -0,0 +1,114 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use pallet_dip_provider::traits::IdentityProvider; +use sp_std::marker::PhantomData; + +pub struct CombinedIdentityResult { + pub a: OutputA, + pub b: OutputB, + pub c: OutputC, +} + +impl From<(OutputA, OutputB, OutputC)> + for CombinedIdentityResult +{ + fn from(value: (OutputA, OutputB, OutputC)) -> Self { + Self { + a: value.0, + b: value.1, + c: value.2, + } + } +} + +impl CombinedIdentityResult +where + OutputB: Default, + OutputC: Default, +{ + pub fn from_a(a: OutputA) -> Self { + Self { + a, + b: OutputB::default(), + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputC: Default, +{ + pub fn from_b(b: OutputB) -> Self { + Self { + a: OutputA::default(), + b, + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputB: Default, +{ + pub fn from_c(c: OutputC) -> Self { + Self { + a: OutputA::default(), + b: OutputB::default(), + c, + } + } +} + +pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); + +impl IdentityProvider for CombineIdentityFrom +where + A: IdentityProvider, + B: IdentityProvider, + C: IdentityProvider, +{ + // TODO: Proper error handling + type Error = (); + type Success = CombinedIdentityResult, Option, Option>; + + fn retrieve(identifier: &Identifier) -> Result, Self::Error> { + match ( + A::retrieve(identifier), + B::retrieve(identifier), + C::retrieve(identifier), + ) { + // If no details is returned, return None for the whole result + (Ok(None), Ok(None), Ok(None)) => Ok(None), + // Otherwise, return `Some` or `None` depending on each result + (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { + a: ok_a, + b: ok_b, + c: ok_c, + })), + // If any of them returns an `Err`, return an `Err` + _ => Err(()), + } + } +} + +pub type OutputOf = ::Output; diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs deleted file mode 100644 index 2f457d7e7b..0000000000 --- a/crates/kilt-dip-support/src/xcm.rs +++ /dev/null @@ -1,289 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use dip_support::IdentityDetailsAction; -use pallet_dip_provider::traits::{IdentityProofDispatcher, TxBuilder}; -use parity_scale_codec::Encode; -use sp_core::Get; -use sp_std::marker::PhantomData; -use xcm::v3::{ - Instruction::{BuyExecution, DepositAsset, DescendOrigin, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, - InteriorMultiLocation, - Junction::AccountId32, - Junctions::{Here, X1}, - MultiAsset, - MultiAssetFilter::Wild, - MultiAssets, MultiLocation, OriginKind, SendError, SendXcm, Weight, - WeightLimit::Limited, - WildMultiAsset::All, - Xcm, -}; - -// Dispatcher using a type implementing the `SendXcm` trait. -// It properly encodes the `Transact` operation, then delegates everything else -// to the sender, similarly to what the XCM pallet's `send` extrinsic does. -pub struct XcmRouterIdentityDispatcher( - PhantomData<(Router, UniversalLocationProvider)>, -); - -impl - IdentityProofDispatcher - for XcmRouterIdentityDispatcher -where - Router: SendXcm, - UniversalLocationProvider: Get, - Identifier: Encode, - ProofOutput: Encode, - AccountId: Into<[u8; 32]> + Clone, -{ - type PreDispatchOutput = Router::Ticket; - type Error = SendError; - - fn pre_dispatch>( - action: IdentityDetailsAction, - source: AccountId, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { - // TODO: Replace with proper error handling - let dest_tx = Builder::build(destination, action) - .map_err(|_| ()) - .expect("Failed to build call"); - - // TODO: Set an error handler and an appendix to refund any leftover funds to - // the provider parachain sovereign account. - let operation = [[ - ExpectOrigin(Some( - Here.into_location() - .reanchored(&destination, UniversalLocationProvider::get()) - .unwrap(), - )), - DescendOrigin(X1(AccountId32 { - network: None, - id: source.clone().into(), - })), - WithdrawAsset(asset.clone().into()), - BuyExecution { - fees: asset, - weight_limit: Limited(weight), - }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: weight, - call: dest_tx, - }, - RefundSurplus, - DepositAsset { - assets: Wild(All), - beneficiary: MultiLocation { - parents: 1, - // Re-anchor the same account junction as seen from the destination. - // TODO: Error handling - interior: Here - .into_location() - .reanchored(&destination, UniversalLocationProvider::get()) - .unwrap() - .pushed_with_interior(AccountId32 { - network: None, - id: source.into(), - }) - .unwrap() - .interior, - }, - }, - ]] - .concat(); - // TODO: Restructure the trait to be able to inject the [Instruction] provider, - // and unit test that. - debug_assert!(barriers::check_expected_dip_instruction_order(&operation).is_ok()); - let op = Xcm(operation); - Router::validate(&mut Some(destination), &mut Some(op)) - } - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Router::deliver(pre_output).map(|_| ()) - } -} - -pub mod barriers { - use super::*; - - use frame_support::{ensure, traits::ProcessMessageError}; - use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; - use xcm_executor::traits::ShouldExecute; - - // Must match the order of instructions as produced by the provider's - // implementation of the `IdentityProofDispatcher` trait. - pub(crate) fn check_expected_dip_instruction_order( - instructions: &[Instruction], - ) -> Result<(), ()> { - let mut iter = instructions.iter(); - match ( - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - ) { - ( - // A first instruction different than `DescendOrigin` is needed to distinguish between user-triggered - // and parachain-triggered XCM messages, since also the XCM pallet always preprends user-created XCM - // messages with a `DescendOrigin` instruction. - Some(ExpectOrigin(..)), - // Go down to user level to charge them for the XCM fees. - Some(DescendOrigin(X1(AccountId32 { .. }))), - // Expect the user to first withdraw an asset to pay for the fees. - Some(WithdrawAsset { .. }), - // Buy execution time. - Some(BuyExecution { .. }), - // Although this is irrelevant since `origin_kind` can also be specified by a user, we use - // `OriginKind::Native` here to make clear this is a parachain-dispatched XCM message. - Some(Transact { - origin_kind: OriginKind::Native, - .. - }), - // Any unused weight is refunded. - Some(RefundSurplus), - // Any unused assets are refunded back into the user's account. - Some(DepositAsset { .. }), - // No more instructions are allowed. - None, - ) => Ok(()), - _ => Err(()), - } - } - - // Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees - // from their balance, and then carry on with a `Transact`. - // Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` - // origin converter. - pub struct AllowParachainProviderAsSubaccount(PhantomData); - - impl ShouldExecute for AllowParachainProviderAsSubaccount - where - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - _max_weight: Weight, - _weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - #[cfg(feature = "std")] - println!( - "AllowParachainProviderAsSubaccount::should_execute(origin = {:?}, instructions = {:?}", - origin, instructions - ); - // Ensure that the origin is a parachain allowed to act as identity provider. - ensure!( - *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), - ProcessMessageError::Yield - ); - check_expected_dip_instruction_order(instructions).map_err(|_| ProcessMessageError::Yield) - } - } - - // Decorate an existing barrier to add one more check in case all the previous - // barriers fail. - pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - - impl ShouldExecute for OkOrElseCheckForParachainProvider - where - Barrier: ShouldExecute, - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - }) - } - } - - // Decorate an existing barrier to check for the provider parachain origin only - // in case none of the previous barriers fail. - pub struct ErrOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - - impl ShouldExecute for ErrOrElseCheckForParachainProvider - where - Barrier: ShouldExecute, - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - } - } -} - -pub mod origins { - use super::*; - - use xcm::v3::{Junction::Parachain, Junctions::X2}; - use xcm_executor::traits::ConvertOrigin; - - pub struct AccountIdJunctionAsParachain( - PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, - ); - - impl ConvertOrigin - for AccountIdJunctionAsParachain - where - ProviderParaId: Get, - ParachainOrigin: From, - RuntimeOrigin: From, - { - fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { - let origin = origin.into(); - let provider_para_id = ProviderParaId::get(); - match (kind, origin) { - ( - OriginKind::Native, - MultiLocation { - parents: 1, - interior: X2(Parachain(para_id), AccountId32 { .. }), - }, - ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), - _ => Err(origin), - } - } - } -} diff --git a/dip-template/nodes/dip-consumer/src/chain_spec.rs b/dip-template/nodes/dip-consumer/src/chain_spec.rs index 4330bf5c62..bfc123f067 100644 --- a/dip-template/nodes/dip-consumer/src/chain_spec.rs +++ b/dip-template/nodes/dip-consumer/src/chain_spec.rs @@ -99,8 +99,6 @@ fn testnet_genesis( }, aura: Default::default(), aura_ext: Default::default(), - polkadot_xcm: Default::default(), - did_lookup: Default::default(), } } diff --git a/dip-template/nodes/dip-provider/src/chain_spec.rs b/dip-template/nodes/dip-provider/src/chain_spec.rs index af6493f67c..6e162c624e 100644 --- a/dip-template/nodes/dip-provider/src/chain_spec.rs +++ b/dip-template/nodes/dip-provider/src/chain_spec.rs @@ -99,7 +99,6 @@ fn testnet_genesis( }, aura: Default::default(), aura_ext: Default::default(), - polkadot_xcm: Default::default(), did_lookup: Default::default(), } } diff --git a/crates/dip-support/Cargo.toml b/dip-template/pallets/pallet-postit/Cargo.toml similarity index 50% rename from crates/dip-support/Cargo.toml rename to dip-template/pallets/pallet-postit/Cargo.toml index 276b3929ac..dbfc82136b 100644 --- a/crates/dip-support/Cargo.toml +++ b/dip-template/pallets/pallet-postit/Cargo.toml @@ -1,27 +1,40 @@ [package] authors.workspace = true -description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "dip-support" readme.workspace = true repository.workspace = true version.workspace = true +name = "pallet-postit" +description = "Simple pallet to store on-chain comments, replies, and likes." + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] [dependencies] -# Parity dependencies +# External dependencies parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -# Substrate dependencies +#External dependencies frame-support.workspace = true +frame-system.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true [features] default = ["std"] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] std = [ "parity-scale-codec/std", "scale-info/std", - "frame-support/std" + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", ] diff --git a/dip-template/pallets/pallet-postit/src/lib.rs b/dip-template/pallets/pallet-postit/src/lib.rs new file mode 100644 index 0000000000..170797f61e --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/lib.rs @@ -0,0 +1,185 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod post; +pub mod traits; + +pub use pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + + use super::*; + + use frame_support::{ + pallet_prelude::{DispatchResult, *}, + traits::EnsureOrigin, + BoundedVec, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::Hash; + use sp_std::fmt::Debug; + + use crate::{ + post::{Comment, Post}, + traits::Usernamable, + }; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + pub type BoundedTextOf = BoundedVec::MaxTextLength>; + pub type PostOf = Post<::Hash, BoundedTextOf, ::Username>; + pub type CommentOf = Comment<::Hash, BoundedTextOf, ::Username>; + + #[pallet::config] + pub trait Config: frame_system::Config { + type MaxTextLength: Get; + type OriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; + type OriginSuccess: Usernamable; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Username: Encode + Decode + TypeInfo + MaxEncodedLen + Clone + PartialEq + Debug + Default; + } + + #[pallet::storage] + #[pallet::getter(fn posts)] + pub type Posts = StorageMap<_, Twox64Concat, ::Hash, PostOf>; + + #[pallet::storage] + #[pallet::getter(fn comments)] + pub type Comments = StorageMap<_, Twox64Concat, ::Hash, CommentOf>; + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewPost { + post_id: T::Hash, + author: T::Username, + }, + NewComment { + resource_id: T::Hash, + comment_id: T::Hash, + author: T::Username, + }, + NewLike { + resource_id: T::Hash, + liker: T::Username, + }, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(1_000)] + pub fn post(origin: OriginFor, text: BoundedTextOf) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let author = success_origin.username().map_err(DispatchError::Other)?; + let post_id = T::Hashing::hash( + (&frame_system::Pallet::::block_number(), &author, &text) + .encode() + .as_slice(), + ); + let post = PostOf::::from_text_and_author(text, author.clone()); + Posts::::insert(post_id, post); + Self::deposit_event(Event::NewPost { post_id, author }); + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(1_000)] + pub fn comment(origin: OriginFor, resource_id: T::Hash, text: BoundedTextOf) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let author = success_origin.username().map_err(DispatchError::Other)?; + let comment_id = T::Hashing::hash( + (&frame_system::Pallet::::block_number(), &author, &text) + .encode() + .as_slice(), + ); + Posts::::try_mutate(resource_id, |post| { + if let Some(post) = post { + post.comments + .try_push(comment_id) + .expect("Failed to add comment to post."); + Ok(()) + } else { + Err(()) + } + }) + .or_else(|_| { + Comments::::try_mutate(resource_id, |comment| { + if let Some(comment) = comment { + comment + .details + .comments + .try_push(comment_id) + .expect("Failed to add comment to comment."); + Ok(()) + } else { + Err(()) + } + }) + }) + .map_err(|_| DispatchError::Other("No post or comment with provided ID found."))?; + let comment = CommentOf::::from_post_id_text_and_author(resource_id, text, author.clone()); + Comments::::insert(comment_id, comment); + Self::deposit_event(Event::NewComment { + resource_id, + comment_id, + author, + }); + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(1_000)] + pub fn like(origin: OriginFor, resource_id: T::Hash) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let liker = success_origin.username().map_err(DispatchError::Other)?; + Posts::::try_mutate(resource_id, |post| { + if let Some(post) = post { + post.likes.try_push(liker.clone()).expect("Failed to add like to post."); + Ok(()) + } else { + Err(()) + } + }) + .or_else(|_| { + Comments::::try_mutate(resource_id, |comment| { + if let Some(comment) = comment { + comment + .details + .likes + .try_push(liker.clone()) + .expect("Failed to add like to comment."); + Ok(()) + } else { + Err(()) + } + }) + }) + .map_err(|_| DispatchError::Other("No post or comment with provided ID found."))?; + Self::deposit_event(Event::NewLike { resource_id, liker }); + Ok(()) + } + } +} diff --git a/dip-template/pallets/pallet-postit/src/post.rs b/dip-template/pallets/pallet-postit/src/post.rs new file mode 100644 index 0000000000..49fd158fc9 --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/post.rs @@ -0,0 +1,55 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{traits::ConstU32, BoundedVec}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Default)] +pub struct Post { + pub author: Username, + pub text: Text, + pub likes: BoundedVec>, + pub comments: BoundedVec>, +} + +impl Post { + pub(crate) fn from_text_and_author(text: Text, author: Username) -> Self { + Self { + text, + author, + likes: BoundedVec::default(), + comments: BoundedVec::default(), + } + } +} + +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Default)] +pub struct Comment { + pub details: Post, + pub in_response_to: Id, +} + +impl Comment { + pub(crate) fn from_post_id_text_and_author(in_response_to: Id, text: Text, author: Username) -> Self { + Self { + in_response_to, + details: Post::from_text_and_author(text, author), + } + } +} diff --git a/dip-template/pallets/pallet-postit/src/traits.rs b/dip-template/pallets/pallet-postit/src/traits.rs new file mode 100644 index 0000000000..8a1d20f580 --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/traits.rs @@ -0,0 +1,23 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +pub trait Usernamable { + type Username; + + fn username(&self) -> Result; +} diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index b5a18965ee..aae167b098 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -19,11 +19,12 @@ scale-info = {workspace = true, features = ["derive"]} # DIP dip-provider-runtime-template.workspace = true -dip-support.workspace = true did.workspace = true kilt-dip-support.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true +pallet-postit.workspace = true +pallet-relay-store.workspace = true runtime-common.workspace = true # Substrate @@ -53,18 +54,10 @@ sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true -# Polkadot -pallet-xcm.workspace = true -xcm.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true - # Cumulus cumulus-pallet-aura-ext.workspace = true cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true -cumulus-pallet-xcm.workspace = true -cumulus-pallet-xcmp-queue.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true cumulus-primitives-utility.workspace = true @@ -79,11 +72,12 @@ std = [ "parity-scale-codec/std", "scale-info/std", "dip-provider-runtime-template/std", - "dip-support/std", "did/std", "kilt-dip-support/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", + "pallet-postit/std", + "pallet-relay-store/std", "runtime-common/std", "frame-executive/std", "frame-support/std", @@ -110,15 +104,9 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", - "pallet-xcm/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", @@ -128,10 +116,9 @@ std = [ runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index b629fe5820..11b355dd7f 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,54 +17,45 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; -use dip_provider_runtime_template::Web3Name; +use dip_provider_runtime_template::{Runtime as ProviderRuntime, Web3Name}; use frame_support::traits::Contains; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleLeavesAndDidSignature, MerkleRevealedDidSignatureVerifier}, - merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, - traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, - MerkleProofAndDidSignatureVerifier, + traits::{DipCallOriginFilter, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}, + DipSiblingProviderStateProofVerifier, RococoStateRootsViaRelayStorePallet, }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_std::vec::Vec; +use sp_core::ConstU32; +use sp_runtime::traits::BlakeTwo256; -use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; -pub type MerkleProofVerifier = - DidMerkleProofVerifier, BlockNumber, u128, Web3Name, LinkableAccountId, 10, 10>; pub type MerkleProofVerifierOutputOf = - >::VerificationResult; -pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< - KeyIdOf, - BlockNumber, - Hash, - u128, + >::VerificationResult; +pub type ProofVerifier = DipSiblingProviderStateProofVerifier< + RococoStateRootsViaRelayStorePallet, + ConstU32<2_000>, + ProviderParachainStateInfoViaProviderPallet, AccountId, - MerkleProofVerifierOutputOf, - BlockNumberProvider, + BlakeTwo256, + KeyIdOf, + Web3Name, + LinkableAccountId, + 10, + 10, + u128, // Signatures are valid for 50 blocks - 50, - GenesisProvider, - Hash, + FrameSystemDidSignatureContext, + DipCallFilter, >; impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; - type IdentityDetails = u128; - type Proof = MerkleLeavesAndDidSignature< - MerkleProof>, ProofLeaf>, - BlockNumber, - >; - type ProofDigest = Hash; - type ProofVerifier = MerkleProofAndDidSignatureVerifier< - BlockNumber, - MerkleProofVerifier, - DidSignatureAndCallVerifier, DipCallFilter>, - >; + type IdentityProof = >::Proof; + type LocalIdentityInfo = u128; + type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; } @@ -74,7 +65,7 @@ impl Contains for PreliminaryDipOriginFilter { fn contains(t: &RuntimeCall) -> bool { matches!( t, - RuntimeCall::DidLookup { .. } + RuntimeCall::PostIt { .. } | RuntimeCall::Utility(pallet_utility::Call::batch { .. }) | RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) | RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) @@ -84,7 +75,7 @@ impl Contains for PreliminaryDipOriginFilter { fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { - RuntimeCall::DidLookup { .. } => Some(DidVerificationKeyRelationship::Authentication), + RuntimeCall::PostIt { .. } => Some(DidVerificationKeyRelationship::Authentication), RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls.iter()).ok(), @@ -116,7 +107,7 @@ fn single_key_relationship<'a>( pub struct DipCallFilter; -impl DidDipOriginFilter for DipCallFilter { +impl DipCallOriginFilter for DipCallFilter { type Error = (); type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); type Success = (); @@ -132,43 +123,6 @@ impl DidDipOriginFilter for DipCallFilter { } } -#[cfg(test)] -mod dip_call_origin_filter_tests { - use super::*; - - use frame_support::assert_err; - - #[test] - fn test_key_relationship_derivation() { - // Can call DidLookup functions with an authentication key - let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); - assert_eq!( - single_key_relationship(vec![did_lookup_call].iter()), - Ok(DidVerificationKeyRelationship::Authentication) - ); - // Can't call System functions with a DID key (hence a DIP origin) - let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(vec![system_call].iter()), ()); - // Can't call empty batch with a DID key - let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(vec![empty_batch_call].iter()), ()); - // Can call batch with a DipLookup with an authentication key - let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { - calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], - }); - assert_eq!( - single_key_relationship(vec![did_lookup_batch_call].iter()), - Ok(DidVerificationKeyRelationship::Authentication) - ); - // Can't call a batch with different required keys - let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { - calls: vec![ - // Authentication key - pallet_did_lookup::Call::associate_sender {}.into(), - // No key - frame_system::Call::remark { remark: vec![] }.into(), - ], - }); - assert_err!(single_key_relationship(vec![did_lookup_batch_call].iter()), ()); - } +impl pallet_relay_store::Config for Runtime { + type MaxRelayBlocksStored = ConstU32<100>; } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index b68617f36a..5a0c38bbd7 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,10 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use did::KeyIdOf; use dip_provider_runtime_template::Web3Name; -use kilt_dip_support::merkle::VerificationResult; -use pallet_did_lookup::linkable_account::LinkableAccountId; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -51,7 +48,6 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; -use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; @@ -68,8 +64,8 @@ use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; mod dip; -mod xcm_config; -pub use crate::{dip::*, xcm_config::*}; +mod origin_adapter; +pub use crate::{dip::*, origin_adapter::*}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -136,17 +132,12 @@ construct_runtime!( Aura: pallet_aura = 23, AuraExt: cumulus_pallet_aura_ext = 24, - // XCM - XcmpQueue: cumulus_pallet_xcmp_queue = 30, - DmpQueue: cumulus_pallet_dmp_queue = 31, - PolkadotXcm: pallet_xcm = 32, - CumulusXcm: cumulus_pallet_xcm = 33, - - // DID lookup - DidLookup: pallet_did_lookup = 40, + // PostIt + PostIt: pallet_postit = 30, // DIP - DipConsumer: pallet_dip_consumer = 50, + DipConsumer: pallet_dip_consumer = 40, + RelayStore: pallet_relay_store = 41, } ); @@ -251,21 +242,16 @@ impl frame_system::Config for Runtime { type Version = Version; } -parameter_types! { - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); - pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); -} - impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type DmpMessageHandler = DmpQueue; + type DmpMessageHandler = (); type OnSystemEvent = (); - type OutboundXcmpMessageSource = XcmpQueue; - type ReservedDmpWeight = ReservedDmpWeight; - type ReservedXcmpWeight = ReservedXcmpWeight; + type OutboundXcmpMessageSource = (); + type ReservedDmpWeight = (); + type ReservedXcmpWeight = (); type RuntimeEvent = RuntimeEvent; type SelfParaId = ParachainInfo; - type XcmpMessageHandler = XcmpQueue; + type XcmpMessageHandler = (); } impl pallet_timestamp::Config for Runtime { @@ -367,27 +353,12 @@ impl pallet_aura::Config for Runtime { impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - pub const LinkDeposit: Balance = UNIT; -} - -impl pallet_did_lookup::Config for Runtime { - type Currency = Balances; - type Deposit = ConstU128; - type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin< - DidIdentifier, - AccountId, - VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, - >; - type OriginSuccess = DipOrigin< - DidIdentifier, - AccountId, - VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, - >; +impl pallet_postit::Config for Runtime { + type MaxTextLength = ConstU32<160>; + type OriginCheck = EnsureDipOriginAdapter; + type OriginSuccess = DipOriginAdapter; type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; - type WeightInfo = (); + type Username = Web3Name; } impl_runtime_apis! { diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs new file mode 100644 index 0000000000..b9d008e5c8 --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -0,0 +1,59 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutputOf, RuntimeCall, RuntimeOrigin, Web3Name}; +use frame_support::traits::EnsureOrigin; +use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; +use pallet_postit::traits::Usernamable; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +pub struct EnsureDipOriginAdapter; + +impl EnsureOrigin for EnsureDipOriginAdapter { + type Success = DipOriginAdapter; + + fn try_origin(o: RuntimeOrigin) -> Result { + EnsureDipOrigin::try_origin(o).map(DipOriginAdapter) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + // TODO: Replace with actual DIP origin upon benchmarking + Ok(RuntimeOrigin::root()) + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct DipOriginAdapter( + DipOrigin>, +); + +impl Usernamable for DipOriginAdapter { + type Username = Web3Name; + + fn username(&self) -> Result { + self.0 + .details + .web3_name + .as_ref() + .map(|leaf| leaf.web3_name.clone()) + .ok_or("No username for the subject.") + } +} diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs deleted file mode 100644 index 24443a088e..0000000000 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ /dev/null @@ -1,153 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use cumulus_primitives_utility::ParentAsUmp; -use frame_support::{ - parameter_types, - traits::{ConstU32, Contains, Nothing}, - weights::{IdentityFee, Weight}, -}; -use frame_system::EnsureRoot; -use kilt_dip_support::xcm::{barriers::OkOrElseCheckForParachainProvider, origins::AccountIdJunctionAsParachain}; -use pallet_xcm::TestWeightInfo; -use xcm::v3::prelude::*; -use xcm_builder::{ - Account32Hash, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - SignedToAccountId32, UsingComponents, -}; -use xcm_executor::XcmExecutor; - -use crate::{ - AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, XcmpQueue, -}; - -parameter_types! { - pub HereLocation: MultiLocation = MultiLocation::here(); - pub NoneNetworkId: Option = None; - pub UnitWeightCost: Weight = Weight::from_parts(1_000, 0); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -pub type Barrier = OkOrElseCheckForParachainProvider, ConstU32<2_000>>; -pub type AssetTransactorLocationConverter = Account32Hash; -pub type LocalAssetTransactor = - CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; -pub type XcmRouter = (ParentAsUmp, XcmpQueue); - -pub struct DipTransactSafeCalls; - -impl Contains for DipTransactSafeCalls { - fn contains(t: &RuntimeCall) -> bool { - matches!( - t, - RuntimeCall::DipConsumer(pallet_dip_consumer::Call::process_identity_action { .. }) - ) - } -} - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type AssetClaims = (); - type AssetExchanger = (); - type AssetLocker = (); - type AssetTransactor = LocalAssetTransactor; - type AssetTrap = (); - type Barrier = Barrier; - type CallDispatcher = RuntimeCall; - type FeeManager = (); - type IsReserve = (); - type IsTeleporter = (); - type MaxAssetsIntoHolding = ConstU32<64>; - type MessageExporter = (); - type OriginConverter = AccountIdJunctionAsParachain, cumulus_pallet_xcm::Origin, RuntimeOrigin>; - type PalletInstancesInfo = AllPalletsWithSystem; - type ResponseHandler = (); - type RuntimeCall = RuntimeCall; - type SafeCallFilter = DipTransactSafeCalls; - type SubscriptionService = (); - type UniversalAliases = Nothing; - type UniversalLocation = UniversalLocation; - type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; - type Weigher = FixedWeightBounds>; - type XcmSender = XcmRouter; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type ChannelInfo = ParachainSystem; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = (); - type ExecuteOverweightOrigin = EnsureRoot; - type PriceForSiblingDelivery = (); - type RuntimeEvent = RuntimeEvent; - type VersionWrapper = (); - type WeightInfo = (); - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type ExecuteOverweightOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -const MAX_INSTRUCTIONS: u32 = 100; - -parameter_types! { - pub RelayNetwork: Option = None; -} -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - pub ReachableDest: Option = Some(Parent.into()); -} - -pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - - type AdminOrigin = EnsureRoot; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type ExecuteXcmOrigin = EnsureXcmOrigin; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<8>; - type RemoteLockConsumerIdentifier = [u8; 8]; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SendXcmOrigin = EnsureXcmOrigin; - type SovereignAccountOf = (); - type TrustedLockers = (); - type UniversalLocation = UniversalLocation; - type Weigher = FixedWeightBounds>; - type WeightInfo = TestWeightInfo; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmReserveTransferFilter = Nothing; - type XcmRouter = XcmRouter; - type XcmTeleportFilter = Nothing; - #[cfg(feature = "runtime-benchmarks")] - type ReachableDest = ReachableDest; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 14fdeb6257..dff25abda3 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -19,7 +19,6 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true -dip-support.workspace = true kilt-dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-did-lookup.workspace = true @@ -52,18 +51,10 @@ sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true -# Polkadot -pallet-xcm.workspace = true -xcm.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true - # Cumulus cumulus-pallet-aura-ext.workspace = true cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true -cumulus-pallet-xcm.workspace = true -cumulus-pallet-xcmp-queue.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true cumulus-primitives-utility.workspace = true @@ -78,7 +69,6 @@ std = [ "parity-scale-codec/std", "scale-info/std", "did/std", - "dip-support/std", "kilt-dip-support/std", "kilt-runtime-api-dip-provider/std", "pallet-did-lookup/std", @@ -108,15 +98,9 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", - "pallet-xcm/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", @@ -131,7 +115,5 @@ runtime-benchmarks = [ "pallet-web3-names/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "xcm-builder/runtime-benchmarks" + "frame-support/runtime-benchmarks" ] diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 59b6aedade..108bcbd7f2 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,51 +17,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{DidRawOrigin, EnsureDidOrigin}; -use dip_support::IdentityDetailsAction; -use kilt_dip_support::xcm::XcmRouterIdentityDispatcher; -use pallet_dip_provider::traits::TxBuilder; -use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; -use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, UniversalLocation, XcmRouter}; - -#[derive(Encode, Decode)] -enum ConsumerParachainCalls { - #[codec(index = 50)] - DipConsumer(ConsumerParachainDipConsumerCalls), -} - -#[derive(Encode, Decode)] -enum ConsumerParachainDipConsumerCalls { - #[codec(index = 0)] - ProcessIdentityAction(IdentityDetailsAction), -} - -pub struct ConsumerParachainTxBuilder; -impl TxBuilder for ConsumerParachainTxBuilder { - type Error = (); - - fn build( - _dest: MultiLocation, - action: IdentityDetailsAction, - ) -> Result, Self::Error> { - let double_encoded: DoubleEncoded<()> = - ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) - .encode() - .into(); - Ok(double_encoded) - } -} +use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent}; impl pallet_dip_provider::Config for Runtime { type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; - type IdentityProofDispatcher = XcmRouterIdentityDispatcher; - type IdentityProofGenerator = DidMerkleRootGenerator; + type IdentityCommitment = Hash; + type IdentityCommitmentGenerator = DidMerkleRootGenerator; type IdentityProvider = LinkedDidInfoProviderOf; - type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; - type TxBuilder = ConsumerParachainTxBuilder; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 989dda86f2..f8b97a98ec 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -76,8 +76,7 @@ pub use sp_runtime::BuildStorage; use sp_version::NativeVersion; mod dip; -mod xcm_config; -pub use crate::{dip::*, xcm_config::*}; +pub use crate::dip::*; pub type AccountId = AccountId32; pub type Address = MultiAddress; @@ -136,19 +135,13 @@ construct_runtime!( Aura: pallet_aura = 23, AuraExt: cumulus_pallet_aura_ext = 24, - // XCM - XcmpQueue: cumulus_pallet_xcmp_queue = 30, - DmpQueue: cumulus_pallet_dmp_queue = 31, - PolkadotXcm: pallet_xcm = 32, - CumulusXcm: cumulus_pallet_xcm = 33, - // DID - Did: did = 40, - DidLookup: pallet_did_lookup = 41, - Web3Names: pallet_web3_names = 42, + Did: did = 30, + DidLookup: pallet_did_lookup = 31, + Web3Names: pallet_web3_names = 32, // DIP - DipProvider: pallet_dip_provider = 50, + DipProvider: pallet_dip_provider = 40, } ); @@ -195,7 +188,8 @@ register_validate_block! { CheckInherents = CheckInherents, } -pub const SS58_PREFIX: u16 = 100; +// Same as official KILT prefix. +pub const SS58_PREFIX: u16 = 38; const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( @@ -252,21 +246,16 @@ impl frame_system::Config for Runtime { type Version = Version; } -parameter_types! { - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); - pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); -} - impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type DmpMessageHandler = DmpQueue; + type DmpMessageHandler = (); type OnSystemEvent = (); - type OutboundXcmpMessageSource = XcmpQueue; - type ReservedDmpWeight = ReservedDmpWeight; - type ReservedXcmpWeight = ReservedXcmpWeight; + type OutboundXcmpMessageSource = (); + type ReservedDmpWeight = (); + type ReservedXcmpWeight = (); type RuntimeEvent = RuntimeEvent; type SelfParaId = ParachainInfo; - type XcmpMessageHandler = XcmpQueue; + type XcmpMessageHandler = (); } impl pallet_timestamp::Config for Runtime { diff --git a/dip-template/runtimes/dip-provider/src/xcm_config.rs b/dip-template/runtimes/dip-provider/src/xcm_config.rs deleted file mode 100644 index 41bcc47fc4..0000000000 --- a/dip-template/runtimes/dip-provider/src/xcm_config.rs +++ /dev/null @@ -1,133 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use cumulus_primitives_utility::ParentAsUmp; -use frame_support::{ - parameter_types, - traits::{ConstU32, Nothing}, - weights::{IdentityFee, Weight}, -}; -use frame_system::EnsureRoot; -use pallet_xcm::TestWeightInfo; -use xcm::v3::prelude::*; -use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; -use xcm_executor::XcmExecutor; - -use crate::{ - AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, XcmpQueue, -}; - -parameter_types! { - pub HereLocation: MultiLocation = Junctions::Here.into(); - pub UnitWeightCost: Weight = Weight::from_parts(1_000, 0); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -pub type XcmRouter = (ParentAsUmp, XcmpQueue); - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type AssetClaims = (); - type AssetExchanger = (); - type AssetLocker = (); - type AssetTransactor = (); - type AssetTrap = (); - type Barrier = (); - type CallDispatcher = RuntimeCall; - type FeeManager = (); - type IsReserve = (); - type IsTeleporter = (); - type MaxAssetsIntoHolding = ConstU32<64>; - type MessageExporter = (); - type OriginConverter = (); - type PalletInstancesInfo = AllPalletsWithSystem; - type ResponseHandler = (); - type RuntimeCall = RuntimeCall; - type SafeCallFilter = Nothing; - type SubscriptionService = (); - type UniversalAliases = Nothing; - type UniversalLocation = UniversalLocation; - type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; - type Weigher = FixedWeightBounds>; - type XcmSender = XcmRouter; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type ChannelInfo = ParachainSystem; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = (); - type ExecuteOverweightOrigin = EnsureRoot; - type PriceForSiblingDelivery = (); - type RuntimeEvent = RuntimeEvent; - type VersionWrapper = (); - type WeightInfo = (); - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type ExecuteOverweightOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -const MAX_INSTRUCTIONS: u32 = 100; - -parameter_types! { - pub RelayNetwork: Option = None; -} -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - pub ReachableDest: Option = Some(Parent.into()); -} - -pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - - type AdminOrigin = EnsureRoot; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type ExecuteXcmOrigin = EnsureXcmOrigin; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<8>; - type RemoteLockConsumerIdentifier = [u8; 8]; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SendXcmOrigin = EnsureXcmOrigin; - type SovereignAccountOf = (); - type TrustedLockers = (); - type UniversalLocation = UniversalLocation; - type Weigher = FixedWeightBounds>; - type WeightInfo = TestWeightInfo; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmReserveTransferFilter = Nothing; - type XcmRouter = XcmRouter; - type XcmTeleportFilter = Nothing; - #[cfg(feature = "runtime-benchmarks")] - type ReachableDest = ReachableDest; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index a59f1e8f9d..5025ad90dd 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -14,25 +14,23 @@ version.workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -cumulus-pallet-xcm.workspace = true -dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true kilt-support.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} +sp-core.workspace = true sp-std.workspace = true [features] default = ["std"] std = [ - "cumulus-pallet-xcm/std", - "dip-support/std", "frame-support/std", "frame-system/std", "kilt-support/std", "parity-scale-codec/std", "scale-info/std", + "sp-core/std", "sp-std/std", ] runtime-benchmarks = [ diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index ab0cde6dda..cb0e49f801 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -31,15 +31,13 @@ pub use crate::{origin::*, pallet::*}; pub mod pallet { use super::*; - use cumulus_pallet_xcm::ensure_sibling_para; use frame_support::{dispatch::Dispatchable, pallet_prelude::*, traits::Contains, Twox64Concat}; use frame_system::pallet_prelude::*; - use parity_scale_codec::MaxEncodedLen; + use parity_scale_codec::{FullCodec, MaxEncodedLen}; + use scale_info::TypeInfo; use sp_std::boxed::Box; - use dip_support::IdentityDetailsAction; - - use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; + use crate::traits::IdentityProofVerifier; pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< ::RuntimeCall, @@ -48,15 +46,10 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - // TODO: Store also additional details received by the provider. #[pallet::storage] #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityEntries = StorageMap< - _, - Twox64Concat, - ::Identifier, - IdentityDetails<::ProofDigest, ::IdentityDetails>, - >; + pub(crate) type IdentityEntries = + StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; #[pallet::config] pub trait Config: frame_system::Config { @@ -65,51 +58,33 @@ pub mod pallet { type DipCallOriginFilter: Contains<::RuntimeCall>; /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; - /// The details stored in this pallet associated with any given subject. - type IdentityDetails: Parameter + MaxEncodedLen + Default; /// The proof users must provide to operate with their higher-level /// identity. Depending on the use cases, this proof can contain /// heterogeneous bits of information that the proof verifier will /// utilize. For instance, a proof could contain both a Merkle proof and /// a DID signature. - type Proof: Parameter; - /// The type of the committed proof digest used as the basis for - /// verifying identity proofs. - type ProofDigest: Parameter + MaxEncodedLen; + type IdentityProof: Parameter; + /// The details stored in this pallet associated with any given subject. + type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; /// The logic of the proof verifier, called upon each execution of the /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier< ::RuntimeCall, Self::Identifier, - Proof = Self::Proof, - IdentityDetails = IdentityDetails, + Proof = Self::IdentityProof, + IdentityDetails = Self::LocalIdentityInfo, Submitter = ::AccountId, >; /// The overarching runtime call type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The overarching runtime origin type. - type RuntimeOrigin: From> - + From<::RuntimeOrigin> - + Into::RuntimeOrigin>>; + type RuntimeOrigin: From> + From<::RuntimeOrigin>; } #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// The identity information related to a given subject has been - /// deleted. - IdentityInfoDeleted(T::Identifier), - /// The identity information related to a given subject has been updated - /// to a new digest. - IdentityInfoUpdated(T::Identifier, T::ProofDigest), - } - #[pallet::error] pub enum Error { /// An identity with the provided identifier could not be found. @@ -129,59 +104,34 @@ pub mod pallet { // TODO: Benchmarking #[pallet::call] impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn process_identity_action( - origin: OriginFor, - action: IdentityDetailsAction, - ) -> DispatchResult { - ensure_sibling_para(::RuntimeOrigin::from(origin))?; - - let event = match action { - IdentityDetailsAction::Updated(identifier, proof, _) => { - IdentityEntries::::mutate( - &identifier, - |entry: &mut Option< - IdentityDetails<::ProofDigest, ::IdentityDetails>, - >| { *entry = Some(proof.clone().into()) }, - ); - Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) - } - IdentityDetailsAction::Deleted(identifier) => { - IdentityEntries::::remove(&identifier); - Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) - } - }?; - - Self::deposit_event(event); - - Ok(()) - } - // TODO: Replace with a SignedExtra. - #[pallet::call_index(1)] + #[pallet::call_index(0)] #[pallet::weight(0)] pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, - proof: T::Proof, + proof: T::IdentityProof, call: Box<::RuntimeCall>, ) -> DispatchResult { + // TODO: Make origin check configurable, and require that it at least returns + // the submitter's account. let submitter = ensure_signed(origin)?; // TODO: Proper error handling ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); - let mut identity_entry = IdentityEntries::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let mut identity_entry = IdentityEntries::::get(&identifier); let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( &*call, &identifier, &submitter, &mut identity_entry, - &proof, - ) - .map_err(|_| Error::::InvalidProof)?; + proof, + ); // Write the identity info to storage after it has optionally been updated by - // the `ProofVerifier`. - IdentityEntries::::mutate(&identifier, |entry| *entry = Some(identity_entry)); + // the `ProofVerifier`, regardless of whether the proof has been verified or + // not. + IdentityEntries::::mutate(&identifier, |entry| *entry = identity_entry); + // Unwrap the result if `ok`. + let proof_verification_result = proof_verification_result.map_err(|_| Error::::InvalidProof)?; let did_origin = DipOrigin { identifier, account_address: submitter, diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 63a5c8a831..0b7fa2a85c 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -20,8 +20,8 @@ use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { type Error; - type Proof; type IdentityDetails; + type Proof; type Submitter; type VerificationResult; @@ -29,19 +29,19 @@ pub trait IdentityProofVerifier { call: &Call, subject: &Subject, submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, + identity_details: &mut Option, + proof: Self::Proof, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(Proof, ProofEntry, Submitter)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(IdentityDetails, Proof, Submitter)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { type Error = (); + type IdentityDetails = IdentityDetails; type Proof = Proof; - type IdentityDetails = ProofEntry; type Submitter = Submitter; type VerificationResult = (); @@ -49,8 +49,8 @@ impl IdentityProofVerifier, + _proof: Self::Proof, ) -> Result { Ok(()) } diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index e87da12329..5e22e357a1 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -15,25 +15,21 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] did.workspace = true -dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sp-std.workspace = true -xcm.workspace = true [features] default = ["std"] std = [ "did/std", - "dip-support/std", "frame-support/std", "frame-system/std", "parity-scale-codec/std", "scale-info/std", "sp-std/std", - "xcm/std", ] runtime-benchmarks = [ "did/runtime-benchmarks", diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 4f136590cd..5171084147 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -28,17 +28,14 @@ pub use crate::pallet::*; pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, traits::EnsureOrigin, weights::Weight}; + use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; - use sp_std::{boxed::Box, fmt::Debug}; - use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; + use parity_scale_codec::FullCodec; + use sp_std::fmt::Debug; - use dip_support::IdentityDetailsAction; - - use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, SubmitterInfo, TxBuilder}; + use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, SubmitterInfo}; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; - pub type IdentityProofActionOf = IdentityDetailsAction<::Identifier, ::ProofOutput>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -46,19 +43,22 @@ pub mod pallet { pub trait Config: frame_system::Config { type CommitOriginCheck: EnsureOrigin; type CommitOrigin: SubmitterInfo; - type Identifier: Parameter; - type IdentityProofGenerator: IdentityProofGenerator< + type Identifier: Parameter + MaxEncodedLen; + type IdentityCommitment: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; + type IdentityCommitmentGenerator: IdentityCommitmentGenerator< Self::Identifier, IdentityOf, - Output = Self::ProofOutput, + Output = Self::IdentityCommitment, >; - type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; - type ProofOutput: Clone + Eq + Debug; type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type TxBuilder: TxBuilder; } + #[pallet::storage] + #[pallet::getter(fn identity_commitments)] + pub type IdentityCommitments = + StorageMap<_, Twox64Concat, ::Identifier, ::IdentityCommitment>; + #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -66,16 +66,19 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - IdentityInfoDispatched(IdentityProofActionOf, Box), + IdentityCommitted { + identifier: T::Identifier, + commitment: T::IdentityCommitment, + }, + IdentityDeleted { + identifier: T::Identifier, + }, } #[pallet::error] pub enum Error { - BadVersion, - Dispatch, IdentityNotFound, - IdentityProofGeneration, - Predispatch, + IdentityCommitmentGeneration, } #[pallet::call] @@ -83,43 +86,29 @@ pub mod pallet { #[pallet::call_index(0)] // TODO: Update weight #[pallet::weight(0)] - pub fn commit_identity( - origin: OriginFor, - identifier: T::Identifier, - destination: Box, - asset: Box, - weight: Weight, - ) -> DispatchResult { - let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.submitter())?; - - let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; - let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { - Ok(Some(identity)) => { - let identity_proof = T::IdentityProofGenerator::generate_commitment(&identifier, &identity) - .map_err(|_| Error::::IdentityProofGeneration)?; - Ok(IdentityDetailsAction::Updated(identifier, identity_proof, ())) - } - Ok(None) => Ok(IdentityDetailsAction::Deleted(identifier)), + pub fn commit_identity(origin: OriginFor, identifier: T::Identifier) -> DispatchResult { + // TODO: use dispatcher to get deposit + let _dispatcher = + T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; + + let identity_commitment: Option = match T::IdentityProvider::retrieve(&identifier) { + Ok(Some(identity)) => T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity) + .map(Some) + .map_err(|_| Error::::IdentityCommitmentGeneration), + Ok(None) => Ok(None), Err(_) => Err(Error::::IdentityNotFound), }?; - // TODO: Add correct version creation based on lookup (?) - - let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - - let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( - action.clone(), - dispatcher, - asset, - weight, - destination, - ) - .map_err(|_| Error::::Predispatch)?; - // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the - // fee. - T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; + if let Some(commitment) = identity_commitment { + // TODO: Take deposit (once 0.9.42 PR is merged into develop) + IdentityCommitments::::insert(&identifier, commitment.clone()); + Self::deposit_event(Event::::IdentityCommitted { identifier, commitment }); + } else { + // TODO: Release deposit (once 0.9.42 PR is merged into develop) + IdentityCommitments::::remove(&identifier); + Self::deposit_event(Event::::IdentityDeleted { identifier }); + } - Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); Ok(()) } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index f5a8836d1f..5f0d137963 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -17,26 +17,24 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::DidRawOrigin; -use dip_support::IdentityDetailsAction; -use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; pub mod identity_generation { use sp_std::marker::PhantomData; - pub trait IdentityProofGenerator { + pub trait IdentityCommitmentGenerator { type Error; type Output; fn generate_commitment(identifier: &Identifier, identity: &Identity) -> Result; } - // Implement the `IdentityProofGenerator` by returning the `Default` value for - // the `Output` type. - pub struct DefaultIdentityProofGenerator(PhantomData); + // Implement the `IdentityCommitmentGenerator` by returning the `Default` value + // for the `Output` type. + pub struct DefaultIdentityCommitmentGenerator(PhantomData); - impl IdentityProofGenerator - for DefaultIdentityProofGenerator + impl IdentityCommitmentGenerator + for DefaultIdentityCommitmentGenerator where Output: Default, { @@ -49,52 +47,6 @@ pub mod identity_generation { } } -pub use identity_dispatch::*; -pub mod identity_dispatch { - use super::*; - - use frame_support::weights::Weight; - - pub trait IdentityProofDispatcher { - type PreDispatchOutput; - type Error; - - fn pre_dispatch>( - action: IdentityDetailsAction, - source: AccountId, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error>; - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error>; - } - - // Returns `Ok` without doing anything. - pub struct NullIdentityProofDispatcher; - - impl - IdentityProofDispatcher for NullIdentityProofDispatcher - { - type PreDispatchOutput = (); - type Error = (); - - fn pre_dispatch<_B>( - _action: IdentityDetailsAction, - _source: AccountId, - _asset: MultiAsset, - _weight: Weight, - _destination: MultiLocation, - ) -> Result<((), MultiAssets), Self::Error> { - Ok(((), MultiAssets::default())) - } - - fn dispatch(_pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Ok(()) - } - } -} - pub use identity_provision::*; pub mod identity_provision { use sp_std::marker::PhantomData; @@ -134,17 +86,6 @@ pub mod identity_provision { } } -// Given a destination and an identity action, creates and encodes the proper -// `Transact` call. -pub trait TxBuilder { - type Error; - - fn build( - dest: MultiLocation, - action: IdentityDetailsAction, - ) -> Result, Self::Error>; -} - pub trait SubmitterInfo { type Submitter; diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml new file mode 100644 index 0000000000..c5398f052f --- /dev/null +++ b/pallets/pallet-relay-store/Cargo.toml @@ -0,0 +1,42 @@ +[package] +authors.workspace = true +description = "Pallet enabling storing finalize relay head data on chain." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-relay-store" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +cumulus-pallet-parachain-system.workspace = true +frame-support.workspace = true +frame-system.workspace = true +log.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-core.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "cumulus-pallet-parachain-system/std", + "frame-support/std", + "frame-system/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs new file mode 100644 index 0000000000..3cc307e836 --- /dev/null +++ b/pallets/pallet-relay-store/src/lib.rs @@ -0,0 +1,109 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +mod relay; + +pub use crate::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use frame_support::{pallet_prelude::*, BoundedVec}; + use frame_system::pallet_prelude::*; + use sp_core::H256; + + use crate::relay::RelayParentInfo; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::storage] + #[pallet::getter(fn latest_relay_head_for_block)] + pub(crate) type LatestRelayHeads = StorageMap<_, Twox64Concat, u32, RelayParentInfo>; + + // TODO: Replace this with a fixed-length array once support for const generics + // is fully supported in Substrate. + #[pallet::storage] + pub(crate) type LatestBlockHeights = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type MaxRelayBlocksStored: Get; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks> for Pallet + where + T: cumulus_pallet_parachain_system::Config, + { + fn on_initialize(_n: BlockNumberFor) -> Weight { + // Reserve weight to update the last relay state root + // TODO: Replace with benchmarked version of `on_finalize(` + ::DbWeight::get().writes(2) + } + + // TODO: Benchmarks + fn on_finalize(_n: BlockNumberFor) { + // Called before the validation data is cleaned in the + // parachain_system::on_finalize hook + let Some(new_validation_data) = cumulus_pallet_parachain_system::Pallet::::validation_data() else { return; }; + let mut latest_block_heights = LatestBlockHeights::::get(); + // Remove old relay block from both storage entries. + if latest_block_heights.is_full() { + let oldest_block_height = latest_block_heights.remove(0); + LatestRelayHeads::::remove(oldest_block_height); + log::trace!( + "Relay block queue full. Removing oldest block at height {:?}", + oldest_block_height + ); + } + // Set the new relay block in storage. + let relay_block_height = new_validation_data.relay_parent_number; + log::trace!( + "Adding new relay block with state root {:#02x?} and number {:?}", + new_validation_data.relay_parent_storage_root, + new_validation_data.relay_parent_number, + ); + let push_res = latest_block_heights.try_push(relay_block_height); + if let Err(err) = push_res { + log::error!( + "Pushing a new relay block to the queue should not fail but it did when adding relay block n. {:?}", + err + ); + } else { + LatestBlockHeights::::set(latest_block_heights); + LatestRelayHeads::::insert( + relay_block_height, + RelayParentInfo { + relay_parent_storage_root: new_validation_data.relay_parent_storage_root, + }, + ); + } + } + } +} diff --git a/crates/dip-support/src/lib.rs b/pallets/pallet-relay-store/src/relay.rs similarity index 73% rename from crates/dip-support/src/lib.rs rename to pallets/pallet-relay-store/src/relay.rs index 31256a1dda..9dfaf69f95 100644 --- a/crates/dip-support/src/lib.rs +++ b/pallets/pallet-relay-store/src/relay.rs @@ -16,16 +16,11 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Crate documentation - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +use sp_core::RuntimeDebug; -#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub enum IdentityDetailsAction { - Updated(Identifier, Proof, Details), - Deleted(Identifier), +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug, MaxEncodedLen)] +pub struct RelayParentInfo { + pub relay_parent_storage_root: Hash, } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index da0addb5eb..7134a22ed9 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -69,6 +69,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "kilt-support/runtime-benchmarks", "kilt-dip-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index cebbf104ce..8929a672cb 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -18,8 +18,8 @@ use did::did_details::DidDetails; use kilt_dip_support::{ - did::{CombineIdentityFrom, CombinedIdentityResult}, merkle::RevealedWeb3Name, + utils::{CombineIdentityFrom, CombinedIdentityResult}, }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 254380b65f..62050e2359 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -18,22 +18,22 @@ use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; -use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, MerkleProof}; +use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::traits::IdentityProofGenerator; +use pallet_dip_provider::traits::IdentityCommitmentGenerator; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use kilt_dip_support::merkle::{DidKeyRelationship, ProofLeaf}; +use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::{dip::did::LinkedDidInfoOf, DidIdentifier}; pub type BlindedValue = Vec; -pub type DidMerkleProofOf = MerkleProof< +pub type DidMerkleProofOf = DidMerkleProof< Vec, - ProofLeaf< + RevealedDidMerkleProofLeaf< KeyIdOf, ::BlockNumber, ::Web3Name, @@ -49,7 +49,7 @@ pub struct CompleteMerkleProof { pub struct DidMerkleRootGenerator(PhantomData); -type ProofLeafOf = ProofLeaf< +type ProofLeafOf = RevealedDidMerkleProofLeaf< KeyIdOf, ::BlockNumber, ::Web3Name, @@ -212,12 +212,18 @@ where } else { Err(()) }?; - Ok(ProofLeaf::DidKey(did_key_merkle_key, key_details.clone().into())) + Ok(RevealedDidMerkleProofLeaf::DidKey( + did_key_merkle_key, + key_details.clone().into(), + )) }) .chain(account_ids.map(|account_id| -> Result, ()> { let Some(linked_accounts) = linked_accounts else { return Err(()) }; if linked_accounts.contains(account_id) { - Ok(ProofLeaf::LinkedAccount(account_id.clone().into(), ().into())) + Ok(RevealedDidMerkleProofLeaf::LinkedAccount( + account_id.clone().into(), + ().into(), + )) } else { Err(()) } @@ -227,7 +233,7 @@ where match (should_include_web3_name, linked_web3_name) { // If web3name should be included and it exists... (true, Some(web3name_details)) => { - leaves.push(ProofLeaf::Web3Name( + leaves.push(RevealedDidMerkleProofLeaf::Web3Name( web3name_details.web3_name.clone().into(), web3name_details.claimed_at.into(), )); @@ -251,7 +257,7 @@ where } } -impl IdentityProofGenerator> for DidMerkleRootGenerator +impl IdentityCommitmentGenerator> for DidMerkleRootGenerator where T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { From eb60cbb89a182fd675939004c9a765d14431db62 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 22 Sep 2023 12:49:41 +0200 Subject: [PATCH 11/28] Fmt --- crates/kilt-dip-support/src/did.rs | 7 +++++-- crates/kilt-dip-support/src/lib.rs | 6 ++++-- crates/kilt-dip-support/src/merkle.rs | 9 +++++++-- crates/kilt-dip-support/src/state_proofs.rs | 8 ++++++-- pallets/pallet-relay-store/src/lib.rs | 4 +++- runtimes/common/src/dip/did.rs | 8 ++++++-- runtimes/common/src/dip/merkle.rs | 12 +++++++++--- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 1b6a8fff95..65e7ff81b4 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -100,7 +100,8 @@ impl< DidLocalDetails: Bump + Default + Encode, RemoteAccountId: Clone, MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, - CallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, + CallVerifier: + DipCallOriginFilter, DidVerificationKeyRelationship)>, { #[allow(clippy::result_unit_err)] pub(crate) fn verify_did_signature_for_call( @@ -139,7 +140,9 @@ impl< .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) .is_ok() }); - let Some((key, relationship)) = valid_signing_key else { return Err(()) }; + let Some((key, relationship)) = valid_signing_key else { + return Err(()); + }; if let Some(details) = local_details { details.bump(); } else { diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index faa13d56f9..9bfc305b1f 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -165,7 +165,8 @@ impl< LocalContextProvider::Hash: Encode, LocalContextProvider::SignedExtra: Encode, LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, + LocalDidCallVerifier: + DipCallOriginFilter, DidVerificationKeyRelationship)>, ProviderDipMerkleHasher: sp_core::Hasher, ProviderDidKeyId: Encode + Clone + Into, @@ -372,7 +373,8 @@ impl< LocalContextProvider::Hash: Encode, LocalContextProvider::SignedExtra: Encode, LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, + LocalDidCallVerifier: + DipCallOriginFilter, DidVerificationKeyRelationship)>, ProviderDipMerkleHasher: sp_core::Hasher, ProviderDidKeyId: Encode + Clone + Into, diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index f981cbb993..a346659444 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -68,7 +68,9 @@ impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey { #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct DidKeyMerkleValue(pub DidPublicKeyDetails); -impl From> for DidKeyMerkleValue { +impl From> + for DidKeyMerkleValue +{ fn from(value: DidPublicKeyDetails) -> Self { Self(value) } @@ -272,7 +274,10 @@ impl< #[allow(clippy::type_complexity)] pub(crate) fn verify_dip_merkle_proof( identity_commitment: &Hasher::Out, - proof: DidMerkleProof>, RevealedDidMerkleProofLeaf>, + proof: DidMerkleProof< + Vec>, + RevealedDidMerkleProofLeaf, + >, ) -> Result< RevealedDidMerkleProofLeaves< KeyId, diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 0e8bf02562..94a9a63421 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -117,7 +117,9 @@ pub(super) mod relay_chain { debug_assert!(revealed_leaves.len() == 1usize); debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); } - let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { return Err(()) }; + let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { + return Err(()); + }; // TODO: Figure out why RPC call returns 2 bytes in front which we don't need let mut unwrapped_head = &encoded_head[2..]; Header::decode(&mut unwrapped_head).map_err(|_| ()) @@ -276,7 +278,9 @@ pub(super) mod parachain { debug_assert!(revealed_leaves.len() == 1usize); debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); } - let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { return Err(()) }; + let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { + return Err(()); + }; ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).map_err(|_| ()) } } diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs index 3cc307e836..d5ca7bc1cf 100644 --- a/pallets/pallet-relay-store/src/lib.rs +++ b/pallets/pallet-relay-store/src/lib.rs @@ -71,7 +71,9 @@ pub mod pallet { fn on_finalize(_n: BlockNumberFor) { // Called before the validation data is cleaned in the // parachain_system::on_finalize hook - let Some(new_validation_data) = cumulus_pallet_parachain_system::Pallet::::validation_data() else { return; }; + let Some(new_validation_data) = cumulus_pallet_parachain_system::Pallet::::validation_data() else { + return; + }; let mut latest_block_heights = LatestBlockHeights::::get(); // Remove old relay block from both storage entries. if latest_block_heights.is_full() { diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 8929a672cb..853d70e4bb 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -61,8 +61,12 @@ where type Success = Web3OwnershipOf; fn retrieve(identifier: &T::Web3NameOwner) -> Result, Self::Error> { - let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) else { return Ok(None) }; - let Some(details) = pallet_web3_names::Pallet::::owner(&web3_name) else { return Err(()) }; + let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) else { + return Ok(None); + }; + let Some(details) = pallet_web3_names::Pallet::::owner(&web3_name) else { + return Err(()); + }; Ok(Some(Web3OwnershipOf:: { web3_name, claimed_at: details.claimed_at, diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index eea090626c..4ece36e180 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -75,7 +75,9 @@ where // details leaf, as we already do with out `DidDetails` type. fn calculate_root_with_db(identity: &LinkedDidInfoOf, db: &mut MemoryDB) -> Result { // Fails if the DID details do not exist. - let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { return Err(()) }; + let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { + return Err(()); + }; let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); @@ -193,7 +195,9 @@ where A: Iterator, { // Fails if the DID details do not exist. - let (Some(did_details), linked_web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { return Err(()) }; + let (Some(did_details), linked_web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { + return Err(()); + }; let mut db = MemoryDB::default(); let root = Self::calculate_root_with_db(identity, &mut db)?; @@ -220,7 +224,9 @@ where )) }) .chain(account_ids.map(|account_id| -> Result, ()> { - let Some(linked_accounts) = linked_accounts else { return Err(()) }; + let Some(linked_accounts) = linked_accounts else { + return Err(()); + }; if linked_accounts.contains(account_id) { Ok(RevealedDidMerkleProofLeaf::LinkedAccount( account_id.clone().into(), From 85019bb4f3d5101e654ea9849c0d1ac5bbb29b51 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 31 Oct 2023 13:41:34 +0000 Subject: [PATCH 12/28] feat: add meaningful errors (#572) Fixes https://github.com/KILTprotocol/ticket/issues/2552, by both removing all occurrences of `.expect()` and `.unwrap()` and replacing them with `Internal` errors, and by introducing error enums. The logic is that for both the provider and the consumer, a generic error is bubbled up from the runtime, and because of the constraints imposed by Substrate, the error reason is encoded as a `u16`, where each "class" of error is namespaced with a 255 value increment. A snippet of this is shown below: ```rust fn from( value: DipSiblingProviderStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, DidSignatureVerificationError >, ) -> Self { match value { DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError(error) => { error.into() } DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError(error) => { u8::MAX as u16 + error.into() as u16 } DipSiblingProviderStateProofVerifierError::DipProofVerificationError(error) => { u8::MAX as u16 * 2 + error.into() as u16 } DipSiblingProviderStateProofVerifierError::DidSignatureVerificationError(error) => { u8::MAX as u16 * 3 + error.into() as u16 } } } ``` The specific reason for the error can then be retrieved and decoded directly from the Polkadot Apps interface, as the example below shows, where the first byte `01` indicates the enum variant index, the second byte is the varint encoding of the SCALE-encoded u16, and the third and forth bytes `e000` represent the `u16` error reason. This can be decoded as decimal, where each value has a one-to-one link to a specific error from the runtime. The pallet consumer expects any other implementation of the DIP consumer protocol to expose the proof verification error reason as a `u16`, but of course developers are free to not give any meaning to it and treat everything as a generic `InvalidProof` error. --- Cargo.lock | 1 + crates/kilt-dip-support/Cargo.toml | 1 + crates/kilt-dip-support/src/did.rs | 50 ++++- crates/kilt-dip-support/src/lib.rs | 175 ++++++++++++++++-- crates/kilt-dip-support/src/merkle.rs | 57 +++--- crates/kilt-dip-support/src/state_proofs.rs | 60 ++++-- crates/kilt-dip-support/src/utils.rs | 32 +++- dip-template/runtimes/dip-consumer/src/dip.rs | 13 +- dip-template/runtimes/dip-provider/src/dip.rs | 29 ++- dip-template/runtimes/dip-provider/src/lib.rs | 26 +-- nodes/standalone/src/command.rs | 2 +- nodes/standalone/src/rpc.rs | 3 +- pallets/attestation/src/benchmarking.rs | 3 +- pallets/delegation/src/benchmarking.rs | 3 +- pallets/did/src/benchmarking.rs | 3 +- pallets/pallet-did-lookup/src/benchmarking.rs | 3 +- pallets/pallet-dip-consumer/src/lib.rs | 21 +-- pallets/pallet-dip-provider/src/lib.rs | 15 +- pallets/pallet-migration/src/lib.rs | 3 +- pallets/pallet-migration/src/test.rs | 3 +- pallets/pallet-web3-names/src/benchmarking.rs | 3 +- pallets/parachain-staking/src/lib.rs | 7 +- runtimes/common/src/dip/did.rs | 34 +++- runtimes/common/src/dip/merkle.rs | 149 +++++++++++---- 24 files changed, 518 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2370e0c8c7..a5aef17c56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4530,6 +4530,7 @@ dependencies = [ "frame-system", "hash-db", "hex-literal 0.3.4", + "log", "pallet-dip-consumer", "pallet-dip-provider", "pallet-relay-store", diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index 49893b6fd6..8941e0e46d 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true [dependencies] # External dependencies hash-db.workspace = true +log.workspace = true # Internal dependencies did.workspace = true diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 65e7ff81b4..7b16bcbb8e 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -25,7 +25,7 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; use sp_runtime::traits::CheckedSub; -use sp_std::marker::PhantomData; +use sp_std::{marker::PhantomData, vec::Vec}; use crate::{ merkle::RevealedDidKey, @@ -44,6 +44,24 @@ pub struct TimeBoundDidSignature { pub block_number: BlockNumber, } +pub enum RevealedDidKeysSignatureAndCallVerifierError { + SignatureNotFresh, + SignatureUnverifiable, + OriginCheckFailed, + Internal, +} + +impl From for u8 { + fn from(value: RevealedDidKeysSignatureAndCallVerifierError) -> Self { + match value { + RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh => 0, + RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable => 1, + RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed => 2, + RevealedDidKeysSignatureAndCallVerifierError::Internal => u8::MAX, + } + } +} + pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< Call, Submitter, @@ -109,7 +127,10 @@ impl< submitter: &Submitter, local_details: &mut Option, merkle_revealed_did_signature: RevealedDidKeysAndSignature, - ) -> Result<(DidVerificationKey, DidVerificationKeyRelationship), ()> { + ) -> Result< + (DidVerificationKey, DidVerificationKeyRelationship), + RevealedDidKeysSignatureAndCallVerifierError, + > { let block_number = ContextProvider::block_number(); let is_signature_fresh = if let Some(blocks_ago_from_now) = block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) @@ -120,7 +141,10 @@ impl< // Signature generated at a future time, not possible to verify. false }; - ensure!(is_signature_fresh, ()); + ensure!( + is_signature_fresh, + RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh, + ); let encoded_payload = ( call, &local_details, @@ -131,24 +155,32 @@ impl< ) .encode(); // Only consider verification keys from the set of revealed keys. - let mut proof_verification_keys = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { + let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | { let DidPublicKey::PublicVerificationKey(key) = key else { return None }; - Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) }); - let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { + if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) { + // TODO: Fix this logic to avoid cloning + Some(Ok((key.clone(), vr))) + } else { + log::error!("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."); + Some(Err(RevealedDidKeysSignatureAndCallVerifierError::Internal)) + } + }).collect::>()?; + let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { verification_key .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) .is_ok() }); let Some((key, relationship)) = valid_signing_key else { - return Err(()); + return Err(RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable); }; if let Some(details) = local_details { details.bump(); } else { *local_details = Some(DidLocalDetails::default()); }; - CallVerifier::check_call_origin_info(call, &(key.clone(), relationship)).map_err(|_| ())?; - Ok((key.clone(), relationship)) + CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)) + .map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?; + Ok((key.clone(), *relationship)) } } diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 9bfc305b1f..0d0e80425e 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -33,9 +33,18 @@ use ::did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use pallet_dip_consumer::traits::IdentityProofVerifier; use crate::{ - did::{RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, TimeBoundDidSignature}, - merkle::{DidMerkleProof, DidMerkleProofVerifier, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, + did::{ + RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, + RevealedDidKeysSignatureAndCallVerifierError, TimeBoundDidSignature, + }, + merkle::{ + DidMerkleProof, DidMerkleProofVerifier, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, + RevealedDidMerkleProofLeaves, + }, + state_proofs::{ + parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, + relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError}, + }, traits::{ Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, RelayChainStorageInfo, @@ -75,6 +84,63 @@ pub struct DipMerkleProofAndDidSignature { signature: TimeBoundDidSignature, } +pub enum DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, +> { + ParachainHeadMerkleProofVerificationError(ParachainHeadMerkleProofVerificationError), + IdentityCommitmentMerkleProofVerificationError(IdentityCommitmentMerkleProofVerificationError), + DipProofVerificationError(DipProofVerificationError), + DidSignatureVerificationError(DidSignatureVerificationError), +} + +impl< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + > + From< + DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + > for u16 +where + ParachainHeadMerkleProofVerificationError: Into, + IdentityCommitmentMerkleProofVerificationError: Into, + DipProofVerificationError: Into, + DidSignatureVerificationError: Into, +{ + fn from( + value: DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + ) -> Self { + match value { + DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError(error) => { + error.into() as u16 + } + DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError(error) => { + u8::MAX as u16 + error.into() as u16 + } + DipSiblingProviderStateProofVerifierError::DipProofVerificationError(error) => { + u8::MAX as u16 * 2 + error.into() as u16 + } + DipSiblingProviderStateProofVerifierError::DidSignatureVerificationError(error) => { + u8::MAX as u16 * 3 + error.into() as u16 + } + } + } +} + #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct DipSiblingProviderStateProofVerifier< RelayChainStateInfo, @@ -174,7 +240,12 @@ impl< ProviderLinkedAccountId: Encode + Clone, ProviderWeb3Name: Encode + Clone, { - type Error = (); + type Error = DipSiblingProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; type IdentityDetails = LocalDidDetails; type Proof = SiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, @@ -211,7 +282,8 @@ impl< &SiblingProviderParachainId::get(), &proof.para_state_root.relay_block_height, proof.para_state_root.proof, - )?; + ) + .map_err(DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError)?; // 2. Verify parachain state proof. let subject_identity_commitment = @@ -219,7 +291,8 @@ impl< subject, provider_parachain_header.state_root.into(), proof.dip_identity_commitment, - )?; + ) + .map_err(DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError)?; // 3. Verify DIP merkle proof. let proof_leaves = DidMerkleProofVerifier::< @@ -231,7 +304,8 @@ impl< _, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + .map_err(DipSiblingProviderStateProofVerifierError::DipProofVerificationError)?; // 4. Verify DID signature. RevealedDidKeysSignatureAndCallVerifier::< @@ -252,7 +326,7 @@ impl< merkle_leaves: proof_leaves.borrow(), did_signature: proof.did.signature, }, - )?; + ).map_err(DipSiblingProviderStateProofVerifierError::DidSignatureVerificationError)?; Ok(proof_leaves) } @@ -271,6 +345,67 @@ pub struct ChildParachainDipStateProof< did: DipMerkleProofAndDidSignature, } +pub enum DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, +> { + InvalidBlockHeight, + InvalidBlockHash, + ParachainHeadMerkleProofVerificationError(ParachainHeadMerkleProofVerificationError), + IdentityCommitmentMerkleProofVerificationError(IdentityCommitmentMerkleProofVerificationError), + DipProofVerificationError(DipProofVerificationError), + DidSignatureVerificationError(DidSignatureVerificationError), +} + +impl< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + > + From< + DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + > for u16 +where + ParachainHeadMerkleProofVerificationError: Into, + IdentityCommitmentMerkleProofVerificationError: Into, + DipProofVerificationError: Into, + DidSignatureVerificationError: Into, +{ + fn from( + value: DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + ) -> Self { + match value { + DipChildProviderStateProofVerifierError::InvalidBlockHeight => 0, + DipChildProviderStateProofVerifierError::InvalidBlockHash => 1, + DipChildProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError(error) => { + u8::MAX as u16 + error.into() as u16 + } + DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError(error) => { + u8::MAX as u16 * 2 + error.into() as u16 + } + DipChildProviderStateProofVerifierError::DipProofVerificationError(error) => { + u8::MAX as u16 * 3 + error.into() as u16 + } + DipChildProviderStateProofVerifierError::DidSignatureVerificationError(error) => { + u8::MAX as u16 * 4 + error.into() as u16 + } + } + } +} + pub struct DipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, @@ -382,7 +517,12 @@ impl< ProviderLinkedAccountId: Encode + Clone, ProviderWeb3Name: Encode + Clone, { - type Error = (); + type Error = DipChildProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; type IdentityDetails = LocalDidDetails; type Proof = ChildParachainDipStateProof< ::BlockNumber, @@ -415,12 +555,12 @@ impl< proof: Self::Proof, ) -> Result { // 1. Retrieve block hash from provider at the proof height - let block_hash_at_height = - RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height).ok_or(())?; + let block_hash_at_height = RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height) + .ok_or(DipChildProviderStateProofVerifierError::InvalidBlockHeight)?; // 1.1 Verify that the provided header hashes to the same block has retrieved if block_hash_at_height != proof.relay_header.hash() { - return Err(()); + return Err(DipChildProviderStateProofVerifierError::InvalidBlockHash); } // 1.2 If so, extract the state root from the header let state_root_at_height = proof.relay_header.state_root; @@ -432,7 +572,8 @@ impl< &ChildProviderParachainId::get(), &state_root_at_height, proof.para_state_root.proof, - )?; + ) + .map_err(DipChildProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError)?; // 3. Verify parachain state proof. let subject_identity_commitment = @@ -440,7 +581,8 @@ impl< subject, provider_parachain_header.state_root.into(), proof.dip_identity_commitment, - )?; + ) + .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError)?; // 4. Verify DIP merkle proof. let proof_leaves = DidMerkleProofVerifier::< @@ -452,7 +594,8 @@ impl< _, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + .map_err(DipChildProviderStateProofVerifierError::DipProofVerificationError)?; // 5. Verify DID signature. RevealedDidKeysSignatureAndCallVerifier::< @@ -473,7 +616,7 @@ impl< merkle_leaves: proof_leaves.borrow(), did_signature: proof.did.signature, }, - )?; + ).map_err(DipChildProviderStateProofVerifierError::DidSignatureVerificationError)?; Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index a346659444..2f23c952e5 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -44,7 +44,6 @@ impl From for DidKeyRelationship { } impl TryFrom for DidVerificationKeyRelationship { - // TODO: Error handling type Error = (); fn try_from(value: DidKeyRelationship) -> Result { @@ -179,32 +178,6 @@ pub struct RevealedDidMerkleProofLeaves< pub linked_accounts: BoundedVec>, } -// impl< -// KeyId, -// AccountId, -// BlockNumber, -// Web3Name, -// LinkedAccountId, -// const MAX_REVEALED_KEYS_COUNT: u32, -// const MAX_REVEALED_ACCOUNTS_COUNT: u32, -// > Default for RevealedDidMerkleProofLeaves< -// KeyId, -// AccountId, -// BlockNumber, -// Web3Name, -// LinkedAccountId, -// MAX_REVEALED_KEYS_COUNT, -// MAX_REVEALED_ACCOUNTS_COUNT, -// > { -// fn default() -> Self { -// Self { -// did_keys: BoundedVec::default(), -// linked_accounts: BoundedVec::default(), -// web3_name: None, -// } -// } -// } - impl< KeyId, AccountId, @@ -229,6 +202,22 @@ impl< } } +pub enum DidMerkleProofVerifierError { + InvalidMerkleProof, + TooManyRevealedKeys, + TooManyRevealedAccounts, +} + +impl From for u8 { + fn from(value: DidMerkleProofVerifierError) -> Self { + match value { + DidMerkleProofVerifierError::InvalidMerkleProof => 0, + DidMerkleProofVerifierError::TooManyRevealedKeys => 1, + DidMerkleProofVerifierError::TooManyRevealedAccounts => 2, + } + } +} + /// A type that verifies a Merkle proof that reveals some leaves representing /// keys in a DID Document. /// Can also be used on its own, without any DID signature verification. @@ -288,7 +277,7 @@ impl< MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, >, - (), + DidMerkleProofVerifierError, > { // TODO: more efficient by removing cloning and/or collecting. // Did not find another way of mapping a Vec<(Vec, Vec)> to a @@ -299,7 +288,7 @@ impl< .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves) - .map_err(|_| ())?; + .map_err(|_| DidMerkleProofVerifierError::InvalidMerkleProof)?; // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. @@ -322,8 +311,8 @@ impl< relationship: key_id.1, details: key_value.0.clone(), }) - .map_err(|_| ())?; - Ok::<_, ()>((keys, web3_name, linked_accounts)) + .map_err(|_| DidMerkleProofVerifierError::TooManyRevealedKeys)?; + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) } // TODO: Avoid cloning if possible RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( @@ -335,8 +324,10 @@ impl< linked_accounts, )), RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { - linked_accounts.try_push(account_id.0.clone()).map_err(|_| ())?; - Ok::<_, ()>((keys, web3_name, linked_accounts)) + linked_accounts + .try_push(account_id.0.clone()) + .map_err(|_| DidMerkleProofVerifierError::TooManyRevealedAccounts)?; + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) } }, )?; diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 94a9a63421..53099e6adb 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use parity_scale_codec::{Decode, Encode, HasCompact}; -use sp_core::{storage::StorageKey, U256}; +use sp_core::{storage::StorageKey, RuntimeDebug, U256}; use sp_runtime::generic::Header; use sp_std::{marker::PhantomData, vec::Vec}; use sp_trie::StorageProof; @@ -92,6 +92,25 @@ pub(super) mod relay_chain { use crate::traits::{RelayChainStateInfo, RelayChainStorageInfo}; + #[derive(RuntimeDebug)] + pub enum ParachainHeadProofVerifierError { + InvalidMerkleProof, + RequiredLeafNotRevealed, + HeaderDecode, + RelaychainStateRootNotFound, + } + + impl From for u8 { + fn from(value: ParachainHeadProofVerifierError) -> Self { + match value { + ParachainHeadProofVerifierError::InvalidMerkleProof => 0, + ParachainHeadProofVerifierError::RequiredLeafNotRevealed => 1, + ParachainHeadProofVerifierError::HeaderDecode => 2, + ParachainHeadProofVerifierError::RelaychainStateRootNotFound => 3, + } + } + } + pub struct ParachainHeadProofVerifier(PhantomData); // Uses the provided `root` to verify the proof. @@ -106,23 +125,23 @@ pub(super) mod relay_chain { para_id: &RelayChainState::ParaId, root: &OutputOf<::Hasher>, proof: impl IntoIterator>, - ) -> Result, ()> { + ) -> Result, ParachainHeadProofVerifierError> { let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); let storage_proof = StorageProof::new(proof); let revealed_leaves = read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) - .map_err(|_| ())?; + .map_err(|_| ParachainHeadProofVerifierError::InvalidMerkleProof)?; // TODO: Remove at some point { debug_assert!(revealed_leaves.len() == 1usize); debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); } let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { - return Err(()); + return Err(ParachainHeadProofVerifierError::RequiredLeafNotRevealed); }; // TODO: Figure out why RPC call returns 2 bytes in front which we don't need let mut unwrapped_head = &encoded_head[2..]; - Header::decode(&mut unwrapped_head).map_err(|_| ()) + Header::decode(&mut unwrapped_head).map_err(|_| ParachainHeadProofVerifierError::HeaderDecode) } } @@ -140,8 +159,9 @@ pub(super) mod relay_chain { para_id: &RelayChainState::ParaId, relay_height: &RelayChainState::BlockNumber, proof: impl IntoIterator>, - ) -> Result, ()> { - let relay_state_root = RelayChainState::state_root_for_block(relay_height).ok_or(())?; + ) -> Result, ParachainHeadProofVerifierError> { + let relay_state_root = RelayChainState::state_root_for_block(relay_height) + .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) } } @@ -250,6 +270,23 @@ pub(super) mod parachain { use crate::traits::ProviderParachainStateInfo; + #[derive(RuntimeDebug)] + pub enum DipIdentityCommitmentProofVerifierError { + InvalidMerkleProof, + RequiredLeafNotRevealed, + CommitmentDecode, + } + + impl From for u8 { + fn from(value: DipIdentityCommitmentProofVerifierError) -> Self { + match value { + DipIdentityCommitmentProofVerifierError::InvalidMerkleProof => 0, + DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed => 1, + DipIdentityCommitmentProofVerifierError::CommitmentDecode => 2, + } + } + } + pub struct DipIdentityCommitmentProofVerifier(PhantomData); impl DipIdentityCommitmentProofVerifier @@ -264,7 +301,7 @@ pub(super) mod parachain { identifier: &ParaInfo::Identifier, state_root: OutputOf, proof: impl IntoIterator>, - ) -> Result { + ) -> Result { let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier); let storage_proof = StorageProof::new(proof); let revealed_leaves = read_proof_check::( @@ -272,16 +309,17 @@ pub(super) mod parachain { storage_proof, [&dip_commitment_storage_key].iter(), ) - .map_err(|_| ())?; + .map_err(|_| DipIdentityCommitmentProofVerifierError::InvalidMerkleProof)?; // TODO: Remove at some point { debug_assert!(revealed_leaves.len() == 1usize); debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); } let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { - return Err(()); + return Err(DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed); }; - ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).map_err(|_| ()) + ParaInfo::Commitment::decode(&mut &encoded_commitment[..]) + .map_err(|_| DipIdentityCommitmentProofVerifierError::CommitmentDecode) } } diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs index 85fa1334f9..30d2d01d50 100644 --- a/crates/kilt-dip-support/src/utils.rs +++ b/crates/kilt-dip-support/src/utils.rs @@ -17,6 +17,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use pallet_dip_provider::traits::IdentityProvider; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; use sp_std::marker::PhantomData; pub struct CombinedIdentityResult { @@ -81,14 +83,35 @@ where pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); +#[derive(Encode, Decode, TypeInfo)] +pub enum CombineError { + A(ErrorA), + B(ErrorB), + C(ErrorC), +} + +impl From> for u16 +where + ErrorA: Into, + ErrorB: Into, + ErrorC: Into, +{ + fn from(value: CombineError) -> Self { + match value { + CombineError::A(error) => error.into(), + CombineError::B(error) => error.into(), + CombineError::C(error) => error.into(), + } + } +} + impl IdentityProvider for CombineIdentityFrom where A: IdentityProvider, B: IdentityProvider, C: IdentityProvider, { - // TODO: Proper error handling - type Error = (); + type Error = CombineError; type Success = CombinedIdentityResult, Option, Option>; fn retrieve(identifier: &Identifier) -> Result, Self::Error> { @@ -105,8 +128,9 @@ where b: ok_b, c: ok_c, })), - // If any of them returns an `Err`, return an `Err` - _ => Err(()), + (Err(e), _, _) => Err(CombineError::A(e)), + (_, Err(e), _) => Err(CombineError::B(e)), + (_, _, Err(e)) => Err(CombineError::C(e)), } } } diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index b9ac581d0a..40eb0bf100 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -55,6 +55,7 @@ impl pallet_dip_consumer::Config for Runtime { type Identifier = DidIdentifier; type IdentityProof = >::Proof; type LocalIdentityInfo = u128; + type ProofVerificationError = >::Error; type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; @@ -106,20 +107,26 @@ fn single_key_relationship<'a>( }) } +pub enum DipCallFilterError { + BadOrigin, + WrongVerificationRelationship, +} + pub struct DipCallFilter; impl DipCallOriginFilter for DipCallFilter { - type Error = (); + type Error = DipCallFilterError; type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); type Success = (); // Accepts only a DipOrigin for the DidLookup pallet calls. fn check_call_origin_info(call: &RuntimeCall, info: &Self::OriginInfo) -> Result { - let key_relationship = single_key_relationship([call].into_iter())?; + let key_relationship = + single_key_relationship([call].into_iter()).map_err(|_| DipCallFilterError::BadOrigin)?; if info.1 == key_relationship { Ok(()) } else { - Err(()) + Err(DipCallFilterError::WrongVerificationRelationship) } } } diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 108bcbd7f2..bd86831553 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,17 +16,42 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{DidRawOrigin, EnsureDidOrigin}; -use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; +use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; +use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_dip_provider::traits::IdentityProvider; +use parity_scale_codec::{Decode, Encode}; +use runtime_common::dip::{ + did::LinkedDidInfoProviderOf, + merkle::{DidMerkleProofError, DidMerkleRootGenerator}, +}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent}; +#[derive(Encode, Decode, TypeInfo)] +pub struct RuntimeApiDipProofRequest { + pub(crate) identifier: DidIdentifier, + pub(crate) keys: Vec>, + pub(crate) accounts: Vec, + pub(crate) should_include_web3_name: bool, +} + +#[derive(Encode, Decode, TypeInfo)] +pub enum RuntimeApiDipProofError { + IdentityNotFound, + IdentityProviderError( as IdentityProvider>::Error), + MerkleProofError(DidMerkleProofError), +} + impl pallet_dip_provider::Config for Runtime { type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; type IdentityCommitment = Hash; type IdentityCommitmentGenerator = DidMerkleRootGenerator; + type IdentityCommitmentGeneratorError = DidMerkleProofError; type IdentityProvider = LinkedDidInfoProviderOf; + type IdentityProviderError = as IdentityProvider>::Error; type RuntimeEvent = RuntimeEvent; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 784a881bf3..578fb1939b 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -22,7 +22,6 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_web3_names::web3_name::AsciiWeb3Name; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -34,7 +33,7 @@ use cumulus_pallet_parachain_system::{ }; use cumulus_primitives_core::CollationInfo; use cumulus_primitives_timestamp::InherentDataProvider; -use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; +use did::{DidRawOrigin, EnsureDidOrigin}; use frame_support::{ construct_runtime, dispatch::DispatchClass, @@ -425,14 +424,6 @@ impl pallet_web3_names::Config for Runtime { type WeightInfo = (); } -#[derive(Encode, Decode, TypeInfo)] -pub struct DipProofRequest { - identifier: DidIdentifier, - keys: Vec>, - accounts: Vec, - should_include_web3_name: bool, -} - impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> SlotDuration { @@ -578,13 +569,14 @@ impl_runtime_apis! { } // TODO: Support generating different versions of the proof, based on the provided parameter - impl kilt_runtime_api_dip_provider::DipProvider>, ()> for Runtime { - fn generate_proof(request: DipProofRequest) -> Result>, ()> { - let Some(linked_did_info) = ::IdentityProvider::retrieve(&request.identifier)? else { return Err(()) }; - if linked_did_info.a.is_none() { - return Err(()); - } - DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()) + impl kilt_runtime_api_dip_provider::DipProvider>, RuntimeApiDipProofError> for Runtime { + fn generate_proof(request: RuntimeApiDipProofRequest) -> Result>, RuntimeApiDipProofError> { + let linked_did_info = match ::IdentityProvider::retrieve(&request.identifier) { + Ok(Some(linked_did_info)) => Ok(linked_did_info), + Ok(None) => Err(RuntimeApiDipProofError::IdentityNotFound), + Err(e) => Err(RuntimeApiDipProofError::IdentityProviderError(e)) + }?; + DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(RuntimeApiDipProofError::MerkleProofError) } } } diff --git a/nodes/standalone/src/command.rs b/nodes/standalone/src/command.rs index 1c813ecc08..378cc50fec 100644 --- a/nodes/standalone/src/command.rs +++ b/nodes/standalone/src/command.rs @@ -16,12 +16,12 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use crate::service::ExecutorDispatch; use crate::{ benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder}, chain_spec, cli::{Cli, Subcommand}, service, + service::ExecutorDispatch, }; use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; diff --git a/nodes/standalone/src/rpc.rs b/nodes/standalone/src/rpc.rs index 6a164d9ce0..44baefbe65 100644 --- a/nodes/standalone/src/rpc.rs +++ b/nodes/standalone/src/rpc.rs @@ -72,7 +72,8 @@ where // Extend this RPC with a custom API by using the following syntax. // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. - // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` + // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, + // ...)))?;` Ok(module) } diff --git a/pallets/attestation/src/benchmarking.rs b/pallets/attestation/src/benchmarking.rs index 930c5f89c8..821f005a53 100644 --- a/pallets/attestation/src/benchmarking.rs +++ b/pallets/attestation/src/benchmarking.rs @@ -18,8 +18,7 @@ use frame_benchmarking::{account, benchmarks}; use frame_support::traits::{fungible::Mutate, Get}; -use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use sp_runtime::traits::Hash; use ctype::CtypeEntryOf; diff --git a/pallets/delegation/src/benchmarking.rs b/pallets/delegation/src/benchmarking.rs index 19a72acb6a..f5c8902533 100644 --- a/pallets/delegation/src/benchmarking.rs +++ b/pallets/delegation/src/benchmarking.rs @@ -27,8 +27,7 @@ use frame_support::{ Get, }, }; -use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use parity_scale_codec::Encode; use sp_core::{offchain::KeyTypeId, sr25519}; use sp_io::crypto::sr25519_generate; diff --git a/pallets/did/src/benchmarking.rs b/pallets/did/src/benchmarking.rs index a1fa993728..c11f98a8d6 100644 --- a/pallets/did/src/benchmarking.rs +++ b/pallets/did/src/benchmarking.rs @@ -22,8 +22,7 @@ use frame_support::{ assert_ok, traits::fungible::{Inspect, Mutate, MutateHold}, }; -use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use parity_scale_codec::Encode; use sp_core::{crypto::KeyTypeId, ecdsa, ed25519, sr25519}; use sp_io::crypto::{ecdsa_generate, ecdsa_sign, ed25519_generate, ed25519_sign, sr25519_generate, sr25519_sign}; diff --git a/pallets/pallet-did-lookup/src/benchmarking.rs b/pallets/pallet-did-lookup/src/benchmarking.rs index 688c0215d2..292f3ad7a6 100644 --- a/pallets/pallet-did-lookup/src/benchmarking.rs +++ b/pallets/pallet-did-lookup/src/benchmarking.rs @@ -27,8 +27,7 @@ use frame_support::{ Get, }, }; -use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use sha3::{Digest, Keccak256}; use sp_io::crypto::{ecdsa_generate, ed25519_generate, sr25519_generate}; use sp_runtime::{ diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index cb0e49f801..c513bbc130 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -66,11 +66,13 @@ pub mod pallet { type IdentityProof: Parameter; /// The details stored in this pallet associated with any given subject. type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; + type ProofVerificationError: Into; /// The logic of the proof verifier, called upon each execution of the /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier< ::RuntimeCall, Self::Identifier, + Error = Self::ProofVerificationError, Proof = Self::IdentityProof, IdentityDetails = Self::LocalIdentityInfo, Submitter = ::AccountId, @@ -90,9 +92,9 @@ pub mod pallet { /// An identity with the provided identifier could not be found. IdentityNotFound, /// The identity proof provided could not be successfully verified. - InvalidProof, - /// The specified call could not be dispatched. - Dispatch, + InvalidProof(u16), + /// The specified call is filtered by the DIP call origin filter. + Filtered, } /// The origin this pallet creates after a user has provided a valid @@ -116,8 +118,7 @@ pub mod pallet { // TODO: Make origin check configurable, and require that it at least returns // the submitter's account. let submitter = ensure_signed(origin)?; - // TODO: Proper error handling - ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); + ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Filtered); let mut identity_entry = IdentityEntries::::get(&identifier); let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( &*call, @@ -125,20 +126,16 @@ pub mod pallet { &submitter, &mut identity_entry, proof, - ); - // Write the identity info to storage after it has optionally been updated by - // the `ProofVerifier`, regardless of whether the proof has been verified or - // not. + ) + .map_err(|e| Error::::InvalidProof(e.into()))?; IdentityEntries::::mutate(&identifier, |entry| *entry = identity_entry); - // Unwrap the result if `ok`. - let proof_verification_result = proof_verification_result.map_err(|_| Error::::InvalidProof)?; let did_origin = DipOrigin { identifier, account_address: submitter, details: proof_verification_result, }; // TODO: Use dispatch info for weight calculation - let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; + let _ = call.dispatch(did_origin.into()).map_err(|e| e.error)?; Ok(()) } } diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 5171084147..ad17b78e4e 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -48,9 +48,12 @@ pub mod pallet { type IdentityCommitmentGenerator: IdentityCommitmentGenerator< Self::Identifier, IdentityOf, + Error = Self::IdentityCommitmentGeneratorError, Output = Self::IdentityCommitment, >; - type IdentityProvider: IdentityProvider; + type IdentityCommitmentGeneratorError: Into; + type IdentityProvider: IdentityProvider; + type IdentityProviderError: Into; type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -77,8 +80,8 @@ pub mod pallet { #[pallet::error] pub enum Error { - IdentityNotFound, - IdentityCommitmentGeneration, + IdentityProvider(u16), + IdentityCommitmentGenerator(u16), } #[pallet::call] @@ -94,9 +97,9 @@ pub mod pallet { let identity_commitment: Option = match T::IdentityProvider::retrieve(&identifier) { Ok(Some(identity)) => T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity) .map(Some) - .map_err(|_| Error::::IdentityCommitmentGeneration), + .map_err(|error| Error::::IdentityCommitmentGenerator(error.into())), Ok(None) => Ok(None), - Err(_) => Err(Error::::IdentityNotFound), + Err(error) => Err(Error::::IdentityProvider(error.into())), }?; if let Some(commitment) = identity_commitment { @@ -111,5 +114,7 @@ pub mod pallet { Ok(()) } + // TODO: Add extrinsic to remove commitment without requiring the identity to be + // deleted. } } diff --git a/pallets/pallet-migration/src/lib.rs b/pallets/pallet-migration/src/lib.rs index abc074ad65..90ac062660 100644 --- a/pallets/pallet-migration/src/lib.rs +++ b/pallets/pallet-migration/src/lib.rs @@ -39,8 +39,7 @@ pub mod pallet { traits::{fungible::Inspect, Currency, ReservableCurrency}, }; use frame_system::pallet_prelude::*; - use sp_runtime::traits::Hash; - use sp_runtime::SaturatedConversion; + use sp_runtime::{traits::Hash, SaturatedConversion}; use attestation::{Attestations, ClaimHashOf}; use delegation::{DelegationNodeIdOf, DelegationNodes}; diff --git a/pallets/pallet-migration/src/test.rs b/pallets/pallet-migration/src/test.rs index 5cc1374f7e..7b70e8e6a1 100644 --- a/pallets/pallet-migration/src/test.rs +++ b/pallets/pallet-migration/src/test.rs @@ -292,7 +292,8 @@ fn check_attempt_to_migrate_already_migrated_keys() { requested_migrations.clone() )); - // Since the keys are already migrated, a second attempt should have not affect to the storage. + // Since the keys are already migrated, a second attempt should have not affect + // to the storage. assert_storage_noop!( Migration::update_balance(RuntimeOrigin::signed(ACCOUNT_00), requested_migrations) .expect("Update balance should not panic") diff --git a/pallets/pallet-web3-names/src/benchmarking.rs b/pallets/pallet-web3-names/src/benchmarking.rs index 3954a3d637..608775860a 100644 --- a/pallets/pallet-web3-names/src/benchmarking.rs +++ b/pallets/pallet-web3-names/src/benchmarking.rs @@ -27,8 +27,7 @@ use frame_support::{ }, BoundedVec, }; -use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use sp_runtime::app_crypto::sr25519; use kilt_support::{traits::GenerateBenchmarkOrigin, Deposit}; diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index c36def60c0..095bed7e4e 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -143,7 +143,6 @@ pub mod pallet { pub use crate::inflation::{InflationInfo, RewardRate, StakingInfo}; use core::cmp::Ordering; - use frame_support::traits::BuildGenesisConfig; use frame_support::{ pallet_prelude::*, storage::bounded_btree_map::BoundedBTreeMap, @@ -153,7 +152,7 @@ pub mod pallet { fungible::{Inspect, MutateFreeze, Unbalanced}, Fortitude, Precision, Preservation, }, - EstimateNextSessionRotation, Get, OnUnbalanced, StorageVersion, + BuildGenesisConfig, EstimateNextSessionRotation, Get, OnUnbalanced, StorageVersion, }, BoundedVec, }; @@ -2513,8 +2512,8 @@ pub mod pallet { /// 2. In hook new_session: Read the current top n candidates from the /// TopCandidates and assign this set to author blocks for the next /// session. - /// 3. AuRa queries the authorities from the session pallet for - /// this session and picks authors on round-robin-basis from list of + /// 3. AuRa queries the authorities from the session pallet for this + /// session and picks authors on round-robin-basis from list of /// authorities. fn new_session(new_index: SessionIndex) -> Option> { log::debug!( diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 1bd6194e17..f4255c438b 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -24,16 +24,32 @@ use kilt_dip_support::{ }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; use sp_std::{marker::PhantomData, vec::Vec}; +#[derive(Encode, Decode, TypeInfo)] +pub enum DidIdentityProviderError { + DidNotFound, + Internal, +} + +impl From for u16 { + fn from(value: DidIdentityProviderError) -> Self { + match value { + DidIdentityProviderError::DidNotFound => 0, + DidIdentityProviderError::Internal => u16::MAX, + } + } +} + pub struct DidIdentityProvider(PhantomData); impl IdentityProvider for DidIdentityProvider where T: did::Config, { - // TODO: Proper error handling - type Error = (); + type Error = DidIdentityProviderError; type Success = DidDetails; fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { @@ -43,7 +59,7 @@ where ) { (Some(details), _) => Ok(Some(details)), (_, Some(_)) => Ok(None), - _ => Err(()), + _ => Err(DidIdentityProviderError::DidNotFound), } } } @@ -56,8 +72,7 @@ impl IdentityProvider for DidWeb3NameProvider where T: pallet_web3_names::Config, { - // TODO: Proper error handling - type Error = (); + type Error = DidIdentityProviderError; type Success = Web3OwnershipOf; fn retrieve(identifier: &T::Web3NameOwner) -> Result, Self::Error> { @@ -65,7 +80,11 @@ where return Ok(None); }; let Some(details) = pallet_web3_names::Pallet::::owner(&web3_name) else { - return Err(()); + log::error!( + "Inconsistent reverse map pallet_web3_names::owner(web3_name). Cannot find owner for web3name {:#?}", + web3_name + ); + return Err(DidIdentityProviderError::Internal); }; Ok(Some(Web3OwnershipOf:: { web3_name, @@ -80,8 +99,7 @@ impl IdentityProvider for DidLinkedAccountsProvider where T: pallet_did_lookup::Config, { - // TODO: Proper error handling - type Error = (); + type Error = DidIdentityProviderError; type Success = Vec; fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 3f485f3be2..0059d267ae 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . // If you feel like getting in touch with us, you can do so at info@botlabs.org - use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; use frame_system::pallet_prelude::BlockNumberFor; @@ -49,6 +48,27 @@ pub struct CompleteMerkleProof { pub proof: Proof, } +#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo, PartialEq)] +pub enum DidMerkleProofError { + DidNotFound, + KeyNotFound, + LinkedAccountNotFound, + Web3NameNotFound, + Internal, +} + +impl From for u16 { + fn from(value: DidMerkleProofError) -> Self { + match value { + DidMerkleProofError::DidNotFound => 0, + DidMerkleProofError::KeyNotFound => 1, + DidMerkleProofError::LinkedAccountNotFound => 2, + DidMerkleProofError::Web3NameNotFound => 3, + DidMerkleProofError::Internal => u16::MAX, + } + } +} + pub struct DidMerkleRootGenerator(PhantomData); type ProofLeafOf = RevealedDidMerkleProofLeaf< @@ -74,20 +94,25 @@ where // A valid proof will contain a leaf with the key details for each reference // leaf, with multiple reference leaves potentially referring to the same // details leaf, as we already do with out `DidDetails` type. - fn calculate_root_with_db(identity: &LinkedDidInfoOf, db: &mut MemoryDB) -> Result { + fn calculate_root_with_db( + identity: &LinkedDidInfoOf, + db: &mut MemoryDB, + ) -> Result { // Fails if the DID details do not exist. let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { - return Err(()); + return Err(DidMerkleProofError::DidNotFound); }; let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); // Authentication key - // TODO: No panic let auth_key_details = did_details .public_keys .get(&did_details.authentication_key) - .expect("Authentication key should be part of the public keys."); + .ok_or_else(|| { + log::error!("Authentication key should be part of the public keys."); + DidMerkleProofError::Internal + })?; let auth_leaf = ProofLeafOf::::DidKey( DidKeyMerkleKey( did_details.authentication_key, @@ -97,51 +122,75 @@ where ); trie_builder .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert authentication key in the trie builder. Authentication leaf: {:#?}", + auth_leaf + ); + DidMerkleProofError::Internal + })?; // Attestation key, if present if let Some(att_key_id) = did_details.attestation_key { - let att_key_details = did_details - .public_keys - .get(&att_key_id) - .expect("Attestation key should be part of the public keys."); + let att_key_details = did_details.public_keys.get(&att_key_id).ok_or_else(|| { + log::error!("Attestation key should be part of the public keys."); + DidMerkleProofError::Internal + })?; let att_leaf = ProofLeafOf::::DidKey( (att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into(), att_key_details.clone().into(), ); trie_builder .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert attestation key in the trie builder. Attestation leaf: {:#?}", + att_leaf + ); + DidMerkleProofError::Internal + })?; }; // Delegation key, if present if let Some(del_key_id) = did_details.delegation_key { - let del_key_details = did_details - .public_keys - .get(&del_key_id) - .expect("Delegation key should be part of the public keys."); + let del_key_details = did_details.public_keys.get(&del_key_id).ok_or_else(|| { + log::error!("Delegation key should be part of the public keys."); + DidMerkleProofError::Internal + })?; let del_leaf = ProofLeafOf::::DidKey( (del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into(), del_key_details.clone().into(), ); trie_builder .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert delegation key in the trie builder. Delegation leaf: {:#?}", + del_leaf + ); + DidMerkleProofError::Internal + })?; }; // Key agreement keys did_details .key_agreement_keys .iter() - .try_for_each(|id| -> Result<(), ()> { - let key_agreement_details = did_details - .public_keys - .get(id) - .expect("Key agreement key should be part of the public keys."); + .try_for_each(|id| -> Result<(), DidMerkleProofError> { + let key_agreement_details = did_details.public_keys.get(id).ok_or_else(|| { + log::error!("Key agreement key should be part of the public keys."); + DidMerkleProofError::Internal + })?; let enc_leaf = ProofLeafOf::::DidKey( (*id, DidKeyRelationship::Encryption).into(), key_agreement_details.clone().into(), ); trie_builder .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert key agreement key in the trie builder. Key agreement leaf: {:#?}", + enc_leaf + ); + DidMerkleProofError::Internal + })?; Ok(()) })?; @@ -149,14 +198,20 @@ where if let Some(linked_accounts) = linked_accounts { linked_accounts .iter() - .try_for_each(|linked_account| -> Result<(), ()> { + .try_for_each(|linked_account| -> Result<(), DidMerkleProofError> { let linked_account_leaf = ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); trie_builder .insert( linked_account_leaf.encoded_key().as_slice(), linked_account_leaf.encoded_value().as_slice(), ) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert linked account in the trie builder. Linked account leaf: {:#?}", + linked_account_leaf + ); + DidMerkleProofError::Internal + })?; Ok(()) })?; } @@ -172,14 +227,19 @@ where web3_name_leaf.encoded_key().as_slice(), web3_name_leaf.encoded_value().as_slice(), ) - .map_err(|_| ())?; + .map_err(|_| { + log::error!( + "Failed to insert web3name in the trie builder. Web3name leaf: {:#?}", + web3_name_leaf + ); + DidMerkleProofError::Internal + })?; } trie_builder.commit(); Ok(trie_builder.root().to_owned()) } - // TODO: Better error handling // Only used for testing and as part of the features exposed by the runtime API // of the provider. Given the provided `DidDetails` and a list of key IDs, it // generates a merkle proof which only reveals the details of the provided key @@ -190,22 +250,25 @@ where key_ids: K, should_include_web3_name: bool, account_ids: A, - ) -> Result>, ()> + ) -> Result>, DidMerkleProofError> where K: Iterator>, A: Iterator, { // Fails if the DID details do not exist. let (Some(did_details), linked_web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { - return Err(()); + return Err(DidMerkleProofError::DidNotFound); }; let mut db = MemoryDB::default(); let root = Self::calculate_root_with_db(identity, &mut db)?; let mut leaves = key_ids - .map(|key_id| -> Result, ()> { - let key_details = did_details.public_keys.get(key_id).ok_or(())?; + .map(|key_id| -> Result<_, DidMerkleProofError> { + let key_details = did_details + .public_keys + .get(key_id) + .ok_or(DidMerkleProofError::KeyNotFound)?; // Create the merkle leaf key depending on the relationship of the key to the // DID document. let did_key_merkle_key: DidKeyMerkleKey> = if *key_id == did_details.authentication_key { @@ -217,16 +280,19 @@ where } else if did_details.key_agreement_keys.contains(key_id) { Ok((*key_id, DidKeyRelationship::Encryption).into()) } else { - Err(()) + log::error!("Unknown key ID {:#?} retrieved from DID details.", key_id); + Err(DidMerkleProofError::Internal) }?; Ok(RevealedDidMerkleProofLeaf::DidKey( did_key_merkle_key, key_details.clone().into(), )) }) - .chain(account_ids.map(|account_id| -> Result, ()> { + .chain(account_ids.map(|account_id| -> Result<_, DidMerkleProofError> { let Some(linked_accounts) = linked_accounts else { - return Err(()); + // Directly LinkedAccountNotFound since there's no linked accounts to check + // against. + return Err(DidMerkleProofError::LinkedAccountNotFound); }; if linked_accounts.contains(account_id) { Ok(RevealedDidMerkleProofLeaf::LinkedAccount( @@ -234,7 +300,7 @@ where ().into(), )) } else { - Err(()) + Err(DidMerkleProofError::LinkedAccountNotFound) } })) .collect::, _>>()?; @@ -249,13 +315,19 @@ where Ok(()) } // ...else if web3name should be included and it DOES NOT exist... - (true, None) => Err(()), - // ...else if web3name should NOT be included. + (true, None) => Err(DidMerkleProofError::Web3NameNotFound), + // ...else (if web3name should NOT be included). (false, _) => Ok(()), }?; let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); - let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; + let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| { + log::error!( + "Failed to generate a merkle proof for the encoded keys: {:#?}", + encoded_keys + ); + DidMerkleProofError::Internal + })?; Ok(CompleteMerkleProof { root, proof: DidMerkleProofOf:: { @@ -270,8 +342,7 @@ impl IdentityCommitmentGenerator> for DidMe where T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { - // TODO: Proper error handling - type Error = (); + type Error = DidMerkleProofError; type Output = T::Hash; fn generate_commitment(_identifier: &DidIdentifier, identity: &LinkedDidInfoOf) -> Result { From 7ffaac4f1cf89a5ac196f01c6c4424cb9e47062e Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 31 Oct 2023 13:44:38 +0000 Subject: [PATCH 13/28] feat: add versioning support (#573) Fixes https://github.com/KILTprotocol/ticket/issues/2971 on top of https://github.com/KILTprotocol/kilt-node/pull/572. This PR adds support for versioning, by introducing support for the following: 1. [PROVIDER PALLET] Generate an identity commitment for a specific version 2. [PROVIDER PALLET] Delete a previously-created identity commitment of a specific version 3. [PROVIDER RUNTIME API] Generate a DIP proof for a specific version 4. [CONSUMER] Add `Versioned*` types for DIP verifiers and DIP proofs running on both a sibling parachain or the parent relaychain --- Cargo.lock | 1 + crates/kilt-dip-support/src/export/child.rs | 553 ++++++++++++++++ crates/kilt-dip-support/src/export/common.rs | 42 ++ crates/kilt-dip-support/src/export/mod.rs | 34 + crates/kilt-dip-support/src/export/sibling.rs | 497 +++++++++++++++ crates/kilt-dip-support/src/lib.rs | 596 +----------------- crates/kilt-dip-support/src/state_proofs.rs | 8 +- crates/kilt-dip-support/src/traits.rs | 7 +- dip-template/runtimes/dip-consumer/src/dip.rs | 4 +- dip-template/runtimes/dip-provider/src/dip.rs | 3 +- dip-template/runtimes/dip-provider/src/lib.rs | 3 +- pallets/pallet-dip-provider/Cargo.toml | 1 + pallets/pallet-dip-provider/src/lib.rs | 98 ++- pallets/pallet-dip-provider/src/traits.rs | 14 +- runtimes/common/src/dip/merkle.rs | 160 +++-- 15 files changed, 1332 insertions(+), 689 deletions(-) create mode 100644 crates/kilt-dip-support/src/export/child.rs create mode 100644 crates/kilt-dip-support/src/export/common.rs create mode 100644 crates/kilt-dip-support/src/export/mod.rs create mode 100644 crates/kilt-dip-support/src/export/sibling.rs diff --git a/Cargo.lock b/Cargo.lock index a5aef17c56..8915054d6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6491,6 +6491,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", + "sp-io", "sp-std", ] diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs new file mode 100644 index 0000000000..c238080099 --- /dev/null +++ b/crates/kilt-dip-support/src/export/child.rs @@ -0,0 +1,553 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; +use pallet_dip_consumer::traits::IdentityProofVerifier; +use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; +use scale_info::TypeInfo; +use sp_core::{RuntimeDebug, U256}; +use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedSub, Get, Hash, MaybeDisplay, Member, SimpleBitOps}; +use sp_std::{marker::PhantomData, vec::Vec}; + +use crate::{ + did::RevealedDidKeysSignatureAndCallVerifierError, + merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, + traits::{ + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, + RelayChainStorageInfo, + }, + utils::OutputOf, +}; + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +#[non_exhaustive] +pub enum VersionedChildParachainDipStateProof< + ParentBlockHeight: Copy + Into + TryFrom, + ParentBlockHasher: Hash, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, +> { + V0( + v0::ChildParachainDipStateProof< + ParentBlockHeight, + ParentBlockHasher, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + >, + ), +} + +pub enum DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, +> { + UnsupportedVersion, + InvalidBlockHeight, + InvalidBlockHash, + ParachainHeadMerkleProof(ParachainHeadMerkleProofVerificationError), + IdentityCommitmentMerkleProof(IdentityCommitmentMerkleProofVerificationError), + DipProof(DipProofVerificationError), + DidSignature(DidSignatureVerificationError), +} + +impl< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + > + From< + DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + > for u16 +where + ParachainHeadMerkleProofVerificationError: Into, + IdentityCommitmentMerkleProofVerificationError: Into, + DipProofVerificationError: Into, + DidSignatureVerificationError: Into, +{ + fn from( + value: DipChildProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + ) -> Self { + match value { + DipChildProviderStateProofVerifierError::UnsupportedVersion => 0, + DipChildProviderStateProofVerifierError::InvalidBlockHeight => 1, + DipChildProviderStateProofVerifierError::InvalidBlockHash => 2, + DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + u8::MAX as u16 + error.into() as u16 + } + DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + u8::MAX as u16 * 2 + error.into() as u16 + } + DipChildProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipChildProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + } + } +} + +pub struct VersionedDipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, +); + +impl< + Call, + Subject, + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for VersionedDipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainInfo: RelayChainStorageInfo + + HistoricalBlockRegistry< + BlockNumber = ::BlockNumber, + Hasher = ::Hasher, + >, + OutputOf<::Hasher>: + Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::BlockNumber: Copy + + Into + + TryFrom + + HasCompact + + Member + + sp_std::hash::Hash + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec, + RelayChainInfo::Key: AsRef<[u8]>, + + ChildProviderParachainId: Get, + + ChildProviderStateInfo: ProviderParachainStateInfo, + OutputOf: Ord + From::Hasher>>, + ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::Commitment: Decode, + ChildProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: + DidSignatureVerifierContext::BlockNumber>, + LocalContextProvider::BlockNumber: CheckedSub + From, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: + DipCallOriginFilter, DidVerificationKeyRelationship)>, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderAccountId: Encode + Clone, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, +{ + type Error = DipChildProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type IdentityDetails = LocalDidDetails; + type Proof = VersionedChildParachainDipStateProof< + ::BlockNumber, + ::Hasher, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ProviderAccountId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + match proof { + VersionedChildParachainDipStateProof::V0(v0_proof) => { + v0::DipChildProviderStateProofVerifier::< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + >::verify_proof_for_call_against_details(call, subject, submitter, identity_details, v0_proof) + } + } + } +} + +pub mod latest { + pub use super::v0::ChildParachainDipStateProof; +} + +mod v0 { + use super::*; + + use parity_scale_codec::Codec; + use sp_runtime::{ + generic::Header, + traits::{AtLeast32BitUnsigned, Hash, MaybeDisplay, Member, SimpleBitOps}, + }; + use sp_std::{borrow::Borrow, vec::Vec}; + + use crate::{ + did::{ + RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, + RevealedDidKeysSignatureAndCallVerifierError, + }, + export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, + merkle::{ + DidMerkleProofVerifier, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, + RevealedDidMerkleProofLeaves, + }, + state_proofs::{ + parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, + relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError}, + }, + traits::{ + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, + ProviderParachainStateInfo, RelayChainStorageInfo, + }, + utils::OutputOf, + }; + + #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] + pub struct ChildParachainDipStateProof< + ParentBlockHeight: Copy + Into + TryFrom, + ParentBlockHasher: Hash, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + > { + para_state_root: ParachainRootStateProof, + relay_header: Header, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, + } + + pub struct DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + >( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, + ); + + impl< + Call, + Subject, + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainInfo: RelayChainStorageInfo + + HistoricalBlockRegistry< + BlockNumber = ::BlockNumber, + Hasher = ::Hasher, + >, + OutputOf<::Hasher>: + Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::BlockNumber: Copy + + Into + + TryFrom + + HasCompact + + Member + + sp_std::hash::Hash + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec, + RelayChainInfo::Key: AsRef<[u8]>, + + ChildProviderParachainId: Get, + + ChildProviderStateInfo: + ProviderParachainStateInfo, + OutputOf: + Ord + From::Hasher>>, + ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::Commitment: Decode, + ChildProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: + DidSignatureVerifierContext::BlockNumber>, + LocalContextProvider::BlockNumber: CheckedSub + From, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter< + Call, + OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + >, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderAccountId: Encode + Clone, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, + { + type Error = DipChildProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type IdentityDetails = LocalDidDetails; + type Proof = ChildParachainDipStateProof< + ::BlockNumber, + ::Hasher, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ProviderAccountId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + // 1. Retrieve block hash from provider at the proof height + let block_hash_at_height = RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height) + .ok_or(DipChildProviderStateProofVerifierError::InvalidBlockHeight)?; + + // 1.1 Verify that the provided header hashes to the same block has retrieved + if block_hash_at_height != proof.relay_header.hash() { + return Err(DipChildProviderStateProofVerifierError::InvalidBlockHash); + } + // 1.2 If so, extract the state root from the header + let state_root_at_height = proof.relay_header.state_root; + + // FIXME: Compilation error + // 2. Verify relay chain proof + let provider_parachain_header = + ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( + &ChildProviderParachainId::get(), + &state_root_at_height, + proof.para_state_root.proof, + ) + .map_err(DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + + // 3. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + ) + .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + + // 4. Verify DIP merkle proof. + let proof_leaves = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + .map_err(DipChildProviderStateProofVerifierError::DipProof)?; + + // 5. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( + call, + submitter, + identity_details, + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + ) + .map_err(DipChildProviderStateProofVerifierError::DidSignature)?; + Ok(proof_leaves) + } + } +} diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-support/src/export/common.rs new file mode 100644 index 0000000000..d0eff66d68 --- /dev/null +++ b/crates/kilt-dip-support/src/export/common.rs @@ -0,0 +1,42 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +pub mod latest { + pub use super::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}; +} + +pub mod v0 { + use parity_scale_codec::{Decode, Encode}; + use scale_info::TypeInfo; + use sp_core::RuntimeDebug; + use sp_std::vec::Vec; + + use crate::{did::TimeBoundDidSignature, merkle::DidMerkleProof}; + + #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] + pub struct ParachainRootStateProof { + pub(crate) relay_block_height: RelayBlockHeight, + pub(crate) proof: Vec>, + } + + #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] + pub struct DipMerkleProofAndDidSignature { + pub(crate) leaves: DidMerkleProof, + pub(crate) signature: TimeBoundDidSignature, + } +} diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-support/src/export/mod.rs new file mode 100644 index 0000000000..c6a92e7d5b --- /dev/null +++ b/crates/kilt-dip-support/src/export/mod.rs @@ -0,0 +1,34 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +mod child; +mod common; +mod sibling; + +pub use child::{ + DipChildProviderStateProofVerifierError, VersionedChildParachainDipStateProof, + VersionedDipChildProviderStateProofVerifier, +}; +pub use sibling::{ + DipSiblingProviderStateProofVerifierError, VersionedDipSiblingProviderStateProofVerifier, + VersionedSiblingParachainDipStateProof, +}; + +pub mod latest { + pub use super::{child::latest::*, common::latest::*, sibling::latest::*}; +} diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs new file mode 100644 index 0000000000..e0b3154c54 --- /dev/null +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -0,0 +1,497 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; +use pallet_dip_consumer::traits::IdentityProofVerifier; +use parity_scale_codec::{Decode, Encode, HasCompact}; +use scale_info::TypeInfo; +use sp_core::{RuntimeDebug, U256}; +use sp_runtime::traits::{CheckedSub, Get}; +use sp_std::{marker::PhantomData, vec::Vec}; + +use crate::{ + did::RevealedDidKeysSignatureAndCallVerifierError, + merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, + traits::{self, Bump, DidSignatureVerifierContext, DipCallOriginFilter}, + utils::OutputOf, +}; + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +#[non_exhaustive] +pub enum VersionedSiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, +> { + V0( + v0::SiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, + >, + ), +} + +pub enum DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, +> { + UnsupportedVersion, + ParachainHeadMerkleProof(ParachainHeadMerkleProofVerificationError), + IdentityCommitmentMerkleProof(IdentityCommitmentMerkleProofVerificationError), + DipProof(DipProofVerificationError), + DidSignature(DidSignatureVerificationError), +} + +impl< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + > + From< + DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + > for u16 +where + ParachainHeadMerkleProofVerificationError: Into, + IdentityCommitmentMerkleProofVerificationError: Into, + DipProofVerificationError: Into, + DidSignatureVerificationError: Into, +{ + fn from( + value: DipSiblingProviderStateProofVerifierError< + ParachainHeadMerkleProofVerificationError, + IdentityCommitmentMerkleProofVerificationError, + DipProofVerificationError, + DidSignatureVerificationError, + >, + ) -> Self { + match value { + DipSiblingProviderStateProofVerifierError::UnsupportedVersion => 0, + DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + u8::MAX as u16 + error.into() as u16 + } + DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + u8::MAX as u16 * 2 + error.into() as u16 + } + DipSiblingProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipSiblingProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + } + } +} + +pub struct VersionedDipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, +); + +impl< + Call, + Subject, + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for VersionedDipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, + OutputOf: Ord, + RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::Key: AsRef<[u8]>, + + SiblingProviderParachainId: Get, + + SiblingProviderStateInfo: + traits::ProviderParachainStateInfo, + OutputOf: Ord + From>, + SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::Commitment: Decode, + SiblingProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: DidSignatureVerifierContext, + LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: + DipCallOriginFilter, DidVerificationKeyRelationship)>, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderAccountId: Encode + Clone, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, +{ + type Error = DipSiblingProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type IdentityDetails = LocalDidDetails; + type Proof = VersionedSiblingParachainDipStateProof< + RelayChainStateInfo::BlockNumber, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + LocalContextProvider::BlockNumber, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ProviderAccountId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + match proof { + VersionedSiblingParachainDipStateProof::V0(v0_proof) => { + v0::DipSiblingProviderStateProofVerifier::< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + >::verify_proof_for_call_against_details(call, subject, submitter, identity_details, v0_proof) + } + } + } +} + +pub mod latest { + pub use super::v0::SiblingParachainDipStateProof; +} + +mod v0 { + use super::*; + + use sp_std::borrow::Borrow; + + use crate::{ + did::{RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier}, + export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, + merkle::DidMerkleProofVerifier, + state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, + traits::ProviderParachainStateInfo, + }; + + #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] + pub struct SiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, + > { + para_state_root: ParachainRootStateProof, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, + } + + #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] + pub struct DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + >( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, + ); + + impl< + Call, + Subject, + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderAccountId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, + OutputOf: Ord, + RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::Key: AsRef<[u8]>, + + SiblingProviderParachainId: Get, + + SiblingProviderStateInfo: + traits::ProviderParachainStateInfo, + OutputOf: Ord + From>, + SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::Commitment: Decode, + SiblingProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: DidSignatureVerifierContext, + LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter< + Call, + OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + >, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderAccountId: Encode + Clone, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, + { + type Error = DipSiblingProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type IdentityDetails = LocalDidDetails; + type Proof = SiblingParachainDipStateProof< + RelayChainStateInfo::BlockNumber, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + LocalContextProvider::BlockNumber, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ProviderAccountId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + // 1. Verify relay chain proof. + let provider_parachain_header = + ParachainHeadProofVerifier::::verify_proof_for_parachain( + &SiblingProviderParachainId::get(), + &proof.para_state_root.relay_block_height, + proof.para_state_root.proof, + ) + .map_err(DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + + // 2. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + ) + .map_err(DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + + // 3. Verify DIP merkle proof. + let proof_leaves: RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ProviderAccountId, + ::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + > = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + .map_err(DipSiblingProviderStateProofVerifierError::DipProof)?; + + // 4. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( + call, + submitter, + identity_details, + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + ) + .map_err(DipSiblingProviderStateProofVerifierError::DidSignature)?; + + Ok(proof_leaves) + } + } +} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 0d0e80425e..2daacfc640 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -20,603 +20,13 @@ #![cfg_attr(not(feature = "std"), no_std)] -use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; -use scale_info::TypeInfo; -use sp_core::{Get, RuntimeDebug, U256}; -use sp_runtime::{ - generic::Header, - traits::{AtLeast32BitUnsigned, CheckedSub, Hash, MaybeDisplay, Member, SimpleBitOps}, -}; -use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; - -use ::did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; -use pallet_dip_consumer::traits::IdentityProofVerifier; - -use crate::{ - did::{ - RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, - RevealedDidKeysSignatureAndCallVerifierError, TimeBoundDidSignature, - }, - merkle::{ - DidMerkleProof, DidMerkleProofVerifier, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, - RevealedDidMerkleProofLeaves, - }, - state_proofs::{ - parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, - relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError}, - }, - traits::{ - Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, - RelayChainStorageInfo, - }, - utils::OutputOf, -}; - pub mod did; pub mod merkle; pub mod state_proofs; pub mod traits; pub mod utils; -pub use state_proofs::relay_chain::RococoStateRootsViaRelayStorePallet; - -#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -pub struct SiblingParachainDipStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, -> { - para_state_root: ParachainRootStateProof, - dip_identity_commitment: Vec>, - did: DipMerkleProofAndDidSignature, -} - -#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] -pub struct ParachainRootStateProof { - relay_block_height: RelayBlockHeight, - proof: Vec>, -} - -#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -pub struct DipMerkleProofAndDidSignature { - leaves: DidMerkleProof, - signature: TimeBoundDidSignature, -} - -pub enum DipSiblingProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, -> { - ParachainHeadMerkleProofVerificationError(ParachainHeadMerkleProofVerificationError), - IdentityCommitmentMerkleProofVerificationError(IdentityCommitmentMerkleProofVerificationError), - DipProofVerificationError(DipProofVerificationError), - DidSignatureVerificationError(DidSignatureVerificationError), -} - -impl< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - > - From< - DipSiblingProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - > for u16 -where - ParachainHeadMerkleProofVerificationError: Into, - IdentityCommitmentMerkleProofVerificationError: Into, - DipProofVerificationError: Into, - DidSignatureVerificationError: Into, -{ - fn from( - value: DipSiblingProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - ) -> Self { - match value { - DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError(error) => { - error.into() as u16 - } - DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError(error) => { - u8::MAX as u16 + error.into() as u16 - } - DipSiblingProviderStateProofVerifierError::DipProofVerificationError(error) => { - u8::MAX as u16 * 2 + error.into() as u16 - } - DipSiblingProviderStateProofVerifierError::DidSignatureVerificationError(error) => { - u8::MAX as u16 * 3 + error.into() as u16 - } - } - } -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct DipSiblingProviderStateProofVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - )>, -); - -impl< - Call, - Subject, - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - > IdentityProofVerifier - for DipSiblingProviderStateProofVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - > where - Call: Encode, - TxSubmitter: Encode, - - RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, - OutputOf: Ord, - RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, - RelayChainStateInfo::Key: AsRef<[u8]>, - - SiblingProviderParachainId: Get, - - SiblingProviderStateInfo: - traits::ProviderParachainStateInfo, - OutputOf: Ord + From>, - SiblingProviderStateInfo::BlockNumber: Encode + Clone, - SiblingProviderStateInfo::Commitment: Decode, - SiblingProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: DidSignatureVerifierContext, - LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, - LocalContextProvider::Hash: Encode, - LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: - DipCallOriginFilter, DidVerificationKeyRelationship)>, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, -{ - type Error = DipSiblingProviderStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; - type IdentityDetails = LocalDidDetails; - type Proof = SiblingParachainDipStateProof< - RelayChainStateInfo::BlockNumber, - Vec>, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, - LocalContextProvider::BlockNumber, - >; - type Submitter = TxSubmitter; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, - proof: Self::Proof, - ) -> Result { - // 1. Verify relay chain proof. - let provider_parachain_header = ParachainHeadProofVerifier::::verify_proof_for_parachain( - &SiblingProviderParachainId::get(), - &proof.para_state_root.relay_block_height, - proof.para_state_root.proof, - ) - .map_err(DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError)?; - - // 2. Verify parachain state proof. - let subject_identity_commitment = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - subject, - provider_parachain_header.state_root.into(), - proof.dip_identity_commitment, - ) - .map_err(DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError)?; - - // 3. Verify DIP merkle proof. - let proof_leaves = DidMerkleProofVerifier::< - ProviderDipMerkleHasher, - _, - _, - _, - _, - _, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) - .map_err(DipSiblingProviderStateProofVerifierError::DipProofVerificationError)?; +mod export; - // 4. Verify DID signature. - RevealedDidKeysSignatureAndCallVerifier::< - _, - _, - _, - _, - LocalContextProvider, - _, - _, - _, - LocalDidCallVerifier, - >::verify_did_signature_for_call( - call, - submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, - ).map_err(DipSiblingProviderStateProofVerifierError::DidSignatureVerificationError)?; - - Ok(proof_leaves) - } -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct ChildParachainDipStateProof< - ParentBlockHeight: Copy + Into + TryFrom, - ParentBlockHasher: Hash, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, -> { - para_state_root: ParachainRootStateProof, - relay_header: Header, - dip_identity_commitment: Vec>, - did: DipMerkleProofAndDidSignature, -} - -pub enum DipChildProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, -> { - InvalidBlockHeight, - InvalidBlockHash, - ParachainHeadMerkleProofVerificationError(ParachainHeadMerkleProofVerificationError), - IdentityCommitmentMerkleProofVerificationError(IdentityCommitmentMerkleProofVerificationError), - DipProofVerificationError(DipProofVerificationError), - DidSignatureVerificationError(DidSignatureVerificationError), -} - -impl< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - > - From< - DipChildProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - > for u16 -where - ParachainHeadMerkleProofVerificationError: Into, - IdentityCommitmentMerkleProofVerificationError: Into, - DipProofVerificationError: Into, - DidSignatureVerificationError: Into, -{ - fn from( - value: DipChildProviderStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - ) -> Self { - match value { - DipChildProviderStateProofVerifierError::InvalidBlockHeight => 0, - DipChildProviderStateProofVerifierError::InvalidBlockHash => 1, - DipChildProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError(error) => { - u8::MAX as u16 + error.into() as u16 - } - DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError(error) => { - u8::MAX as u16 * 2 + error.into() as u16 - } - DipChildProviderStateProofVerifierError::DipProofVerificationError(error) => { - u8::MAX as u16 * 3 + error.into() as u16 - } - DipChildProviderStateProofVerifierError::DidSignatureVerificationError(error) => { - u8::MAX as u16 * 4 + error.into() as u16 - } - } - } -} - -pub struct DipChildProviderStateProofVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - )>, -); - -impl< - Call, - Subject, - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - > IdentityProofVerifier - for DipChildProviderStateProofVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - > where - Call: Encode, - TxSubmitter: Encode, - - RelayChainInfo: RelayChainStorageInfo - + HistoricalBlockRegistry< - BlockNumber = ::BlockNumber, - Hasher = ::Hasher, - >, - OutputOf<::Hasher>: - Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - ::BlockNumber: Copy - + Into - + TryFrom - + HasCompact - + Member - + sp_std::hash::Hash - + MaybeDisplay - + AtLeast32BitUnsigned - + Codec, - RelayChainInfo::Key: AsRef<[u8]>, - - ChildProviderParachainId: Get, - - ChildProviderStateInfo: ProviderParachainStateInfo, - OutputOf: Ord + From::Hasher>>, - ChildProviderStateInfo::BlockNumber: Encode + Clone, - ChildProviderStateInfo::Commitment: Decode, - ChildProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: - DidSignatureVerifierContext::BlockNumber>, - LocalContextProvider::BlockNumber: CheckedSub + From, - LocalContextProvider::Hash: Encode, - LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: - DipCallOriginFilter, DidVerificationKeyRelationship)>, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, -{ - type Error = DipChildProviderStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; - type IdentityDetails = LocalDidDetails; - type Proof = ChildParachainDipStateProof< - ::BlockNumber, - ::Hasher, - Vec>, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, - >; - type Submitter = TxSubmitter; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, - proof: Self::Proof, - ) -> Result { - // 1. Retrieve block hash from provider at the proof height - let block_hash_at_height = RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height) - .ok_or(DipChildProviderStateProofVerifierError::InvalidBlockHeight)?; - - // 1.1 Verify that the provided header hashes to the same block has retrieved - if block_hash_at_height != proof.relay_header.hash() { - return Err(DipChildProviderStateProofVerifierError::InvalidBlockHash); - } - // 1.2 If so, extract the state root from the header - let state_root_at_height = proof.relay_header.state_root; - - // FIXME: Compilation error - // 2. Verify relay chain proof - let provider_parachain_header = - ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( - &ChildProviderParachainId::get(), - &state_root_at_height, - proof.para_state_root.proof, - ) - .map_err(DipChildProviderStateProofVerifierError::ParachainHeadMerkleProofVerificationError)?; - - // 3. Verify parachain state proof. - let subject_identity_commitment = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - subject, - provider_parachain_header.state_root.into(), - proof.dip_identity_commitment, - ) - .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProofVerificationError)?; - - // 4. Verify DIP merkle proof. - let proof_leaves = DidMerkleProofVerifier::< - ProviderDipMerkleHasher, - _, - _, - _, - _, - _, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) - .map_err(DipChildProviderStateProofVerifierError::DipProofVerificationError)?; - - // 5. Verify DID signature. - RevealedDidKeysSignatureAndCallVerifier::< - _, - _, - _, - _, - LocalContextProvider, - _, - _, - _, - LocalDidCallVerifier, - >::verify_did_signature_for_call( - call, - submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, - ).map_err(DipChildProviderStateProofVerifierError::DidSignatureVerificationError)?; - Ok(proof_leaves) - } -} +pub use export::*; +pub use state_proofs::relay_chain::RococoStateRootsViaRelayStorePallet; diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 53099e6adb..1f4866ab85 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -302,7 +302,7 @@ pub(super) mod parachain { state_root: OutputOf, proof: impl IntoIterator>, ) -> Result { - let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier); + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); let storage_proof = StorageProof::new(proof); let revealed_leaves = read_proof_check::( state_root, @@ -328,6 +328,7 @@ pub(super) mod parachain { use super::*; use hex_literal::hex; + use pallet_dip_provider::IdentityCommitmentVersion; use sp_core::H256; use sp_runtime::traits::BlakeTwo256; @@ -345,7 +346,10 @@ pub(super) mod parachain { type Identifier = (); type Key = StorageKey; - fn dip_subject_storage_key(_identifier: &Self::Identifier) -> Self::Key { + fn dip_subject_storage_key( + _identifier: &Self::Identifier, + _version: IdentityCommitmentVersion, + ) -> Self::Key { // system::eventCount() raw storage key let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); StorageKey(storage_key) diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index e23174a542..d3c013b1b5 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -17,6 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use frame_system::pallet_prelude::BlockNumberFor; +use pallet_dip_provider::IdentityCommitmentVersion; use sp_core::storage::StorageKey; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; @@ -76,7 +77,7 @@ pub trait ProviderParachainStateInfo { type Hasher: sp_runtime::traits::Hash; type Identifier; - fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key; + fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key; } pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); @@ -91,9 +92,9 @@ where type Identifier = T::Identifier; type Key = StorageKey; - fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key { + fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key { StorageKey(pallet_dip_provider::IdentityCommitments::::hashed_key_for( - identifier, + identifier, version, )) } } diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 40eb0bf100..91ca198c50 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -21,7 +21,7 @@ use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as P use frame_support::traits::Contains; use kilt_dip_support::{ traits::{DipCallOriginFilter, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}, - DipSiblingProviderStateProofVerifier, RococoStateRootsViaRelayStorePallet, + RococoStateRootsViaRelayStorePallet, VersionedDipSiblingProviderStateProofVerifier, }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::traits::IdentityProofVerifier; @@ -32,7 +32,7 @@ use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; pub type MerkleProofVerifierOutputOf = >::VerificationResult; -pub type ProofVerifier = DipSiblingProviderStateProofVerifier< +pub type ProofVerifier = VersionedDipSiblingProviderStateProofVerifier< RococoStateRootsViaRelayStorePallet, ConstU32<2_000>, ProviderParachainStateInfoViaProviderPallet, diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index bd86831553..720304f30b 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -18,7 +18,7 @@ use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::traits::IdentityProvider; +use pallet_dip_provider::{traits::IdentityProvider, IdentityCommitmentVersion}; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{ did::LinkedDidInfoProviderOf, @@ -32,6 +32,7 @@ use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent}; #[derive(Encode, Decode, TypeInfo)] pub struct RuntimeApiDipProofRequest { pub(crate) identifier: DidIdentifier, + pub(crate) version: IdentityCommitmentVersion, pub(crate) keys: Vec>, pub(crate) accounts: Vec, pub(crate) should_include_web3_name: bool, diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 578fb1939b..cc8320d55e 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -568,7 +568,6 @@ impl_runtime_apis! { } } - // TODO: Support generating different versions of the proof, based on the provided parameter impl kilt_runtime_api_dip_provider::DipProvider>, RuntimeApiDipProofError> for Runtime { fn generate_proof(request: RuntimeApiDipProofRequest) -> Result>, RuntimeApiDipProofError> { let linked_did_info = match ::IdentityProvider::retrieve(&request.identifier) { @@ -576,7 +575,7 @@ impl_runtime_apis! { Ok(None) => Err(RuntimeApiDipProofError::IdentityNotFound), Err(e) => Err(RuntimeApiDipProofError::IdentityProviderError(e)) }?; - DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(RuntimeApiDipProofError::MerkleProofError) + DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(RuntimeApiDipProofError::MerkleProofError) } } } diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 5e22e357a1..8c078fbc52 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -19,6 +19,7 @@ frame-support.workspace = true frame-system.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} +sp-io.workspace = true sp-std.workspace = true [features] diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index ad17b78e4e..826fd170ac 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -31,14 +31,23 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; use parity_scale_codec::FullCodec; + use sp_io::MultiRemovalResults; use sp_std::fmt::Debug; use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, SubmitterInfo}; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; + pub type IdentityCommitmentVersion = u16; + pub const LATEST_COMMITMENT_VERSION: IdentityCommitmentVersion = 0; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + #[derive(Encode, Decode, RuntimeDebug, TypeInfo, Clone, PartialEq)] + pub enum VersionOrLimit { + Version(IdentityCommitmentVersion), + Limit(u32), + } + #[pallet::config] pub trait Config: frame_system::Config { type CommitOriginCheck: EnsureOrigin; @@ -59,8 +68,14 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn identity_commitments)] - pub type IdentityCommitments = - StorageMap<_, Twox64Concat, ::Identifier, ::IdentityCommitment>; + pub type IdentityCommitments = StorageDoubleMap< + _, + Twox64Concat, + ::Identifier, + Twox64Concat, + IdentityCommitmentVersion, + ::IdentityCommitment, + >; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -72,6 +87,11 @@ pub mod pallet { IdentityCommitted { identifier: T::Identifier, commitment: T::IdentityCommitment, + version: IdentityCommitmentVersion, + }, + VersionedIdentityDeleted { + identifier: T::Identifier, + version: IdentityCommitmentVersion, }, IdentityDeleted { identifier: T::Identifier, @@ -80,6 +100,8 @@ pub mod pallet { #[pallet::error] pub enum Error { + IdentityNotFound, + LimitTooLow, IdentityProvider(u16), IdentityCommitmentGenerator(u16), } @@ -89,32 +111,70 @@ pub mod pallet { #[pallet::call_index(0)] // TODO: Update weight #[pallet::weight(0)] - pub fn commit_identity(origin: OriginFor, identifier: T::Identifier) -> DispatchResult { + pub fn commit_identity( + origin: OriginFor, + identifier: T::Identifier, + version: Option, + ) -> DispatchResult { // TODO: use dispatcher to get deposit let _dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; - let identity_commitment: Option = match T::IdentityProvider::retrieve(&identifier) { - Ok(Some(identity)) => T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity) - .map(Some) - .map_err(|error| Error::::IdentityCommitmentGenerator(error.into())), - Ok(None) => Ok(None), + let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); + let commitment = match T::IdentityProvider::retrieve(&identifier) { + Ok(Some(identity)) => { + T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) + .map_err(|error| Error::::IdentityCommitmentGenerator(error.into())) + } + Ok(None) => Err(Error::::IdentityNotFound), Err(error) => Err(Error::::IdentityProvider(error.into())), }?; - if let Some(commitment) = identity_commitment { - // TODO: Take deposit (once 0.9.42 PR is merged into develop) - IdentityCommitments::::insert(&identifier, commitment.clone()); - Self::deposit_event(Event::::IdentityCommitted { identifier, commitment }); - } else { - // TODO: Release deposit (once 0.9.42 PR is merged into develop) - IdentityCommitments::::remove(&identifier); - Self::deposit_event(Event::::IdentityDeleted { identifier }); - } + // TODO: Take deposit (once 0.9.42 PR is merged into develop) + IdentityCommitments::::insert(&identifier, commitment_version, commitment.clone()); + Self::deposit_event(Event::::IdentityCommitted { + identifier, + commitment, + version: commitment_version, + }); + Ok(()) + } + + #[pallet::call_index(1)] + // TODO: Update weight + #[pallet::weight(0)] + pub fn delete_identity_commitment( + origin: OriginFor, + identifier: T::Identifier, + version_or_limit: VersionOrLimit, + ) -> DispatchResult { + let _dispatcher = + T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; + match version_or_limit { + VersionOrLimit::Version(version) => { + let commitment = IdentityCommitments::::take(&identifier, version); + match commitment { + Some(_) => Err(Error::::IdentityNotFound), + None => { + Self::deposit_event(Event::::VersionedIdentityDeleted { identifier, version }); + Ok(()) + } + } + } + VersionOrLimit::Limit(limit) => { + let MultiRemovalResults { maybe_cursor, .. } = + IdentityCommitments::::clear_prefix(&identifier, limit, None); + match maybe_cursor { + Some(_) => Err(Error::::LimitTooLow), + None => { + Self::deposit_event(Event::::IdentityDeleted { identifier }); + Ok(()) + } + } + } + }?; Ok(()) } - // TODO: Add extrinsic to remove commitment without requiring the identity to be - // deleted. } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 5f0d137963..76b742e129 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -22,11 +22,17 @@ pub use identity_generation::*; pub mod identity_generation { use sp_std::marker::PhantomData; + use crate::IdentityCommitmentVersion; + pub trait IdentityCommitmentGenerator { type Error; type Output; - fn generate_commitment(identifier: &Identifier, identity: &Identity) -> Result; + fn generate_commitment( + identifier: &Identifier, + identity: &Identity, + version: IdentityCommitmentVersion, + ) -> Result; } // Implement the `IdentityCommitmentGenerator` by returning the `Default` value @@ -41,7 +47,11 @@ pub mod identity_generation { type Error = (); type Output = Output; - fn generate_commitment(_identifier: &Identifier, _identity: &Identity) -> Result { + fn generate_commitment( + _identifier: &Identifier, + _identity: &Identity, + _version: IdentityCommitmentVersion, + ) -> Result { Ok(Output::default()) } } diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 0059d267ae..d7af9c87b5 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -20,7 +20,7 @@ use frame_support::RuntimeDebug; use frame_system::pallet_prelude::BlockNumberFor; use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::traits::IdentityCommitmentGenerator; +use pallet_dip_provider::{traits::IdentityCommitmentGenerator, IdentityCommitmentVersion}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; @@ -50,6 +50,7 @@ pub struct CompleteMerkleProof { #[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo, PartialEq)] pub enum DidMerkleProofError { + UnsupportedVersion, DidNotFound, KeyNotFound, LinkedAccountNotFound, @@ -60,50 +61,40 @@ pub enum DidMerkleProofError { impl From for u16 { fn from(value: DidMerkleProofError) -> Self { match value { - DidMerkleProofError::DidNotFound => 0, - DidMerkleProofError::KeyNotFound => 1, - DidMerkleProofError::LinkedAccountNotFound => 2, - DidMerkleProofError::Web3NameNotFound => 3, + DidMerkleProofError::UnsupportedVersion => 0, + DidMerkleProofError::DidNotFound => 1, + DidMerkleProofError::KeyNotFound => 2, + DidMerkleProofError::LinkedAccountNotFound => 3, + DidMerkleProofError::Web3NameNotFound => 4, DidMerkleProofError::Internal => u16::MAX, } } } -pub struct DidMerkleRootGenerator(PhantomData); +pub mod v0 { + use super::*; -type ProofLeafOf = RevealedDidMerkleProofLeaf< - KeyIdOf, - ::AccountId, - BlockNumberFor, - ::Web3Name, - LinkableAccountId, ->; + type ProofLeafOf = RevealedDidMerkleProofLeaf< + KeyIdOf, + ::AccountId, + BlockNumberFor, + ::Web3Name, + LinkableAccountId, + >; -impl DidMerkleRootGenerator -where - T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, -{ - // Calls the function in the `sp_trie` crate to generate the merkle root given - // the provided `DidDetails`. - // Each key in the merkle tree is added in the following way: - // - keys in the `public_keys` map are added by value in the merkle tree, with - // the leaf key being the key ID and the value being the key details - // - keys everywhere else in the DidDetails are added by reference, with the - // leaf key being the encoding of the tuple (keyID, key relationship) and the - // value being hte empty tuple - // A valid proof will contain a leaf with the key details for each reference - // leaf, with multiple reference leaves potentially referring to the same - // details leaf, as we already do with out `DidDetails` type. - fn calculate_root_with_db( - identity: &LinkedDidInfoOf, - db: &mut MemoryDB, - ) -> Result { + pub(super) fn calculate_root_with_db( + identity: &LinkedDidInfoOf, + db: &mut MemoryDB, + ) -> Result + where + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + { // Fails if the DID details do not exist. let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { return Err(DidMerkleProofError::DidNotFound); }; - let mut trie = TrieHash::>::default(); - let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); + let mut trie = TrieHash::>::default(); + let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); // Authentication key let auth_key_details = did_details @@ -113,7 +104,7 @@ where log::error!("Authentication key should be part of the public keys."); DidMerkleProofError::Internal })?; - let auth_leaf = ProofLeafOf::::DidKey( + let auth_leaf = ProofLeafOf::::DidKey( DidKeyMerkleKey( did_details.authentication_key, DidVerificationKeyRelationship::Authentication.into(), @@ -135,7 +126,7 @@ where log::error!("Attestation key should be part of the public keys."); DidMerkleProofError::Internal })?; - let att_leaf = ProofLeafOf::::DidKey( + let att_leaf = ProofLeafOf::::DidKey( (att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into(), att_key_details.clone().into(), ); @@ -155,7 +146,7 @@ where log::error!("Delegation key should be part of the public keys."); DidMerkleProofError::Internal })?; - let del_leaf = ProofLeafOf::::DidKey( + let del_leaf = ProofLeafOf::::DidKey( (del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into(), del_key_details.clone().into(), ); @@ -178,7 +169,7 @@ where log::error!("Key agreement key should be part of the public keys."); DidMerkleProofError::Internal })?; - let enc_leaf = ProofLeafOf::::DidKey( + let enc_leaf = ProofLeafOf::::DidKey( (*id, DidKeyRelationship::Encryption).into(), key_agreement_details.clone().into(), ); @@ -199,7 +190,8 @@ where linked_accounts .iter() .try_for_each(|linked_account| -> Result<(), DidMerkleProofError> { - let linked_account_leaf = ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); + let linked_account_leaf = + ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); trie_builder .insert( linked_account_leaf.encoded_key().as_slice(), @@ -218,7 +210,7 @@ where // Web3name, if present if let Some(web3name_details) = web3_name { - let web3_name_leaf = ProofLeafOf::::Web3Name( + let web3_name_leaf = ProofLeafOf::::Web3Name( web3name_details.web3_name.clone().into(), web3name_details.claimed_at.into(), ); @@ -240,19 +232,15 @@ where Ok(trie_builder.root().to_owned()) } - // Only used for testing and as part of the features exposed by the runtime API - // of the provider. Given the provided `DidDetails` and a list of key IDs, it - // generates a merkle proof which only reveals the details of the provided key - // IDs. - #[allow(clippy::result_unit_err)] - pub fn generate_proof<'a, K, A>( - identity: &LinkedDidInfoOf, + pub(super) fn generate_proof<'a, Runtime, K, A>( + identity: &LinkedDidInfoOf, key_ids: K, should_include_web3_name: bool, account_ids: A, - ) -> Result>, DidMerkleProofError> + ) -> Result>, DidMerkleProofError> where - K: Iterator>, + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + K: Iterator>, A: Iterator, { // Fails if the DID details do not exist. @@ -261,7 +249,7 @@ where }; let mut db = MemoryDB::default(); - let root = Self::calculate_root_with_db(identity, &mut db)?; + let root = calculate_root_with_db(identity, &mut db)?; let mut leaves = key_ids .map(|key_id| -> Result<_, DidMerkleProofError> { @@ -271,7 +259,8 @@ where .ok_or(DidMerkleProofError::KeyNotFound)?; // Create the merkle leaf key depending on the relationship of the key to the // DID document. - let did_key_merkle_key: DidKeyMerkleKey> = if *key_id == did_details.authentication_key { + let did_key_merkle_key: DidKeyMerkleKey> = if *key_id == did_details.authentication_key + { Ok((*key_id, DidVerificationKeyRelationship::Authentication.into()).into()) } else if Some(*key_id) == did_details.attestation_key { Ok((*key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into()) @@ -321,32 +310,73 @@ where }?; let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); - let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| { - log::error!( - "Failed to generate a merkle proof for the encoded keys: {:#?}", - encoded_keys - ); - DidMerkleProofError::Internal - })?; + let proof = + generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| { + log::error!( + "Failed to generate a merkle proof for the encoded keys: {:#?}", + encoded_keys + ); + DidMerkleProofError::Internal + })?; Ok(CompleteMerkleProof { root, - proof: DidMerkleProofOf:: { + proof: DidMerkleProofOf:: { blinded: proof, revealed: leaves.into_iter().collect(), }, }) } + + pub(super) fn generate_commitment( + identity: &LinkedDidInfoOf, + ) -> Result + where + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + { + let mut db = MemoryDB::default(); + calculate_root_with_db(identity, &mut db) + } } -impl IdentityCommitmentGenerator> for DidMerkleRootGenerator +pub struct DidMerkleRootGenerator(PhantomData); + +impl IdentityCommitmentGenerator> for DidMerkleRootGenerator where - T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { type Error = DidMerkleProofError; - type Output = T::Hash; + type Output = Runtime::Hash; - fn generate_commitment(_identifier: &DidIdentifier, identity: &LinkedDidInfoOf) -> Result { - let mut db = MemoryDB::default(); - Self::calculate_root_with_db(identity, &mut db) + fn generate_commitment( + _identifier: &DidIdentifier, + identity: &LinkedDidInfoOf, + version: IdentityCommitmentVersion, + ) -> Result { + match version { + 0 => v0::generate_commitment::(identity), + _ => Err(DidMerkleProofError::UnsupportedVersion), + } + } +} + +impl DidMerkleRootGenerator +where + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, +{ + pub fn generate_proof<'a, K, A>( + identity: &LinkedDidInfoOf, + version: IdentityCommitmentVersion, + key_ids: K, + should_include_web3_name: bool, + account_ids: A, + ) -> Result>, DidMerkleProofError> + where + K: Iterator>, + A: Iterator, + { + match version { + 0 => v0::generate_proof(identity, key_ids, should_include_web3_name, account_ids), + _ => Err(DidMerkleProofError::UnsupportedVersion), + } } } From 6d2481e6d8e3aaa34a98b1c1bfe2e67dd531f652 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 9 Nov 2023 15:02:17 +0000 Subject: [PATCH 14/28] feat: add deposits for DIP provider pallet (#574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/KILTprotocol/ticket/issues/2983. Add a generic pallet to store deposits, as well as a hooking mechanism to both the existing provider pallet (to allow runtimes that take deposits to take deposits, or to do any other operation), and to the new deposit pallet, which allows other pallets to clean up storage if a storage is reclaimed back by its owner (e.g., an identity commitment is removed). Tests will follow in a separate PR, when time allows for them 😁 The whole DIP branch won't be merged on `develop` before tests are written, so it's not a risk. The logic has been tested by spinning up a local network and trying to commit an identity (which takes a deposit), remove the commitment (which frees up the deposit), re-commit the identity, reclaim the deposit (which removes the commitment). - [x] Implement logic for deposit removal hooks - [x] Test - [x] Review --- Cargo.lock | 19 +- Cargo.toml | 1 + crates/kilt-dip-support/Cargo.toml | 5 +- dip-template/runtimes/dip-provider/Cargo.toml | 6 + dip-template/runtimes/dip-provider/src/dip.rs | 118 ++++++++++-- dip-template/runtimes/dip-provider/src/lib.rs | 19 +- out.txt | 1 - pallets/pallet-deposit-storage/Cargo.toml | 41 +++++ pallets/pallet-deposit-storage/src/deposit.rs | 169 ++++++++++++++++++ pallets/pallet-deposit-storage/src/lib.rs | 166 +++++++++++++++++ pallets/pallet-deposit-storage/src/traits.rs | 49 +++++ pallets/pallet-dip-provider/Cargo.toml | 1 - pallets/pallet-dip-provider/src/lib.rs | 95 +++++----- pallets/pallet-dip-provider/src/traits.rs | 57 +++++- 14 files changed, 672 insertions(+), 75 deletions(-) delete mode 100644 out.txt create mode 100644 pallets/pallet-deposit-storage/Cargo.toml create mode 100644 pallets/pallet-deposit-storage/src/deposit.rs create mode 100644 pallets/pallet-deposit-storage/src/lib.rs create mode 100644 pallets/pallet-deposit-storage/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 8915054d6e..abcc85ce85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2535,10 +2535,13 @@ dependencies = [ "frame-system-rpc-runtime-api", "kilt-dip-support", "kilt-runtime-api-dip-provider", + "kilt-support", + "log", "pallet-aura", "pallet-authorship", "pallet-balances", "pallet-collator-selection", + "pallet-deposit-storage", "pallet-did-lookup", "pallet-dip-provider", "pallet-session", @@ -6442,6 +6445,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-deposit-storage" +version = "1.12.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "kilt-support", + "log", + "pallet-dip-provider", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-did-lookup" version = "1.12.0-dev" @@ -6491,7 +6509,6 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-io", "sp-std", ] diff --git a/Cargo.toml b/Cargo.toml index 00b1e3e7c8..afd7e6a757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ ctype = {path = "pallets/ctype", default-features = false} delegation = {path = "pallets/delegation", default-features = false} did = {path = "pallets/did", default-features = false} pallet-configuration = {path = "pallets/pallet-configuration", default-features = false} +pallet-deposit-storage = {path = "pallets/pallet-deposit-storage", default-features = false} pallet-dip-consumer = {path = "pallets/pallet-dip-consumer", default-features = false} pallet-dip-provider = {path = "pallets/pallet-dip-provider", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index 8941e0e46d..b9ba5ff712 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -28,9 +28,9 @@ scale-info = {workspace = true, features = ["derive"]} # Substrate dependencies frame-system.workspace = true frame-support.workspace = true -sp-runtime.workspace = true sp-core.workspace = true sp-io.workspace = true +sp-runtime.workspace = true sp-state-machine.workspace = true sp-std.workspace = true sp-trie.workspace = true @@ -51,6 +51,7 @@ sp-io = { workspace = true, features = ["std"] } default = ["std"] std = [ "hash-db/std", + "log/std", "did/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", @@ -59,9 +60,9 @@ std = [ "scale-info/std", "frame-system/std", "frame-support/std", - "sp-runtime/std", "sp-core/std", "sp-io/std", + "sp-runtime/std", "sp-state-machine/std", "sp-std/std", "sp-trie/std", diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index dff25abda3..19da12ee26 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -14,13 +14,16 @@ version.workspace = true substrate-wasm-builder.workspace = true [dependencies] +log.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true +kilt-support.workspace = true kilt-dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true +pallet-deposit-storage.workspace = true pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true pallet-web3-names.workspace = true @@ -66,11 +69,14 @@ default = [ "std", ] std = [ + "log/std", "parity-scale-codec/std", "scale-info/std", "did/std", + "kilt-support/std", "kilt-dip-support/std", "kilt-runtime-api-dip-provider/std", + "pallet-deposit-storage/std", "pallet-did-lookup/std", "pallet-dip-provider/std", "pallet-web3-names/std", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 720304f30b..12ac28a33b 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,6 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; +use frame_system::EnsureSigned; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::{traits::IdentityProvider, IdentityCommitmentVersion}; use parity_scale_codec::{Decode, Encode}; @@ -25,24 +26,114 @@ use runtime_common::dip::{ merkle::{DidMerkleProofError, DidMerkleRootGenerator}, }; use scale_info::TypeInfo; +use sp_core::ConstU32; use sp_std::vec::Vec; -use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent}; +use crate::{ + deposit::{DepositHooks, DepositNamespaces}, + AccountId, Balances, DidIdentifier, Hash, Runtime, RuntimeEvent, RuntimeHoldReason, +}; + +pub mod runtime_api { + use super::*; + + #[derive(Encode, Decode, TypeInfo)] + pub struct DipProofRequest { + pub(crate) identifier: DidIdentifier, + pub(crate) version: IdentityCommitmentVersion, + pub(crate) keys: Vec>, + pub(crate) accounts: Vec, + pub(crate) should_include_web3_name: bool, + } + + #[derive(Encode, Decode, TypeInfo)] + pub enum DipProofError { + IdentityNotFound, + IdentityProviderError( as IdentityProvider>::Error), + MerkleProofError(DidMerkleProofError), + } +} + +pub mod deposit { + use super::*; + + use crate::{Balance, UNIT}; + + use frame_support::traits::Get; + use pallet_deposit_storage::{ + traits::DepositStorageHooks, DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, + }; + use parity_scale_codec::MaxEncodedLen; + use sp_core::{ConstU128, RuntimeDebug}; + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] + pub enum DepositNamespaces { + DipProvider, + } -#[derive(Encode, Decode, TypeInfo)] -pub struct RuntimeApiDipProofRequest { - pub(crate) identifier: DidIdentifier, - pub(crate) version: IdentityCommitmentVersion, - pub(crate) keys: Vec>, - pub(crate) accounts: Vec, - pub(crate) should_include_web3_name: bool, + pub struct DipProviderDepositNamespace; + + impl Get for DipProviderDepositNamespace { + fn get() -> DepositNamespaces { + DepositNamespaces::DipProvider + } + } + + pub const DEPOSIT_AMOUNT: Balance = 2 * UNIT; + + pub type DepositCollectorHooks = + FixedDepositCollectorViaDepositsPallet>; + + pub enum CommitmentDepositRemovalHookError { + DecodeKey, + Internal, + } + + impl From for u16 { + fn from(value: CommitmentDepositRemovalHookError) -> Self { + match value { + CommitmentDepositRemovalHookError::DecodeKey => 0, + CommitmentDepositRemovalHookError::Internal => u16::MAX, + } + } + } + + pub struct DepositHooks; + + impl DepositStorageHooks for DepositHooks { + type Error = CommitmentDepositRemovalHookError; + + fn on_deposit_reclaimed( + _namespace: &::Namespace, + key: &DepositKeyOf, + _deposit: DepositEntryOf, + ) -> Result<(), Self::Error> { + let (identifier, commitment_version) = <(DidIdentifier, IdentityCommitmentVersion)>::decode(&mut &key[..]) + .map_err(|_| CommitmentDepositRemovalHookError::DecodeKey)?; + pallet_dip_provider::Pallet::::delete_identity_commitment_storage_entry( + &identifier, + commitment_version, + ) + .map_err(|_| { + log::error!( + "Should not fail to remove commitment for identifier {:#?} and version {commitment_version}", + identifier + ); + CommitmentDepositRemovalHookError::Internal + })?; + Ok(()) + } + } } -#[derive(Encode, Decode, TypeInfo)] -pub enum RuntimeApiDipProofError { - IdentityNotFound, - IdentityProviderError( as IdentityProvider>::Error), - MerkleProofError(DidMerkleProofError), +impl pallet_deposit_storage::Config for Runtime { + type CheckOrigin = EnsureSigned; + type Currency = Balances; + type DepositHooks = DepositHooks; + type MaxKeyLength = ConstU32<256>; + type Namespace = DepositNamespaces; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; } impl pallet_dip_provider::Config for Runtime { @@ -54,5 +145,6 @@ impl pallet_dip_provider::Config for Runtime { type IdentityCommitmentGeneratorError = DidMerkleProofError; type IdentityProvider = LinkedDidInfoProviderOf; type IdentityProviderError = as IdentityProvider>::Error; + type ProviderHooks = deposit::DepositCollectorHooks; type RuntimeEvent = RuntimeEvent; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index cc8320d55e..94e58c36c4 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -51,7 +51,7 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; -use pallet_dip_provider::traits::IdentityProvider; +use pallet_dip_provider::{traits::IdentityProvider, IdentityProviderOf}; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleProofOf, DidMerkleRootGenerator}; @@ -137,7 +137,8 @@ construct_runtime!( Web3Names: pallet_web3_names = 32, // DIP - DipProvider: pallet_dip_provider = 40, + DepositStorage: pallet_deposit_storage = 40, + DipProvider: pallet_dip_provider = 41, } ); @@ -568,14 +569,12 @@ impl_runtime_apis! { } } - impl kilt_runtime_api_dip_provider::DipProvider>, RuntimeApiDipProofError> for Runtime { - fn generate_proof(request: RuntimeApiDipProofRequest) -> Result>, RuntimeApiDipProofError> { - let linked_did_info = match ::IdentityProvider::retrieve(&request.identifier) { - Ok(Some(linked_did_info)) => Ok(linked_did_info), - Ok(None) => Err(RuntimeApiDipProofError::IdentityNotFound), - Err(e) => Err(RuntimeApiDipProofError::IdentityProviderError(e)) - }?; - DidMerkleRootGenerator::::generate_proof(&linked_did_info, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(RuntimeApiDipProofError::MerkleProofError) + impl kilt_runtime_api_dip_provider::DipProvider>, runtime_api::DipProofError> for Runtime { + fn generate_proof(request: runtime_api::DipProofRequest) -> Result>, runtime_api::DipProofError> { + let maybe_identity_details = IdentityProviderOf::::retrieve(&request.identifier).map_err(runtime_api::DipProofError::IdentityProviderError)?; + let identity_details = maybe_identity_details.ok_or(runtime_api::DipProofError::IdentityNotFound)?; + + DidMerkleRootGenerator::::generate_proof(&identity_details, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(runtime_api::DipProofError::MerkleProofError) } } } diff --git a/out.txt b/out.txt deleted file mode 100644 index dc1d6e62d6..0000000000 --- a/out.txt +++ /dev/null @@ -1 +0,0 @@ -pallet-dip-consumer v1.12.0-dev (/home/antonio/Developer/kilt-node/pallets/pallet-dip-consumer) diff --git a/pallets/pallet-deposit-storage/Cargo.toml b/pallets/pallet-deposit-storage/Cargo.toml new file mode 100644 index 0000000000..914806a69e --- /dev/null +++ b/pallets/pallet-deposit-storage/Cargo.toml @@ -0,0 +1,41 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "pallet-deposit-storage" +description = "Stores all deposits under a single pallet, with suport for namespacing different deposit contexts." + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# Substrate dependencies +frame-support.workspace = true +frame-system.workspace = true +kilt-support.workspace = true +pallet-dip-provider.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-runtime.workspace = true +sp-std.workspace = true + +log.workspace = true + +[features] +default = ["std"] +std = [ + "frame-support/std", + "frame-system/std", + "kilt-support/std", + "pallet-dip-provider/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "log/std", +] diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs new file mode 100644 index 0000000000..a76acce619 --- /dev/null +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -0,0 +1,169 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{ + sp_runtime::DispatchError, + traits::{ + fungible::{hold::Mutate, Inspect}, + tokens::Precision, + }, +}; +use kilt_support::Deposit; +use pallet_dip_provider::{traits::ProviderHooks as DipProviderHooks, IdentityCommitmentVersion}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::traits::Get; +use sp_std::marker::PhantomData; + +use crate::{BalanceOf, Config, Error, HoldReason, Pallet}; + +#[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)] +pub struct DepositEntry { + pub(crate) deposit: Deposit, + pub(crate) reason: Reason, +} + +pub struct FixedDepositCollectorViaDepositsPallet( + PhantomData<(DepositsNamespace, FixedDepositAmount)>, +); + +pub enum FixedDepositCollectorViaDepositsPalletError { + DepositAlreadyTaken, + DepositNotFound, + FailedToHold, + FailedToRelease, + Internal, +} + +impl From for u16 { + fn from(value: FixedDepositCollectorViaDepositsPalletError) -> Self { + match value { + FixedDepositCollectorViaDepositsPalletError::DepositAlreadyTaken => 0, + FixedDepositCollectorViaDepositsPalletError::DepositNotFound => 1, + FixedDepositCollectorViaDepositsPalletError::FailedToHold => 2, + FixedDepositCollectorViaDepositsPalletError::FailedToRelease => 3, + FixedDepositCollectorViaDepositsPalletError::Internal => u16::MAX, + } + } +} + +impl DipProviderHooks + for FixedDepositCollectorViaDepositsPallet +where + Runtime: pallet_dip_provider::Config + Config, + DepositsNamespace: Get, + FixedDepositAmount: Get>, +{ + type Error = u16; + + fn on_identity_committed( + identifier: &Runtime::Identifier, + submitter: &Runtime::AccountId, + _commitment: &Runtime::IdentityCommitment, + version: IdentityCommitmentVersion, + ) -> Result<(), Self::Error> { + let namespace = DepositsNamespace::get(); + let key = (identifier, version).encode().try_into().map_err(|_| { + log::error!( + "Failed to convert tuple ({:#?}, {version}) to BoundedVec with max length {}", + identifier, + Runtime::MaxKeyLength::get() + ); + FixedDepositCollectorViaDepositsPalletError::Internal + })?; + let deposit_entry = DepositEntry { + deposit: Deposit { + amount: FixedDepositAmount::get(), + owner: submitter.clone(), + }, + reason: HoldReason::Deposit.into(), + }; + Pallet::::add_deposit(namespace, key, deposit_entry).map_err(|e| match e { + pallet_error if pallet_error == DispatchError::from(Error::::DepositExisting) => { + FixedDepositCollectorViaDepositsPalletError::DepositAlreadyTaken + } + _ => { + log::error!( + "Error {:#?} should not be generated inside `on_identity_committed` hook.", + e + ); + FixedDepositCollectorViaDepositsPalletError::Internal + } + })?; + Ok(()) + } + + fn on_commitment_removed( + identifier: &Runtime::Identifier, + _submitter: &Runtime::AccountId, + _commitment: &Runtime::IdentityCommitment, + version: pallet_dip_provider::IdentityCommitmentVersion, + ) -> Result<(), Self::Error> { + let namespace = DepositsNamespace::get(); + let key = (identifier, version).encode().try_into().map_err(|_| { + log::error!( + "Failed to convert tuple ({:#?}, {version}) to BoundedVec with max length {}", + identifier, + Runtime::MaxKeyLength::get() + ); + FixedDepositCollectorViaDepositsPalletError::Internal + })?; + Pallet::::remove_deposit(&namespace, &key, None).map_err(|e| match e { + pallet_error if pallet_error == DispatchError::from(Error::::DepositNotFound) => { + FixedDepositCollectorViaDepositsPalletError::DepositNotFound + } + _ => { + log::error!( + "Error {:#?} should not be generated inside `on_commitment_removed` hook.", + e + ); + FixedDepositCollectorViaDepositsPalletError::Internal + } + })?; + Ok(()) + } +} + +// Taken from dip_support logic, not to make that pub +pub(crate) fn reserve_deposit>( + account: Account, + deposit_amount: Currency::Balance, + reason: &Currency::Reason, +) -> Result, DispatchError> { + Currency::hold(reason, &account, deposit_amount)?; + Ok(Deposit { + owner: account, + amount: deposit_amount, + }) +} + +// Taken from dip_support logic, not to make that pub +pub(crate) fn free_deposit>( + deposit: &Deposit, + reason: &Currency::Reason, +) -> Result<>::Balance, DispatchError> { + let result = Currency::release(reason, &deposit.owner, deposit.amount, Precision::BestEffort); + debug_assert!( + result == Ok(deposit.amount), + "Released deposit amount does not match with expected amount. Expected: {:?}, Released amount: {:?} Error: {:?}", + deposit.amount, + result.ok(), + result.err(), + ); + result +} diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs new file mode 100644 index 0000000000..2674923442 --- /dev/null +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -0,0 +1,166 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +mod deposit; +pub mod traits; + +pub use deposit::FixedDepositCollectorViaDepositsPallet; +pub use pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use crate::{ + deposit::{free_deposit, reserve_deposit, DepositEntry}, + traits::DepositStorageHooks, + }; + + use super::*; + + use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{hold::Mutate, Inspect}, + EnsureOrigin, + }, + }; + use frame_system::pallet_prelude::*; + use parity_scale_codec::FullCodec; + use scale_info::TypeInfo; + use sp_runtime::DispatchError; + use sp_std::fmt::Debug; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + pub type AccountIdOf = ::AccountId; + pub type BalanceOf = <::Currency as Inspect>>::Balance; + pub type DepositKeyOf = BoundedVec::MaxKeyLength>; + pub type DepositEntryOf = DepositEntry, BalanceOf, ::RuntimeHoldReason>; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type MaxKeyLength: Get; + + type CheckOrigin: EnsureOrigin; + type Currency: Mutate; + type DepositHooks: DepositStorageHooks; + type Namespace: Parameter; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeHoldReason: From + Clone + PartialEq + Debug + FullCodec + MaxEncodedLen + TypeInfo; + } + + #[pallet::composite_enum] + pub enum HoldReason { + Deposit, + } + + #[pallet::error] + pub enum Error { + DepositNotFound, + DepositExisting, + Unauthorized, + Hook(u16), + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + DepositAdded { + namespace: T::Namespace, + key: DepositKeyOf, + deposit_entry: DepositEntryOf, + }, + DepositReclaimed { + namespace: T::Namespace, + key: DepositKeyOf, + deposit_entry: DepositEntryOf, + }, + } + + // Double map (namespace, key) -> deposit + #[pallet::storage] + #[pallet::getter(fn deposits)] + pub(crate) type Deposits = + StorageDoubleMap<_, Twox64Concat, ::Namespace, Twox64Concat, DepositKeyOf, DepositEntryOf>; + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + // TODO: Update weight + #[pallet::weight(0)] + pub fn reclaim_deposit(origin: OriginFor, namespace: T::Namespace, key: DepositKeyOf) -> DispatchResult { + let dispatcher = T::CheckOrigin::ensure_origin(origin)?; + + let deposit = Self::remove_deposit(&namespace, &key, Some(&dispatcher))?; + T::DepositHooks::on_deposit_reclaimed(&namespace, &key, deposit).map_err(|e| Error::::Hook(e.into()))?; + Ok(()) + } + } + + impl Pallet { + pub fn add_deposit(namespace: T::Namespace, key: DepositKeyOf, entry: DepositEntryOf) -> DispatchResult { + Deposits::::try_mutate(&namespace, &key, |deposit_entry| match deposit_entry { + Some(_) => Err(DispatchError::from(Error::::DepositExisting)), + None => { + reserve_deposit::, T::Currency>( + entry.deposit.owner.clone(), + entry.deposit.amount, + &entry.reason, + )?; + Self::deposit_event(Event::::DepositAdded { + namespace: namespace.clone(), + key: key.clone(), + deposit_entry: entry.clone(), + }); + *deposit_entry = Some(entry); + Ok(()) + } + })?; + Ok(()) + } + + pub fn remove_deposit( + namespace: &T::Namespace, + key: &DepositKeyOf, + expected_owner: Option<&AccountIdOf>, + ) -> Result, DispatchError> { + let existing_entry = Deposits::::take(namespace, key).ok_or(Error::::DepositNotFound)?; + if let Some(expected_owner) = expected_owner { + ensure!( + existing_entry.deposit.owner == *expected_owner, + Error::::Unauthorized + ); + } + free_deposit::, T::Currency>(&existing_entry.deposit, &existing_entry.reason)?; + Self::deposit_event(Event::::DepositReclaimed { + namespace: namespace.clone(), + key: key.clone(), + deposit_entry: existing_entry.clone(), + }); + Ok(existing_entry) + } + } +} diff --git a/pallets/pallet-deposit-storage/src/traits.rs b/pallets/pallet-deposit-storage/src/traits.rs new file mode 100644 index 0000000000..f514eee108 --- /dev/null +++ b/pallets/pallet-deposit-storage/src/traits.rs @@ -0,0 +1,49 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{Config, DepositEntryOf, DepositKeyOf}; + +pub trait DepositStorageHooks +where + Runtime: Config, +{ + type Error: Into; + + fn on_deposit_reclaimed( + namespace: &Runtime::Namespace, + key: &DepositKeyOf, + deposit: DepositEntryOf, + ) -> Result<(), Self::Error>; +} + +pub struct NoopDepositStorageHooks; + +impl DepositStorageHooks for NoopDepositStorageHooks +where + Runtime: Config, +{ + type Error = u16; + + fn on_deposit_reclaimed( + _namespace: &Runtime::Namespace, + _key: &DepositKeyOf, + _deposit: DepositEntryOf, + ) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 8c078fbc52..5e22e357a1 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -19,7 +19,6 @@ frame-support.workspace = true frame-system.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -sp-io.workspace = true sp-std.workspace = true [features] diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 826fd170ac..f01b78c52e 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -31,23 +31,17 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; use parity_scale_codec::FullCodec; - use sp_io::MultiRemovalResults; use sp_std::fmt::Debug; - use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, SubmitterInfo}; + use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, ProviderHooks, SubmitterInfo}; + pub type IdentityProviderOf = ::IdentityProvider; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; pub type IdentityCommitmentVersion = u16; pub const LATEST_COMMITMENT_VERSION: IdentityCommitmentVersion = 0; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - #[derive(Encode, Decode, RuntimeDebug, TypeInfo, Clone, PartialEq)] - pub enum VersionOrLimit { - Version(IdentityCommitmentVersion), - Limit(u32), - } - #[pallet::config] pub trait Config: frame_system::Config { type CommitOriginCheck: EnsureOrigin; @@ -63,6 +57,7 @@ pub mod pallet { type IdentityCommitmentGeneratorError: Into; type IdentityProvider: IdentityProvider; type IdentityProviderError: Into; + type ProviderHooks: ProviderHooks; type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -104,6 +99,7 @@ pub mod pallet { LimitTooLow, IdentityProvider(u16), IdentityCommitmentGenerator(u16), + Hook(u16), } #[pallet::call] @@ -116,27 +112,43 @@ pub mod pallet { identifier: T::Identifier, version: Option, ) -> DispatchResult { - // TODO: use dispatcher to get deposit - let _dispatcher = + let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); let commitment = match T::IdentityProvider::retrieve(&identifier) { + Ok(None) => Err(Error::::IdentityNotFound), + Err(error) => Err(Error::::IdentityProvider(error.into())), Ok(Some(identity)) => { T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) .map_err(|error| Error::::IdentityCommitmentGenerator(error.into())) } - Ok(None) => Err(Error::::IdentityNotFound), - Err(error) => Err(Error::::IdentityProvider(error.into())), }?; - // TODO: Take deposit (once 0.9.42 PR is merged into develop) - IdentityCommitments::::insert(&identifier, commitment_version, commitment.clone()); - Self::deposit_event(Event::::IdentityCommitted { - identifier, - commitment, - version: commitment_version, - }); + IdentityCommitments::::try_mutate(&identifier, commitment_version, |commitment_entry| { + if let Some(old_commitment) = commitment_entry { + T::ProviderHooks::on_commitment_removed( + &identifier, + &dispatcher, + old_commitment, + commitment_version, + ) + .map_err(|e| Error::::Hook(e.into()))?; + Self::deposit_event(Event::::VersionedIdentityDeleted { + identifier: identifier.clone(), + version: commitment_version, + }); + } + T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) + .map_err(|e| Error::::Hook(e.into()))?; + *commitment_entry = Some(commitment.clone()); + Self::deposit_event(Event::::IdentityCommitted { + identifier: identifier.clone(), + commitment, + version: commitment_version, + }); + Ok::<_, Error>(()) + })?; Ok(()) } @@ -146,35 +158,30 @@ pub mod pallet { pub fn delete_identity_commitment( origin: OriginFor, identifier: T::Identifier, - version_or_limit: VersionOrLimit, + version: Option, ) -> DispatchResult { - let _dispatcher = + let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; - match version_or_limit { - VersionOrLimit::Version(version) => { - let commitment = IdentityCommitments::::take(&identifier, version); - match commitment { - Some(_) => Err(Error::::IdentityNotFound), - None => { - Self::deposit_event(Event::::VersionedIdentityDeleted { identifier, version }); - Ok(()) - } - } - } - VersionOrLimit::Limit(limit) => { - let MultiRemovalResults { maybe_cursor, .. } = - IdentityCommitments::::clear_prefix(&identifier, limit, None); - match maybe_cursor { - Some(_) => Err(Error::::LimitTooLow), - None => { - Self::deposit_event(Event::::IdentityDeleted { identifier }); - Ok(()) - } - } - } - }?; + let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); + let commitment = Self::delete_identity_commitment_storage_entry(&identifier, commitment_version)?; + T::ProviderHooks::on_commitment_removed(&identifier, &dispatcher, &commitment, commitment_version) + .map_err(|e| Error::::Hook(e.into()))?; Ok(()) } } + + impl Pallet { + pub fn delete_identity_commitment_storage_entry( + identifier: &T::Identifier, + version: IdentityCommitmentVersion, + ) -> Result { + let commitment = IdentityCommitments::::take(identifier, version).ok_or(Error::::IdentityNotFound)?; + Self::deposit_event(Event::::VersionedIdentityDeleted { + identifier: identifier.clone(), + version, + }); + Ok(commitment) + } + } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 76b742e129..52785c8ad0 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -17,12 +17,15 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::DidRawOrigin; +use frame_support::sp_runtime::AccountId32; + +use crate::{Config, IdentityCommitmentVersion}; pub use identity_generation::*; pub mod identity_generation { - use sp_std::marker::PhantomData; + use super::*; - use crate::IdentityCommitmentVersion; + use sp_std::marker::PhantomData; pub trait IdentityCommitmentGenerator { type Error; @@ -102,7 +105,7 @@ pub trait SubmitterInfo { fn submitter(&self) -> Self::Submitter; } -impl SubmitterInfo for frame_support::sp_runtime::AccountId32 { +impl SubmitterInfo for AccountId32 { type Submitter = Self; fn submitter(&self) -> Self::Submitter { @@ -120,3 +123,51 @@ where self.submitter.clone() } } + +pub trait ProviderHooks +where + Runtime: Config, +{ + type Error: Into; + + fn on_identity_committed( + identifier: &Runtime::Identifier, + submitter: &Runtime::AccountId, + commitment: &Runtime::IdentityCommitment, + version: IdentityCommitmentVersion, + ) -> Result<(), Self::Error>; + + fn on_commitment_removed( + identifier: &Runtime::Identifier, + submitter: &Runtime::AccountId, + commitment: &Runtime::IdentityCommitment, + version: IdentityCommitmentVersion, + ) -> Result<(), Self::Error>; +} + +pub struct NoopHooks; + +impl ProviderHooks for NoopHooks +where + Runtime: Config, +{ + type Error = u16; + + fn on_commitment_removed( + _identifier: &Runtime::Identifier, + _submitter: &Runtime::AccountId, + _commitment: &Runtime::IdentityCommitment, + _version: IdentityCommitmentVersion, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn on_identity_committed( + _identifier: &Runtime::Identifier, + _submitter: &Runtime::AccountId, + _commitment: &Runtime::IdentityCommitment, + _version: IdentityCommitmentVersion, + ) -> Result<(), Self::Error> { + Ok(()) + } +} From 3ce3da63c6063f6afaf5640d8fbe8a1e4b59bfce Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 15 Nov 2023 09:00:04 +0000 Subject: [PATCH 15/28] chore: improve DX (#577) Fixes https://github.com/KILTprotocol/ticket/issues/2974 and based on top of https://github.com/KILTprotocol/kilt-node/pull/574. This is my take at trying to improve the DX of integrating DIP components. The following is a list of the main changes introduced by this PR: - Definitions of traits that are only ever used and useful inside a pallet have been changed to take a generic `Runtime` type that must implement the pallet config, reducing the clutter when implementing them - Additional bounds have been added to the associated types of those traits, so that they don't need to be specified in the pallet config so that we can have trait bounds of those types. So basically what used to be the trait bounds on the pallet config, is not a trait bound on the trait return type. I think this is also the "Rusty" way to go. - The generic consumer types exported in `kilt-dip-support` crate have been renamed with the `Generic` prefix, because I introduced two new types, starting with `Kilt`, that make it easier to integrate any KILT runtime as an identity provider, with the downside of having to depend on the whole runtime definition, but there's also ways around that (e.g., by having an intermediate crate that only implements the required `Config` traits). Example 1 below shows the change. - Better re-exports (feedback is welcome on this one). For instance, for types that implement a basic version of a pallet's trait definition, the type is exported directly from the pallet crate root, while the trait is still namespaced behind the `mod traits`. Example 2 below. - Change of the origin check for the `pallet_dip_consumer::dispatch_as` extrinsic from `ensure_signed` to a generic `T::DispatchOriginCheck::ensure_origin`, as long as the `DispatchOriginCheck` returns an `AccountId` upon successful verification. ## Example 1: old vs new way of integrating a consumer into a runtime where KILT is a provider ### Old ```rust pub type ProofVerifier = VersionedDipSiblingProviderStateProofVerifier< RelayStateRootsViaRelayStorePallet, ConstU32<2_000>, ProviderParachainStateInfoViaProviderPallet, AccountId, BlakeTwo256, KeyIdOf, ProviderAccountId, Web3Name, LinkableAccountId, 10, 10, u128, // Signatures are valid for 50 blocks FrameSystemDidSignatureContext, DipCallFilter, >; ``` ### New (100% equivalent to the old one) ```rust pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< ProviderRuntime, // KILT runtime definition ConstU32<2_000>, RelayStateRootsViaRelayStorePallet, // Local runtime definition BlakeTwo256, DipCallFilter, 10, 10, 50, >; ``` ## Example 2: example of type implementing a pallet's trait with a basic implementation For the trait ```rust pub trait IdentityCommitmentGenerator where Runtime: Config, Runtime::IdentityProvider: IdentityProvider, { type Error: Into; type Output: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; fn generate_commitment( identifier: &Runtime::Identifier, identity: &IdentityOf, version: IdentityCommitmentVersion, ) -> Result; } ``` a basic implementation (mostly useful for tests) is provided ```rust pub struct DefaultIdentityCommitmentGenerator(PhantomData); impl IdentityCommitmentGenerator for DefaultIdentityCommitmentGenerator where Runtime: Config, Output: Default + Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen, { type Error = u16; type Output = Output; fn generate_commitment( _identifier: &Runtime::Identifier, _identity: &IdentityOf, _version: IdentityCommitmentVersion, ) -> Result { Ok(Output::default()) } } ``` The pallet's crate will then export the trait with `pub mod traits`, while the type is re-exported directly _also_ from the root with `pub use traits::DefaultIdentityCommitmentGenerator`. --- Cargo.lock | 4 + crates/kilt-dip-support/Cargo.toml | 8 + crates/kilt-dip-support/src/export/child.rs | 313 +++++++++++++----- crates/kilt-dip-support/src/export/mod.rs | 10 +- crates/kilt-dip-support/src/export/sibling.rs | 295 ++++++++++++----- crates/kilt-dip-support/src/lib.rs | 6 +- crates/kilt-dip-support/src/state_proofs.rs | 6 +- crates/kilt-dip-support/src/traits.rs | 4 +- crates/kilt-dip-support/src/utils.rs | 119 ------- dip-template/runtimes/dip-consumer/src/dip.rs | 32 +- .../dip-consumer/src/origin_adapter.rs | 6 +- dip-template/runtimes/dip-provider/src/dip.rs | 16 +- dip-template/runtimes/dip-provider/src/lib.rs | 5 +- pallets/pallet-deposit-storage/src/deposit.rs | 6 +- pallets/pallet-deposit-storage/src/lib.rs | 1 + pallets/pallet-dip-consumer/src/lib.rs | 41 +-- pallets/pallet-dip-consumer/src/traits.rs | 44 +-- pallets/pallet-dip-provider/src/lib.rs | 55 ++- pallets/pallet-dip-provider/src/traits.rs | 110 +++--- runtimes/common/src/dip/did.rs | 123 +++---- runtimes/common/src/dip/merkle.rs | 98 +++--- 21 files changed, 695 insertions(+), 607 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abcc85ce85..ca9bc7a55e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4528,15 +4528,19 @@ name = "kilt-dip-support" version = "1.12.0-dev" dependencies = [ "cumulus-pallet-parachain-system", + "cumulus-primitives-core", "did", "frame-support", "frame-system", "hash-db", "hex-literal 0.3.4", "log", + "pallet-did-lookup", "pallet-dip-consumer", "pallet-dip-provider", "pallet-relay-store", + "pallet-web3-names", + "parachain-info", "parity-scale-codec", "rococo-runtime", "scale-info", diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index b9ba5ff712..d9d6048840 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -17,9 +17,11 @@ log.workspace = true # Internal dependencies did.workspace = true +pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true pallet-relay-store.workspace = true +pallet-web3-names.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} @@ -42,6 +44,8 @@ xcm-executor.workspace = true # Cumulus dependencies cumulus-pallet-parachain-system.workspace = true +cumulus-primitives-core.workspace = true +parachain-info.workspace = true [dev-dependencies] hex-literal.workspace = true @@ -53,9 +57,11 @@ std = [ "hash-db/std", "log/std", "did/std", + "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", "pallet-relay-store/std", + "pallet-web3-names/std", "parity-scale-codec/std", "scale-info/std", "frame-system/std", @@ -70,6 +76,8 @@ std = [ "xcm/std", "xcm-executor/std", "cumulus-pallet-parachain-system/std", + "cumulus-primitives-core/std", + "parachain-info/std", ] runtime-benchmarks = [ "pallet-dip-consumer/runtime-benchmarks", diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index c238080099..6df44c2e49 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -16,12 +16,17 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; -use pallet_dip_consumer::traits::IdentityProofVerifier; +use cumulus_primitives_core::ParaId; +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; +use frame_support::Parameter; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_dip_consumer::{traits::IdentityProofVerifier, RuntimeCallOf}; +use pallet_dip_provider::IdentityCommitmentOf; use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; use scale_info::TypeInfo; use sp_core::{RuntimeDebug, U256}; -use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedSub, Get, Hash, MaybeDisplay, Member, SimpleBitOps}; +use sp_runtime::traits::{AtLeast32BitUnsigned, Get, Hash, MaybeDisplay, Member, SimpleBitOps}; use sp_std::{marker::PhantomData, vec::Vec}; use crate::{ @@ -33,6 +38,7 @@ use crate::{ RelayChainStorageInfo, }, utils::OutputOf, + FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -112,11 +118,154 @@ where } } -pub struct VersionedDipChildProviderStateProofVerifier< +// Implements the same `IdentityProvider` trait, but it is internally configured +// by receiving the runtime definitions of both the provider and the receiver. +pub struct KiltVersionedChildProviderVerifier< + KiltRuntime, + KiltParachainId, + RelayChainInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + const MAX_DID_SIGNATURE_DURATION: u16, +>( + PhantomData<( + KiltRuntime, + KiltParachainId, + RelayChainInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + )>, +); + +impl< + ConsumerRuntime, + KiltRuntime, + KiltParachainId, + RelayChainInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + const MAX_DID_SIGNATURE_DURATION: u16, + > IdentityProofVerifier + for KiltVersionedChildProviderVerifier< + KiltRuntime, + KiltParachainId, + RelayChainInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + MAX_DID_SIGNATURE_DURATION, + > where + KiltRuntime: did::Config + + pallet_web3_names::Config + + pallet_did_lookup::Config + + parachain_info::Config + + pallet_dip_provider::Config, + KiltParachainId: Get, + OutputOf: Ord + From::Hasher>>, + KeyIdOf: Into, + KiltDipMerkleHasher: sp_core::Hasher>, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + RelayChainInfo: RelayChainStorageInfo> + + HistoricalBlockRegistry< + BlockNumber = ::BlockNumber, + Hasher = ::Hasher, + >, + RelayChainInfo::ParaId: From, + OutputOf<::Hasher>: + Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::Hasher: Parameter + 'static, + ::BlockNumber: Copy + + Into + + TryFrom + + HasCompact + + Member + + sp_std::hash::Hash + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec + + Parameter + + 'static, + RelayChainInfo::Key: AsRef<[u8]>, + LocalDidCallVerifier: DipCallOriginFilter< + RuntimeCallOf, + OriginInfo = ( + DidVerificationKey, + DidVerificationKeyRelationship, + ), + >, +{ + type Error = DipChildProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type Proof = VersionedChildParachainDipStateProof< + ::BlockNumber, + ::Hasher, + Vec>, + RevealedDidMerkleProofLeaf< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + KiltRuntime::Web3Name, + LinkableAccountId, + >, + >; + type VerificationResult = RevealedDidMerkleProofLeaves< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + KiltRuntime::Web3Name, + LinkableAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + , + KiltDipMerkleHasher, + KeyIdOf, + KiltRuntime::AccountId, + KiltRuntime::Web3Name, + LinkableAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + FrameSystemDidSignatureContext, + LocalDidCallVerifier, + > as IdentityProofVerifier>::verify_proof_for_call_against_details( + call, + subject, + submitter, + identity_details, + proof, + ) + } +} + +// More generic version compared to `VersionedChildKiltProviderVerifier`, to be +// used in cases in which it is not possible or not desirable to depend on the +// whole provider runtime definition. Hence, required types must be filled in +// manually. +pub struct GenericVersionedDipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -124,7 +273,6 @@ pub struct VersionedDipChildProviderStateProofVerifier< ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, >( @@ -133,25 +281,21 @@ pub struct VersionedDipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, ProviderWeb3Name, ProviderLinkedAccountId, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, )>, ); impl< - Call, - Subject, + ConsumerRuntime, RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -159,15 +303,13 @@ impl< ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, - > IdentityProofVerifier - for VersionedDipChildProviderStateProofVerifier< + > IdentityProofVerifier + for GenericVersionedDipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -175,20 +317,20 @@ impl< ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, > where - Call: Encode, - TxSubmitter: Encode, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default, - RelayChainInfo: RelayChainStorageInfo + RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< BlockNumber = ::BlockNumber, Hasher = ::Hasher, >, OutputOf<::Hasher>: Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::Hasher: Parameter + 'static, ::BlockNumber: Copy + Into + TryFrom @@ -197,31 +339,33 @@ impl< + sp_std::hash::Hash + MaybeDisplay + AtLeast32BitUnsigned - + Codec, + + Codec + + Parameter + + 'static, RelayChainInfo::Key: AsRef<[u8]>, ChildProviderParachainId: Get, - ChildProviderStateInfo: ProviderParachainStateInfo, + ChildProviderStateInfo: + ProviderParachainStateInfo, OutputOf: Ord + From::Hasher>>, - ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::BlockNumber: Parameter + 'static, ChildProviderStateInfo::Commitment: Decode, ChildProviderStateInfo::Key: AsRef<[u8]>, LocalContextProvider: - DidSignatureVerifierContext::BlockNumber>, - LocalContextProvider::BlockNumber: CheckedSub + From, - LocalContextProvider::Hash: Encode, + DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: - DipCallOriginFilter, DidVerificationKeyRelationship)>, + LocalDidCallVerifier: DipCallOriginFilter< + RuntimeCallOf, + OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + >, ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, + ProviderDidKeyId: Parameter + 'static + Into, + ProviderAccountId: Parameter + 'static, + ProviderLinkedAccountId: Parameter + 'static, + ProviderWeb3Name: Parameter + 'static, { type Error = DipChildProviderStateProofVerifierError< ParachainHeadProofVerifierError, @@ -229,7 +373,6 @@ impl< DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type IdentityDetails = LocalDidDetails; type Proof = VersionedChildParachainDipStateProof< ::BlockNumber, ::Hasher, @@ -242,7 +385,6 @@ impl< ProviderLinkedAccountId, >, >; - type Submitter = TxSubmitter; type VerificationResult = RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, @@ -254,31 +396,33 @@ impl< >; fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, proof: Self::Proof, ) -> Result { match proof { - VersionedChildParachainDipStateProof::V0(v0_proof) => { - v0::DipChildProviderStateProofVerifier::< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - >::verify_proof_for_call_against_details(call, subject, submitter, identity_details, v0_proof) - } + VersionedChildParachainDipStateProof::V0(v0_proof) => as IdentityProofVerifier>::verify_proof_for_call_against_details( + call, + subject, + submitter, + identity_details, + v0_proof, + ), } } } @@ -335,7 +479,6 @@ mod v0 { RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -343,7 +486,6 @@ mod v0 { ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, >( @@ -352,25 +494,21 @@ mod v0 { RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, ProviderWeb3Name, ProviderLinkedAccountId, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, )>, ); impl< - Call, - Subject, + ConsumerRuntime, RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -378,15 +516,13 @@ mod v0 { ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, - > IdentityProofVerifier + > IdentityProofVerifier for DipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -394,18 +530,18 @@ mod v0 { ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, > where - Call: Encode, - TxSubmitter: Encode, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default, - RelayChainInfo: RelayChainStorageInfo + RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< BlockNumber = ::BlockNumber, Hasher = ::Hasher, >, + ::Hasher: Parameter + 'static, OutputOf<::Hasher>: Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, ::BlockNumber: Copy @@ -416,35 +552,36 @@ mod v0 { + sp_std::hash::Hash + MaybeDisplay + AtLeast32BitUnsigned - + Codec, + + Codec + + Parameter + + 'static, RelayChainInfo::Key: AsRef<[u8]>, ChildProviderParachainId: Get, - ChildProviderStateInfo: - ProviderParachainStateInfo, + ChildProviderStateInfo: ProviderParachainStateInfo< + Identifier = ConsumerRuntime::Identifier, + Commitment = ProviderDipMerkleHasher::Out, + >, OutputOf: Ord + From::Hasher>>, - ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::BlockNumber: Parameter + 'static, ChildProviderStateInfo::Commitment: Decode, ChildProviderStateInfo::Key: AsRef<[u8]>, LocalContextProvider: - DidSignatureVerifierContext::BlockNumber>, - LocalContextProvider::BlockNumber: CheckedSub + From, - LocalContextProvider::Hash: Encode, + DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, LocalDidCallVerifier: DipCallOriginFilter< - Call, + RuntimeCallOf, OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), >, ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, + ProviderDidKeyId: Parameter + 'static + Into, + ProviderAccountId: Parameter + 'static, + ProviderLinkedAccountId: Parameter + 'static, + ProviderWeb3Name: Parameter + 'static, { type Error = DipChildProviderStateProofVerifierError< ParachainHeadProofVerifierError, @@ -452,7 +589,6 @@ mod v0 { DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type IdentityDetails = LocalDidDetails; type Proof = ChildParachainDipStateProof< ::BlockNumber, ::Hasher, @@ -465,7 +601,6 @@ mod v0 { ProviderLinkedAccountId, >, >; - type Submitter = TxSubmitter; type VerificationResult = RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, @@ -477,10 +612,10 @@ mod v0 { >; fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, proof: Self::Proof, ) -> Result { // 1. Retrieve block hash from provider at the proof height diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-support/src/export/mod.rs index c6a92e7d5b..8d2f289926 100644 --- a/crates/kilt-dip-support/src/export/mod.rs +++ b/crates/kilt-dip-support/src/export/mod.rs @@ -16,16 +16,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -mod child; +pub mod child; +pub mod sibling; + mod common; -mod sibling; pub use child::{ - DipChildProviderStateProofVerifierError, VersionedChildParachainDipStateProof, - VersionedDipChildProviderStateProofVerifier, + DipChildProviderStateProofVerifierError, KiltVersionedChildProviderVerifier, VersionedChildParachainDipStateProof, }; pub use sibling::{ - DipSiblingProviderStateProofVerifierError, VersionedDipSiblingProviderStateProofVerifier, + DipSiblingProviderStateProofVerifierError, KiltVersionedSiblingProviderVerifier, VersionedSiblingParachainDipStateProof, }; diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index e0b3154c54..5f82560104 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -16,12 +16,17 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; -use pallet_dip_consumer::traits::IdentityProofVerifier; +use cumulus_primitives_core::ParaId; +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; +use frame_support::Parameter; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_dip_consumer::{traits::IdentityProofVerifier, RuntimeCallOf}; +use pallet_dip_provider::IdentityCommitmentOf; use parity_scale_codec::{Decode, Encode, HasCompact}; use scale_info::TypeInfo; use sp_core::{RuntimeDebug, U256}; -use sp_runtime::traits::{CheckedSub, Get}; +use sp_runtime::traits::Get; use sp_std::{marker::PhantomData, vec::Vec}; use crate::{ @@ -30,6 +35,7 @@ use crate::{ state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, traits::{self, Bump, DidSignatureVerifierContext, DipCallOriginFilter}, utils::OutputOf, + FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -104,12 +110,137 @@ where } } } +// Implements the same `IdentityProvider` trait, but it is internally configured +// by receiving the runtime definitions of both the provider and the receiver. +pub struct KiltVersionedSiblingProviderVerifier< + KiltRuntime, + KiltParachainId, + RelayChainStateInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + const MAX_DID_SIGNATURE_DURATION: u16, +>( + PhantomData<( + KiltRuntime, + KiltParachainId, + RelayChainStateInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + )>, +); -pub struct VersionedDipSiblingProviderStateProofVerifier< +impl< + ConsumerRuntime, + KiltRuntime, + KiltParachainId, + RelayChainStateInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + const MAX_DID_SIGNATURE_DURATION: u16, + > IdentityProofVerifier + for KiltVersionedSiblingProviderVerifier< + KiltRuntime, + KiltParachainId, + RelayChainStateInfo, + KiltDipMerkleHasher, + LocalDidCallVerifier, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + MAX_DID_SIGNATURE_DURATION, + > where + KiltRuntime: did::Config + + pallet_web3_names::Config + + pallet_did_lookup::Config + + parachain_info::Config + + pallet_dip_provider::Config, + KiltParachainId: Get, + OutputOf: Ord + From>, + KeyIdOf: Into, + KiltDipMerkleHasher: sp_core::Hasher>, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, + RelayChainStateInfo::ParaId: From, + RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::Key: AsRef<[u8]>, + LocalDidCallVerifier: DipCallOriginFilter< + RuntimeCallOf, + OriginInfo = ( + DidVerificationKey, + DidVerificationKeyRelationship, + ), + >, +{ + type Error = DipSiblingProviderStateProofVerifierError< + ParachainHeadProofVerifierError, + DipIdentityCommitmentProofVerifierError, + DidMerkleProofVerifierError, + RevealedDidKeysSignatureAndCallVerifierError, + >; + type Proof = VersionedSiblingParachainDipStateProof< + RelayChainStateInfo::BlockNumber, + Vec>, + RevealedDidMerkleProofLeaf< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + KiltRuntime::Web3Name, + LinkableAccountId, + >, + BlockNumberFor, + >; + type VerificationResult = RevealedDidMerkleProofLeaves< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + KiltRuntime::Web3Name, + LinkableAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + , + KiltDipMerkleHasher, + KeyIdOf, + KiltRuntime::AccountId, + KiltRuntime::Web3Name, + LinkableAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + FrameSystemDidSignatureContext, + LocalDidCallVerifier, + > as IdentityProofVerifier>::verify_proof_for_call_against_details( + call, + subject, + submitter, + identity_details, + proof, + ) + } +} + +// More generic version compared to `VersionedSiblingKiltProviderVerifier`, to +// be used in cases in which it is not possible or not desirable to depend on +// the whole provider runtime definition. Hence, required types must be filled +// in manually. +pub struct GenericVersionedDipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -117,7 +248,6 @@ pub struct VersionedDipSiblingProviderStateProofVerifier< ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, >( @@ -126,25 +256,21 @@ pub struct VersionedDipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, ProviderWeb3Name, ProviderLinkedAccountId, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, )>, ); impl< - Call, - Subject, + ConsumerRuntime, RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -152,15 +278,13 @@ impl< ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, - > IdentityProofVerifier - for VersionedDipSiblingProviderStateProofVerifier< + > IdentityProofVerifier + for GenericVersionedDipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -168,40 +292,41 @@ impl< ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, > where - Call: Encode, - TxSubmitter: Encode, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, - RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, RelayChainStateInfo::Key: AsRef<[u8]>, SiblingProviderParachainId: Get, - SiblingProviderStateInfo: - traits::ProviderParachainStateInfo, + SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + Identifier = ConsumerRuntime::Identifier, + Commitment = ProviderDipMerkleHasher::Out, + >, OutputOf: Ord + From>, - SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::BlockNumber: Parameter + 'static, SiblingProviderStateInfo::Commitment: Decode, SiblingProviderStateInfo::Key: AsRef<[u8]>, - LocalContextProvider: DidSignatureVerifierContext, - LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, - LocalContextProvider::Hash: Encode, + LocalContextProvider: + DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, - LocalDidCallVerifier: - DipCallOriginFilter, DidVerificationKeyRelationship)>, + LocalDidCallVerifier: DipCallOriginFilter< + RuntimeCallOf, + OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + >, ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, + ProviderDidKeyId: Parameter + 'static + Into, + ProviderAccountId: Parameter + 'static, + ProviderLinkedAccountId: Parameter + 'static, + ProviderWeb3Name: Parameter + 'static, { type Error = DipSiblingProviderStateProofVerifierError< ParachainHeadProofVerifierError, @@ -209,7 +334,6 @@ impl< DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type IdentityDetails = LocalDidDetails; type Proof = VersionedSiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, Vec>, @@ -220,9 +344,8 @@ impl< ProviderWeb3Name, ProviderLinkedAccountId, >, - LocalContextProvider::BlockNumber, + BlockNumberFor, >; - type Submitter = TxSubmitter; type VerificationResult = RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, @@ -234,31 +357,33 @@ impl< >; fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, proof: Self::Proof, ) -> Result { match proof { - VersionedSiblingParachainDipStateProof::V0(v0_proof) => { - v0::DipSiblingProviderStateProofVerifier::< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - TxSubmitter, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, - LocalContextProvider, - LocalDidCallVerifier, - >::verify_proof_for_call_against_details(call, subject, submitter, identity_details, v0_proof) - } + VersionedSiblingParachainDipStateProof::V0(v0_proof) => as IdentityProofVerifier>::verify_proof_for_call_against_details( + call, + subject, + submitter, + identity_details, + v0_proof, + ), } } } @@ -270,6 +395,7 @@ pub mod latest { mod v0 { use super::*; + use frame_support::Parameter; use sp_std::borrow::Borrow; use crate::{ @@ -297,7 +423,6 @@ mod v0 { RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -305,7 +430,6 @@ mod v0 { ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, >( @@ -314,25 +438,21 @@ mod v0 { RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, ProviderWeb3Name, ProviderLinkedAccountId, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, )>, ); impl< - Call, - Subject, + ConsumerRuntime, RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -340,15 +460,13 @@ mod v0 { ProviderLinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, - > IdentityProofVerifier + > IdentityProofVerifier for DipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, - TxSubmitter, ProviderDipMerkleHasher, ProviderDidKeyId, ProviderAccountId, @@ -356,42 +474,41 @@ mod v0 { ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - LocalDidDetails, LocalContextProvider, LocalDidCallVerifier, > where - Call: Encode, - TxSubmitter: Encode, + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Bump + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, - RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, RelayChainStateInfo::Key: AsRef<[u8]>, SiblingProviderParachainId: Get, - SiblingProviderStateInfo: - traits::ProviderParachainStateInfo, + SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + Identifier = ConsumerRuntime::Identifier, + Commitment = ProviderDipMerkleHasher::Out, + >, OutputOf: Ord + From>, - SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::BlockNumber: Parameter + 'static, SiblingProviderStateInfo::Commitment: Decode, SiblingProviderStateInfo::Key: AsRef<[u8]>, - LocalContextProvider: DidSignatureVerifierContext, - LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, - LocalContextProvider::Hash: Encode, + LocalContextProvider: + DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, LocalContextProvider::SignedExtra: Encode, - LocalDidDetails: Bump + Default + Encode, LocalDidCallVerifier: DipCallOriginFilter< - Call, + RuntimeCallOf, OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), >, ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Encode + Clone + Into, - ProviderAccountId: Encode + Clone, - ProviderLinkedAccountId: Encode + Clone, - ProviderWeb3Name: Encode + Clone, + ProviderDidKeyId: Parameter + 'static + Into, + ProviderAccountId: Parameter + 'static, + ProviderLinkedAccountId: Parameter + 'static, + ProviderWeb3Name: Parameter + 'static, { type Error = DipSiblingProviderStateProofVerifierError< ParachainHeadProofVerifierError, @@ -399,7 +516,6 @@ mod v0 { DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type IdentityDetails = LocalDidDetails; type Proof = SiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, Vec>, @@ -410,9 +526,8 @@ mod v0 { ProviderWeb3Name, ProviderLinkedAccountId, >, - LocalContextProvider::BlockNumber, + BlockNumberFor, >; - type Submitter = TxSubmitter; type VerificationResult = RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, @@ -424,10 +539,10 @@ mod v0 { >; fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, + call: &RuntimeCallOf, + subject: &ConsumerRuntime::Identifier, + submitter: &ConsumerRuntime::AccountId, + identity_details: &mut Option, proof: Self::Proof, ) -> Result { // 1. Verify relay chain proof. diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 2daacfc640..0322801680 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -29,4 +29,8 @@ pub mod utils; mod export; pub use export::*; -pub use state_proofs::relay_chain::RococoStateRootsViaRelayStorePallet; +pub use state_proofs::{ + parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, + relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, +}; +pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 1f4866ab85..0156143ce9 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -166,9 +166,9 @@ pub(super) mod relay_chain { } } - pub struct RococoStateRootsViaRelayStorePallet(PhantomData); + pub struct RelayStateRootsViaRelayStorePallet(PhantomData); - impl RelayChainStorageInfo for RococoStateRootsViaRelayStorePallet + impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet where Runtime: pallet_relay_store::Config, { @@ -190,7 +190,7 @@ pub(super) mod relay_chain { } } - impl RelayChainStateInfo for RococoStateRootsViaRelayStorePallet + impl RelayChainStateInfo for RelayStateRootsViaRelayStorePallet where Runtime: pallet_relay_store::Config, { diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index d3c013b1b5..aa426aae17 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use frame_system::pallet_prelude::BlockNumberFor; -use pallet_dip_provider::IdentityCommitmentVersion; +use pallet_dip_provider::{IdentityCommitmentOf, IdentityCommitmentVersion}; use sp_core::storage::StorageKey; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; @@ -87,7 +87,7 @@ where T: pallet_dip_provider::Config, { type BlockNumber = BlockNumberFor; - type Commitment = T::IdentityCommitment; + type Commitment = IdentityCommitmentOf; type Hasher = T::Hashing; type Identifier = T::Identifier; type Key = StorageKey; diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs index 30d2d01d50..e382093b93 100644 --- a/crates/kilt-dip-support/src/utils.rs +++ b/crates/kilt-dip-support/src/utils.rs @@ -16,123 +16,4 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use pallet_dip_provider::traits::IdentityProvider; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_std::marker::PhantomData; - -pub struct CombinedIdentityResult { - pub a: OutputA, - pub b: OutputB, - pub c: OutputC, -} - -impl From<(OutputA, OutputB, OutputC)> - for CombinedIdentityResult -{ - fn from(value: (OutputA, OutputB, OutputC)) -> Self { - Self { - a: value.0, - b: value.1, - c: value.2, - } - } -} - -impl CombinedIdentityResult -where - OutputB: Default, - OutputC: Default, -{ - pub fn from_a(a: OutputA) -> Self { - Self { - a, - b: OutputB::default(), - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputC: Default, -{ - pub fn from_b(b: OutputB) -> Self { - Self { - a: OutputA::default(), - b, - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputB: Default, -{ - pub fn from_c(c: OutputC) -> Self { - Self { - a: OutputA::default(), - b: OutputB::default(), - c, - } - } -} - -pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); - -#[derive(Encode, Decode, TypeInfo)] -pub enum CombineError { - A(ErrorA), - B(ErrorB), - C(ErrorC), -} - -impl From> for u16 -where - ErrorA: Into, - ErrorB: Into, - ErrorC: Into, -{ - fn from(value: CombineError) -> Self { - match value { - CombineError::A(error) => error.into(), - CombineError::B(error) => error.into(), - CombineError::C(error) => error.into(), - } - } -} - -impl IdentityProvider for CombineIdentityFrom -where - A: IdentityProvider, - B: IdentityProvider, - C: IdentityProvider, -{ - type Error = CombineError; - type Success = CombinedIdentityResult, Option, Option>; - - fn retrieve(identifier: &Identifier) -> Result, Self::Error> { - match ( - A::retrieve(identifier), - B::retrieve(identifier), - C::retrieve(identifier), - ) { - // If no details is returned, return None for the whole result - (Ok(None), Ok(None), Ok(None)) => Ok(None), - // Otherwise, return `Some` or `None` depending on each result - (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { - a: ok_a, - b: ok_b, - c: ok_c, - })), - (Err(e), _, _) => Err(CombineError::A(e)), - (_, Err(e), _) => Err(CombineError::B(e)), - (_, _, Err(e)) => Err(CombineError::C(e)), - } - } -} - pub type OutputOf = ::Output; diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 91ca198c50..db25e19333 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,46 +16,36 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; -use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as ProviderRuntime, Web3Name}; +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; +use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as ProviderRuntime}; use frame_support::traits::Contains; +use frame_system::EnsureSigned; use kilt_dip_support::{ - traits::{DipCallOriginFilter, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}, - RococoStateRootsViaRelayStorePallet, VersionedDipSiblingProviderStateProofVerifier, + traits::DipCallOriginFilter, KiltVersionedSiblingProviderVerifier, RelayStateRootsViaRelayStorePallet, }; -use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_core::ConstU32; use sp_runtime::traits::BlakeTwo256; use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; -pub type MerkleProofVerifierOutputOf = - >::VerificationResult; -pub type ProofVerifier = VersionedDipSiblingProviderStateProofVerifier< - RococoStateRootsViaRelayStorePallet, +pub type MerkleProofVerifierOutput = >::VerificationResult; +pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< + ProviderRuntime, ConstU32<2_000>, - ProviderParachainStateInfoViaProviderPallet, - AccountId, + RelayStateRootsViaRelayStorePallet, BlakeTwo256, - KeyIdOf, - ProviderAccountId, - Web3Name, - LinkableAccountId, + DipCallFilter, 10, 10, - u128, - // Signatures are valid for 50 blocks - FrameSystemDidSignatureContext, - DipCallFilter, + 50, >; impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; + type DispatchOriginCheck = EnsureSigned; type Identifier = DidIdentifier; - type IdentityProof = >::Proof; type LocalIdentityInfo = u128; - type ProofVerificationError = >::Error; type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index b9d008e5c8..d973367b33 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutputOf, RuntimeCall, RuntimeOrigin, Web3Name}; +use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutput, RuntimeOrigin, Web3Name}; use frame_support::traits::EnsureOrigin; use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; use pallet_postit::traits::Usernamable; @@ -41,9 +41,7 @@ impl EnsureOrigin for EnsureDipOriginAdapter { } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct DipOriginAdapter( - DipOrigin>, -); +pub struct DipOriginAdapter(DipOrigin); impl Usernamable for DipOriginAdapter { type Username = Web3Name; diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 12ac28a33b..91c52a8c1b 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -19,10 +19,10 @@ use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; use frame_system::EnsureSigned; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::{traits::IdentityProvider, IdentityCommitmentVersion}; +use pallet_dip_provider::IdentityCommitmentVersion; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{ - did::LinkedDidInfoProviderOf, + did::{LinkedDidInfoProvider, LinkedDidInfoProviderError}, merkle::{DidMerkleProofError, DidMerkleRootGenerator}, }; use scale_info::TypeInfo; @@ -31,7 +31,7 @@ use sp_std::vec::Vec; use crate::{ deposit::{DepositHooks, DepositNamespaces}, - AccountId, Balances, DidIdentifier, Hash, Runtime, RuntimeEvent, RuntimeHoldReason, + AccountId, Balances, DidIdentifier, Runtime, RuntimeEvent, RuntimeHoldReason, }; pub mod runtime_api { @@ -48,9 +48,8 @@ pub mod runtime_api { #[derive(Encode, Decode, TypeInfo)] pub enum DipProofError { - IdentityNotFound, - IdentityProviderError( as IdentityProvider>::Error), - MerkleProofError(DidMerkleProofError), + IdentityProvider(LinkedDidInfoProviderError), + MerkleProof(DidMerkleProofError), } } @@ -140,11 +139,8 @@ impl pallet_dip_provider::Config for Runtime { type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; - type IdentityCommitment = Hash; type IdentityCommitmentGenerator = DidMerkleRootGenerator; - type IdentityCommitmentGeneratorError = DidMerkleProofError; - type IdentityProvider = LinkedDidInfoProviderOf; - type IdentityProviderError = as IdentityProvider>::Error; + type IdentityProvider = LinkedDidInfoProvider; type ProviderHooks = deposit::DepositCollectorHooks; type RuntimeEvent = RuntimeEvent; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 94e58c36c4..cab15778cb 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -571,10 +571,9 @@ impl_runtime_apis! { impl kilt_runtime_api_dip_provider::DipProvider>, runtime_api::DipProofError> for Runtime { fn generate_proof(request: runtime_api::DipProofRequest) -> Result>, runtime_api::DipProofError> { - let maybe_identity_details = IdentityProviderOf::::retrieve(&request.identifier).map_err(runtime_api::DipProofError::IdentityProviderError)?; - let identity_details = maybe_identity_details.ok_or(runtime_api::DipProofError::IdentityNotFound)?; + let identity_details = IdentityProviderOf::::retrieve(&request.identifier).map_err(runtime_api::DipProofError::IdentityProvider)?; - DidMerkleRootGenerator::::generate_proof(&identity_details, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(runtime_api::DipProofError::MerkleProofError) + DidMerkleRootGenerator::::generate_proof(&identity_details, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(runtime_api::DipProofError::MerkleProof) } } } diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index a76acce619..c01296e23e 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -24,7 +24,7 @@ use frame_support::{ }, }; use kilt_support::Deposit; -use pallet_dip_provider::{traits::ProviderHooks as DipProviderHooks, IdentityCommitmentVersion}; +use pallet_dip_provider::{traits::ProviderHooks as DipProviderHooks, IdentityCommitmentOf, IdentityCommitmentVersion}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::traits::Get; @@ -74,7 +74,7 @@ where fn on_identity_committed( identifier: &Runtime::Identifier, submitter: &Runtime::AccountId, - _commitment: &Runtime::IdentityCommitment, + _commitment: &IdentityCommitmentOf, version: IdentityCommitmentVersion, ) -> Result<(), Self::Error> { let namespace = DepositsNamespace::get(); @@ -111,7 +111,7 @@ where fn on_commitment_removed( identifier: &Runtime::Identifier, _submitter: &Runtime::AccountId, - _commitment: &Runtime::IdentityCommitment, + _commitment: &IdentityCommitmentOf, version: pallet_dip_provider::IdentityCommitmentVersion, ) -> Result<(), Self::Error> { let namespace = DepositsNamespace::get(); diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs index 2674923442..e683b27eac 100644 --- a/pallets/pallet-deposit-storage/src/lib.rs +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -24,6 +24,7 @@ pub mod traits; pub use deposit::FixedDepositCollectorViaDepositsPallet; pub use pallet::*; +pub use traits::NoopDepositStorageHooks; #[frame_support::pallet(dev_mode)] pub mod pallet { diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index c513bbc130..e3a0a2133b 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -25,7 +25,7 @@ pub mod traits; mod origin; -pub use crate::{origin::*, pallet::*}; +pub use crate::{origin::*, pallet::*, traits::SuccessfulProofVerifier}; #[frame_support::pallet(dev_mode)] pub mod pallet { @@ -39,10 +39,9 @@ pub mod pallet { use crate::traits::IdentityProofVerifier; - pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< - ::RuntimeCall, - ::Identifier, - >>::VerificationResult; + pub type IdentityProofOf = <::ProofVerifier as IdentityProofVerifier>::Proof; + pub type RuntimeCallOf = ::RuntimeCall; + pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier>::VerificationResult; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -55,31 +54,17 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Preliminary filter to filter out calls before doing any heavier /// computations. - type DipCallOriginFilter: Contains<::RuntimeCall>; + type DipCallOriginFilter: Contains>; + /// The origin check for the `dispatch_as` call. + type DispatchOriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::AccountId>; /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; - /// The proof users must provide to operate with their higher-level - /// identity. Depending on the use cases, this proof can contain - /// heterogeneous bits of information that the proof verifier will - /// utilize. For instance, a proof could contain both a Merkle proof and - /// a DID signature. - type IdentityProof: Parameter; /// The details stored in this pallet associated with any given subject. type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; - type ProofVerificationError: Into; /// The logic of the proof verifier, called upon each execution of the /// `dispatch_as` extrinsic. - type ProofVerifier: IdentityProofVerifier< - ::RuntimeCall, - Self::Identifier, - Error = Self::ProofVerificationError, - Proof = Self::IdentityProof, - IdentityDetails = Self::LocalIdentityInfo, - Submitter = ::AccountId, - >; - /// The overarching runtime call type. + type ProofVerifier: IdentityProofVerifier; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; - /// The overarching runtime origin type. type RuntimeOrigin: From> + From<::RuntimeOrigin>; } @@ -89,8 +74,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// An identity with the provided identifier could not be found. - IdentityNotFound, /// The identity proof provided could not be successfully verified. InvalidProof(u16), /// The specified call is filtered by the DIP call origin filter. @@ -112,12 +95,10 @@ pub mod pallet { pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, - proof: T::IdentityProof, - call: Box<::RuntimeCall>, + proof: IdentityProofOf, + call: Box>, ) -> DispatchResult { - // TODO: Make origin check configurable, and require that it at least returns - // the submitter's account. - let submitter = ensure_signed(origin)?; + let submitter = T::DispatchOriginCheck::ensure_origin(origin)?; ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Filtered); let mut identity_entry = IdentityEntries::::get(&identifier); let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 0b7fa2a85c..9213166288 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -16,40 +16,42 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use sp_std::marker::PhantomData; +use frame_support::Parameter; -pub trait IdentityProofVerifier { - type Error; - type IdentityDetails; - type Proof; - type Submitter; +use crate::{Config, RuntimeCallOf}; + +pub trait IdentityProofVerifier +where + Runtime: Config, +{ + type Error: Into; + type Proof: Parameter; type VerificationResult; fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Option, + call: &RuntimeCallOf, + subject: &Runtime::Identifier, + submitter: &Runtime::AccountId, + identity_details: &mut Option, proof: Self::Proof, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(IdentityDetails, Proof, Submitter)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier; +impl IdentityProofVerifier for SuccessfulProofVerifier +where + Runtime: Config, { - type Error = (); - type IdentityDetails = IdentityDetails; - type Proof = Proof; - type Submitter = Submitter; + type Error = u16; + type Proof = (); type VerificationResult = (); fn verify_proof_for_call_against_details( - _call: &Call, - _subject: &Subject, - _submitter: &Self::Submitter, - _identity_details: &mut Option, + _call: &RuntimeCallOf, + _subject: &Runtime::Identifier, + _submitter: &Runtime::AccountId, + _identity_details: &mut Option, _proof: Self::Proof, ) -> Result { Ok(()) diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index f01b78c52e..45ac1d34da 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -22,7 +22,10 @@ pub mod traits; -pub use crate::pallet::*; +pub use crate::{ + pallet::*, + traits::{DefaultIdentityCommitmentGenerator, DefaultIdentityProvider, NoopHooks}, +}; #[frame_support::pallet(dev_mode)] pub mod pallet { @@ -30,13 +33,13 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; - use parity_scale_codec::FullCodec; - use sp_std::fmt::Debug; use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, ProviderHooks, SubmitterInfo}; + pub type IdentityCommitmentOf = + <::IdentityCommitmentGenerator as IdentityCommitmentGenerator>::Output; pub type IdentityProviderOf = ::IdentityProvider; - pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; + pub type IdentityOf = <::IdentityProvider as IdentityProvider>::Success; pub type IdentityCommitmentVersion = u16; pub const LATEST_COMMITMENT_VERSION: IdentityCommitmentVersion = 0; @@ -47,16 +50,8 @@ pub mod pallet { type CommitOriginCheck: EnsureOrigin; type CommitOrigin: SubmitterInfo; type Identifier: Parameter + MaxEncodedLen; - type IdentityCommitment: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; - type IdentityCommitmentGenerator: IdentityCommitmentGenerator< - Self::Identifier, - IdentityOf, - Error = Self::IdentityCommitmentGeneratorError, - Output = Self::IdentityCommitment, - >; - type IdentityCommitmentGeneratorError: Into; - type IdentityProvider: IdentityProvider; - type IdentityProviderError: Into; + type IdentityCommitmentGenerator: IdentityCommitmentGenerator; + type IdentityProvider: IdentityProvider; type ProviderHooks: ProviderHooks; type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -69,7 +64,7 @@ pub mod pallet { ::Identifier, Twox64Concat, IdentityCommitmentVersion, - ::IdentityCommitment, + IdentityCommitmentOf, >; #[pallet::pallet] @@ -79,24 +74,20 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - IdentityCommitted { + VersionedIdentityCommitted { identifier: T::Identifier, - commitment: T::IdentityCommitment, + commitment: IdentityCommitmentOf, version: IdentityCommitmentVersion, }, VersionedIdentityDeleted { identifier: T::Identifier, version: IdentityCommitmentVersion, }, - IdentityDeleted { - identifier: T::Identifier, - }, } #[pallet::error] pub enum Error { - IdentityNotFound, - LimitTooLow, + CommitmentNotFound, IdentityProvider(u16), IdentityCommitmentGenerator(u16), Hook(u16), @@ -116,14 +107,11 @@ pub mod pallet { T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); - let commitment = match T::IdentityProvider::retrieve(&identifier) { - Ok(None) => Err(Error::::IdentityNotFound), - Err(error) => Err(Error::::IdentityProvider(error.into())), - Ok(Some(identity)) => { - T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) - .map_err(|error| Error::::IdentityCommitmentGenerator(error.into())) - } - }?; + let identity = T::IdentityProvider::retrieve(&identifier) + .map_err(|error| Error::::IdentityProvider(error.into()))?; + let commitment = + T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) + .map_err(|error| Error::::IdentityCommitmentGenerator(error.into()))?; IdentityCommitments::::try_mutate(&identifier, commitment_version, |commitment_entry| { if let Some(old_commitment) = commitment_entry { @@ -142,7 +130,7 @@ pub mod pallet { T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) .map_err(|e| Error::::Hook(e.into()))?; *commitment_entry = Some(commitment.clone()); - Self::deposit_event(Event::::IdentityCommitted { + Self::deposit_event(Event::::VersionedIdentityCommitted { identifier: identifier.clone(), commitment, version: commitment_version, @@ -175,8 +163,9 @@ pub mod pallet { pub fn delete_identity_commitment_storage_entry( identifier: &T::Identifier, version: IdentityCommitmentVersion, - ) -> Result { - let commitment = IdentityCommitments::::take(identifier, version).ok_or(Error::::IdentityNotFound)?; + ) -> Result, DispatchError> { + let commitment = + IdentityCommitments::::take(identifier, version).ok_or(Error::::CommitmentNotFound)?; Self::deposit_event(Event::::VersionedIdentityDeleted { identifier: identifier.clone(), version, diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 52785c8ad0..8cf5c09146 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -19,82 +19,84 @@ use did::DidRawOrigin; use frame_support::sp_runtime::AccountId32; -use crate::{Config, IdentityCommitmentVersion}; +use crate::{Config, IdentityCommitmentOf, IdentityCommitmentVersion}; -pub use identity_generation::*; -pub mod identity_generation { +pub use identity_provision::*; +pub mod identity_provision { use super::*; use sp_std::marker::PhantomData; - pub trait IdentityCommitmentGenerator { - type Error; - type Output; + pub trait IdentityProvider + where + Runtime: Config, + { + type Error: Into; + type Success; - fn generate_commitment( - identifier: &Identifier, - identity: &Identity, - version: IdentityCommitmentVersion, - ) -> Result; + fn retrieve(identifier: &Runtime::Identifier) -> Result; } - // Implement the `IdentityCommitmentGenerator` by returning the `Default` value - // for the `Output` type. - pub struct DefaultIdentityCommitmentGenerator(PhantomData); + // Return the `Default` value if `Identity` adn `Details` both implement it. + pub struct DefaultIdentityProvider(PhantomData); - impl IdentityCommitmentGenerator - for DefaultIdentityCommitmentGenerator + impl IdentityProvider for DefaultIdentityProvider where - Output: Default, + Runtime: Config, + Identity: Default, { - type Error = (); - type Output = Output; + type Error = u16; + type Success = Identity; - fn generate_commitment( - _identifier: &Identifier, - _identity: &Identity, - _version: IdentityCommitmentVersion, - ) -> Result { - Ok(Output::default()) + fn retrieve(_identifier: &Runtime::Identifier) -> Result { + Ok(Identity::default()) } } } -pub use identity_provision::*; -pub mod identity_provision { - use sp_std::marker::PhantomData; - - pub trait IdentityProvider { - type Error; - type Success; +pub use identity_generation::*; +pub mod identity_generation { + use super::*; - fn retrieve(identifier: &Identifier) -> Result, Self::Error>; - } + use crate::IdentityOf; - // Return the `Default` value if `Identity` adn `Details` both implement it. - pub struct DefaultIdentityProvider(PhantomData); + use parity_scale_codec::{FullCodec, MaxEncodedLen}; + use scale_info::TypeInfo; + use sp_std::{fmt::Debug, marker::PhantomData}; - impl IdentityProvider for DefaultIdentityProvider + pub trait IdentityCommitmentGenerator where - Identity: Default, + Runtime: Config, + Runtime::IdentityProvider: IdentityProvider, { - type Error = (); - type Success = Identity; + type Error: Into; + type Output: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; - fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { - Ok(Some(Identity::default())) - } + fn generate_commitment( + identifier: &Runtime::Identifier, + identity: &IdentityOf, + version: IdentityCommitmentVersion, + ) -> Result; } - // Always return `None`. Might be useful for tests. - pub struct NoneIdentityProvider; + // Implement the `IdentityCommitmentGenerator` by returning the `Default` value + // for the `Output` type. + pub struct DefaultIdentityCommitmentGenerator(PhantomData); - impl IdentityProvider for NoneIdentityProvider { - type Error = (); - type Success = (); + impl IdentityCommitmentGenerator for DefaultIdentityCommitmentGenerator + where + Runtime: Config, + Output: Default + Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen, + { + type Error = u16; + type Output = Output; - fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { - Ok(None) + fn generate_commitment( + _identifier: &Runtime::Identifier, + _identity: &IdentityOf, + _version: IdentityCommitmentVersion, + ) -> Result { + Ok(Output::default()) } } } @@ -133,14 +135,14 @@ where fn on_identity_committed( identifier: &Runtime::Identifier, submitter: &Runtime::AccountId, - commitment: &Runtime::IdentityCommitment, + commitment: &IdentityCommitmentOf, version: IdentityCommitmentVersion, ) -> Result<(), Self::Error>; fn on_commitment_removed( identifier: &Runtime::Identifier, submitter: &Runtime::AccountId, - commitment: &Runtime::IdentityCommitment, + commitment: &IdentityCommitmentOf, version: IdentityCommitmentVersion, ) -> Result<(), Self::Error>; } @@ -156,7 +158,7 @@ where fn on_commitment_removed( _identifier: &Runtime::Identifier, _submitter: &Runtime::AccountId, - _commitment: &Runtime::IdentityCommitment, + _commitment: &IdentityCommitmentOf, _version: IdentityCommitmentVersion, ) -> Result<(), Self::Error> { Ok(()) @@ -165,7 +167,7 @@ where fn on_identity_committed( _identifier: &Runtime::Identifier, _submitter: &Runtime::AccountId, - _commitment: &Runtime::IdentityCommitment, + _commitment: &IdentityCommitmentOf, _version: IdentityCommitmentVersion, ) -> Result<(), Self::Error> { Ok(()) diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index f4255c438b..134c65de9b 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -18,98 +18,83 @@ use did::did_details::DidDetails; use frame_system::pallet_prelude::BlockNumberFor; -use kilt_dip_support::{ - merkle::RevealedWeb3Name, - utils::{CombineIdentityFrom, CombinedIdentityResult}, -}; +use kilt_dip_support::merkle::RevealedWeb3Name; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_std::{marker::PhantomData, vec::Vec}; +use sp_std::vec::Vec; #[derive(Encode, Decode, TypeInfo)] -pub enum DidIdentityProviderError { +pub enum LinkedDidInfoProviderError { DidNotFound, + DidDeleted, Internal, } -impl From for u16 { - fn from(value: DidIdentityProviderError) -> Self { +impl From for u16 { + fn from(value: LinkedDidInfoProviderError) -> Self { match value { - DidIdentityProviderError::DidNotFound => 0, - DidIdentityProviderError::Internal => u16::MAX, + LinkedDidInfoProviderError::DidNotFound => 0, + LinkedDidInfoProviderError::DidDeleted => 1, + LinkedDidInfoProviderError::Internal => u16::MAX, } } } -pub struct DidIdentityProvider(PhantomData); +pub type Web3OwnershipOf = + RevealedWeb3Name<::Web3Name, BlockNumberFor>; -impl IdentityProvider for DidIdentityProvider +pub struct LinkedDidInfoOf where - T: did::Config, + Runtime: did::Config + pallet_web3_names::Config, { - type Error = DidIdentityProviderError; - type Success = DidDetails; - - fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { - match ( - did::Pallet::::get_did(identifier), - did::Pallet::::get_deleted_did(identifier), - ) { - (Some(details), _) => Ok(Some(details)), - (_, Some(_)) => Ok(None), - _ => Err(DidIdentityProviderError::DidNotFound), - } - } -} - -pub type Web3OwnershipOf = RevealedWeb3Name<::Web3Name, BlockNumberFor>; - -pub struct DidWeb3NameProvider(PhantomData); - -impl IdentityProvider for DidWeb3NameProvider -where - T: pallet_web3_names::Config, -{ - type Error = DidIdentityProviderError; - type Success = Web3OwnershipOf; - - fn retrieve(identifier: &T::Web3NameOwner) -> Result, Self::Error> { - let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) else { - return Ok(None); - }; - let Some(details) = pallet_web3_names::Pallet::::owner(&web3_name) else { - log::error!( - "Inconsistent reverse map pallet_web3_names::owner(web3_name). Cannot find owner for web3name {:#?}", - web3_name - ); - return Err(DidIdentityProviderError::Internal); - }; - Ok(Some(Web3OwnershipOf:: { - web3_name, - claimed_at: details.claimed_at, - })) - } + pub did_details: DidDetails, + pub web3_name_details: Option>, + pub linked_accounts: Vec, } -pub struct DidLinkedAccountsProvider(PhantomData); +pub struct LinkedDidInfoProvider; -impl IdentityProvider for DidLinkedAccountsProvider +impl IdentityProvider for LinkedDidInfoProvider where - T: pallet_did_lookup::Config, + Runtime: did::Config::Identifier> + + pallet_web3_names::Config::Identifier> + + pallet_did_lookup::Config::Identifier> + + pallet_dip_provider::Config, { - type Error = DidIdentityProviderError; - type Success = Vec; + type Error = LinkedDidInfoProviderError; + type Success = LinkedDidInfoOf; - fn retrieve(identifier: &T::DidIdentifier) -> Result, Self::Error> { - Ok(Some( - pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier).collect(), - )) + fn retrieve(identifier: &Runtime::Identifier) -> Result { + let did_details = match ( + did::Pallet::::get_did(identifier), + did::Pallet::::get_deleted_did(identifier), + ) { + (Some(details), _) => Ok(details), + (_, Some(_)) => Err(LinkedDidInfoProviderError::DidDeleted), + _ => Err(LinkedDidInfoProviderError::DidNotFound), + }?; + let web3_name_details = if let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) { + let Some(ownership) = pallet_web3_names::Pallet::::owner(&web3_name) else { + log::error!( + "Inconsistent reverse map pallet_web3_names::owner(web3_name). Cannot find owner for web3name {:#?}", + web3_name + ); + return Err(LinkedDidInfoProviderError::Internal); + }; + Ok(Some(Web3OwnershipOf:: { + web3_name, + claimed_at: ownership.claimed_at, + })) + } else { + Ok(None) + }?; + let linked_accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier).collect(); + Ok(LinkedDidInfoOf { + did_details, + web3_name_details, + linked_accounts, + }) } } - -pub type LinkedDidInfoProviderOf = - CombineIdentityFrom, DidWeb3NameProvider, DidLinkedAccountsProvider>; -pub type LinkedDidInfoOf = - CombinedIdentityResult>, Option>, Option>>; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index d7af9c87b5..ec62b3d6a8 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -20,7 +20,10 @@ use frame_support::RuntimeDebug; use frame_system::pallet_prelude::BlockNumberFor; use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::{traits::IdentityCommitmentGenerator, IdentityCommitmentVersion}; +use pallet_dip_provider::{ + traits::{IdentityCommitmentGenerator, IdentityProvider}, + IdentityCommitmentVersion, IdentityOf, +}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; @@ -28,7 +31,7 @@ use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHas use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; -use crate::{dip::did::LinkedDidInfoOf, DidIdentifier}; +use crate::dip::did::LinkedDidInfoOf; pub type BlindedValue = Vec; pub type DidMerkleProofOf = DidMerkleProof< @@ -51,7 +54,6 @@ pub struct CompleteMerkleProof { #[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo, PartialEq)] pub enum DidMerkleProofError { UnsupportedVersion, - DidNotFound, KeyNotFound, LinkedAccountNotFound, Web3NameNotFound, @@ -62,10 +64,9 @@ impl From for u16 { fn from(value: DidMerkleProofError) -> Self { match value { DidMerkleProofError::UnsupportedVersion => 0, - DidMerkleProofError::DidNotFound => 1, - DidMerkleProofError::KeyNotFound => 2, - DidMerkleProofError::LinkedAccountNotFound => 3, - DidMerkleProofError::Web3NameNotFound => 4, + DidMerkleProofError::KeyNotFound => 1, + DidMerkleProofError::LinkedAccountNotFound => 2, + DidMerkleProofError::Web3NameNotFound => 3, DidMerkleProofError::Internal => u16::MAX, } } @@ -89,10 +90,11 @@ pub mod v0 { where Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { - // Fails if the DID details do not exist. - let (Some(did_details), web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { - return Err(DidMerkleProofError::DidNotFound); - }; + let LinkedDidInfoOf { + did_details, + web3_name_details, + linked_accounts, + } = identity; let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); @@ -186,30 +188,28 @@ pub mod v0 { })?; // Linked accounts - if let Some(linked_accounts) = linked_accounts { - linked_accounts - .iter() - .try_for_each(|linked_account| -> Result<(), DidMerkleProofError> { - let linked_account_leaf = - ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); - trie_builder - .insert( - linked_account_leaf.encoded_key().as_slice(), - linked_account_leaf.encoded_value().as_slice(), - ) - .map_err(|_| { - log::error!( - "Failed to insert linked account in the trie builder. Linked account leaf: {:#?}", - linked_account_leaf - ); - DidMerkleProofError::Internal - })?; - Ok(()) - })?; - } + linked_accounts + .iter() + .try_for_each(|linked_account| -> Result<(), DidMerkleProofError> { + let linked_account_leaf = + ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); + trie_builder + .insert( + linked_account_leaf.encoded_key().as_slice(), + linked_account_leaf.encoded_value().as_slice(), + ) + .map_err(|_| { + log::error!( + "Failed to insert linked account in the trie builder. Linked account leaf: {:#?}", + linked_account_leaf + ); + DidMerkleProofError::Internal + })?; + Ok(()) + })?; // Web3name, if present - if let Some(web3name_details) = web3_name { + if let Some(web3name_details) = web3_name_details { let web3_name_leaf = ProofLeafOf::::Web3Name( web3name_details.web3_name.clone().into(), web3name_details.claimed_at.into(), @@ -243,10 +243,11 @@ pub mod v0 { K: Iterator>, A: Iterator, { - // Fails if the DID details do not exist. - let (Some(did_details), linked_web3_name, linked_accounts) = (&identity.a, &identity.b, &identity.c) else { - return Err(DidMerkleProofError::DidNotFound); - }; + let LinkedDidInfoOf { + did_details, + web3_name_details, + linked_accounts, + } = identity; let mut db = MemoryDB::default(); let root = calculate_root_with_db(identity, &mut db)?; @@ -278,11 +279,6 @@ pub mod v0 { )) }) .chain(account_ids.map(|account_id| -> Result<_, DidMerkleProofError> { - let Some(linked_accounts) = linked_accounts else { - // Directly LinkedAccountNotFound since there's no linked accounts to check - // against. - return Err(DidMerkleProofError::LinkedAccountNotFound); - }; if linked_accounts.contains(account_id) { Ok(RevealedDidMerkleProofLeaf::LinkedAccount( account_id.clone().into(), @@ -294,7 +290,7 @@ pub mod v0 { })) .collect::, _>>()?; - match (should_include_web3_name, linked_web3_name) { + match (should_include_web3_name, web3_name_details) { // If web3name should be included and it exists... (true, Some(web3name_details)) => { leaves.push(RevealedDidMerkleProofLeaf::Web3Name( @@ -328,10 +324,11 @@ pub mod v0 { } pub(super) fn generate_commitment( - identity: &LinkedDidInfoOf, + identity: &IdentityOf, ) -> Result where - Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config + pallet_dip_provider::Config, + Runtime::IdentityProvider: IdentityProvider>, { let mut db = MemoryDB::default(); calculate_root_with_db(identity, &mut db) @@ -340,18 +337,19 @@ pub mod v0 { pub struct DidMerkleRootGenerator(PhantomData); -impl IdentityCommitmentGenerator> for DidMerkleRootGenerator +impl IdentityCommitmentGenerator for DidMerkleRootGenerator where - Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, + Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config + pallet_dip_provider::Config, + Runtime::IdentityProvider: IdentityProvider>, { type Error = DidMerkleProofError; type Output = Runtime::Hash; fn generate_commitment( - _identifier: &DidIdentifier, - identity: &LinkedDidInfoOf, + _identifier: &Runtime::Identifier, + identity: &IdentityOf, version: IdentityCommitmentVersion, - ) -> Result { + ) -> Result { match version { 0 => v0::generate_commitment::(identity), _ => Err(DidMerkleProofError::UnsupportedVersion), From 10385bb3b3c302a1bdbd6e4188b034a2f01047d1 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 21 Nov 2023 11:06:49 +0000 Subject: [PATCH 16/28] fix: origin checks for provider and consumer pallets (#581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensuring the origin is a DID origin for the implementor of the provider pallet might not be enough. They need to check that the origin indeed matches the identifier being acted upon. Replacing `EnsureOrigin` with `EnsureOriginWithArg` gives access to that information. Tests will ALL be implemented in a different PR 😄 --- pallets/did/src/origin.rs | 34 +++++++++++++++++++++++++- pallets/pallet-dip-consumer/src/lib.rs | 15 +++++++++--- pallets/pallet-dip-provider/src/lib.rs | 12 ++++----- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/pallets/did/src/origin.rs b/pallets/did/src/origin.rs index 92919482fc..e5ac0ba53a 100644 --- a/pallets/did/src/origin.rs +++ b/pallets/did/src/origin.rs @@ -18,7 +18,7 @@ use frame_support::{ codec::{Decode, Encode}, - traits::EnsureOrigin, + traits::{EnsureOrigin, EnsureOriginWithArg}, }; use kilt_support::traits::CallSources; use parity_scale_codec::MaxEncodedLen; @@ -66,6 +66,38 @@ where } } +impl EnsureOriginWithArg + for EnsureDidOrigin +where + OuterOrigin: Into, OuterOrigin>> + + From> + + Clone, + DidIdentifier: PartialEq + Clone, + AccountId: Clone + Decode, +{ + type Success = DidRawOrigin; + + fn try_origin(o: OuterOrigin, a: &DidIdentifier) -> Result { + let did_origin: DidRawOrigin = o.clone().into()?; + if did_origin.id == *a { + Ok(did_origin) + } else { + Err(o) + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &DidIdentifier) -> Result { + let zero_account_id = AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"); + + Ok(OuterOrigin::from(DidRawOrigin { + id: a.clone(), + submitter: zero_account_id, + })) + } +} + impl CallSources for DidRawOrigin { diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index e3a0a2133b..0c90942b23 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -31,7 +31,12 @@ pub use crate::{origin::*, pallet::*, traits::SuccessfulProofVerifier}; pub mod pallet { use super::*; - use frame_support::{dispatch::Dispatchable, pallet_prelude::*, traits::Contains, Twox64Concat}; + use frame_support::{ + dispatch::Dispatchable, + pallet_prelude::*, + traits::{Contains, EnsureOriginWithArg}, + Twox64Concat, + }; use frame_system::pallet_prelude::*; use parity_scale_codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; @@ -56,7 +61,11 @@ pub mod pallet { /// computations. type DipCallOriginFilter: Contains>; /// The origin check for the `dispatch_as` call. - type DispatchOriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::AccountId>; + type DispatchOriginCheck: EnsureOriginWithArg< + ::RuntimeOrigin, + Self::Identifier, + Success = Self::AccountId, + >; /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; /// The details stored in this pallet associated with any given subject. @@ -98,7 +107,7 @@ pub mod pallet { proof: IdentityProofOf, call: Box>, ) -> DispatchResult { - let submitter = T::DispatchOriginCheck::ensure_origin(origin)?; + let submitter = T::DispatchOriginCheck::ensure_origin(origin, &identifier)?; ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Filtered); let mut identity_entry = IdentityEntries::::get(&identifier); let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 45ac1d34da..b9819b51bd 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -31,7 +31,7 @@ pub use crate::{ pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; + use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; use frame_system::pallet_prelude::*; use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, ProviderHooks, SubmitterInfo}; @@ -47,7 +47,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type CommitOriginCheck: EnsureOrigin; + type CommitOriginCheck: EnsureOriginWithArg; type CommitOrigin: SubmitterInfo; type Identifier: Parameter + MaxEncodedLen; type IdentityCommitmentGenerator: IdentityCommitmentGenerator; @@ -103,8 +103,8 @@ pub mod pallet { identifier: T::Identifier, version: Option, ) -> DispatchResult { - let dispatcher = - T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; + let dispatcher = T::CommitOriginCheck::ensure_origin(origin, &identifier) + .map(|e: ::CommitOrigin| e.submitter())?; let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); let identity = T::IdentityProvider::retrieve(&identifier) @@ -148,8 +148,8 @@ pub mod pallet { identifier: T::Identifier, version: Option, ) -> DispatchResult { - let dispatcher = - T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; + let dispatcher = T::CommitOriginCheck::ensure_origin(origin, &identifier) + .map(|e: ::CommitOrigin| e.submitter())?; let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); let commitment = Self::delete_identity_commitment_storage_entry(&identifier, commitment_version)?; From 55e45f8f952bd07ec3d6925ac26455c95a92fe9e Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Nov 2023 16:05:45 +0100 Subject: [PATCH 17/28] Add pallet-utility to provider and DID runtime API --- Cargo.lock | 2 + dip-template/runtimes/dip-consumer/Cargo.toml | 1 + dip-template/runtimes/dip-provider/Cargo.toml | 7 +- dip-template/runtimes/dip-provider/src/lib.rs | 102 ++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ca9bc7a55e..d7c2a117ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2534,6 +2534,7 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "kilt-dip-support", + "kilt-runtime-api-did", "kilt-runtime-api-dip-provider", "kilt-support", "log", @@ -2549,6 +2550,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "pallet-web3-names", "parachain-info", "parity-scale-codec", diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index aae167b098..078f172253 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -121,4 +121,5 @@ runtime-benchmarks = [ "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 19da12ee26..58db107592 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -22,6 +22,7 @@ scale-info = {workspace = true, features = ["derive"]} did.workspace = true kilt-support.workspace = true kilt-dip-support.workspace = true +kilt-runtime-api-did.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-deposit-storage.workspace = true pallet-did-lookup.workspace = true @@ -42,6 +43,7 @@ pallet-sudo.workspace = true pallet-timestamp.workspace = true pallet-transaction-payment.workspace = true pallet-transaction-payment-rpc-runtime-api.workspace = true +pallet-utility.workspace = true sp-api.workspace = true sp-block-builder.workspace = true sp-consensus-aura.workspace = true @@ -75,6 +77,7 @@ std = [ "did/std", "kilt-support/std", "kilt-dip-support/std", + "kilt-runtime-api-did/std", "kilt-runtime-api-dip-provider/std", "pallet-deposit-storage/std", "pallet-did-lookup/std", @@ -93,6 +96,7 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-utility/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -121,5 +125,6 @@ runtime-benchmarks = [ "pallet-web3-names/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", - "frame-support/runtime-benchmarks" + "frame-support/runtime-benchmarks", + "pallet-utility/runtime-benchmarks" ] diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index cab15778cb..a0f474b817 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -22,6 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_web3_names::web3_name::AsciiWeb3Name; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -119,6 +120,7 @@ construct_runtime!( Timestamp: pallet_timestamp = 2, ParachainInfo: parachain_info = 3, Sudo: pallet_sudo = 4, + Utility: pallet_utility = 5, // Money Balances: pallet_balances = 10, @@ -269,6 +271,13 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = (); } +impl pallet_utility::Config for Runtime { + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; impl pallet_balances::Config for Runtime { @@ -569,6 +578,99 @@ impl_runtime_apis! { } } + impl kilt_runtime_api_did::Did< + Block, + DidIdentifier, + AccountId, + LinkableAccountId, + Balance, + Hash, + BlockNumber + > for Runtime { + fn query_by_web3_name(name: Vec) -> Option + > { + let name: pallet_web3_names::web3_name::AsciiWeb3Name = name.try_into().ok()?; + pallet_web3_names::Owner::::get(&name) + .and_then(|owner_info| { + did::Did::::get(&owner_info.owner).map(|details| (owner_info, details)) + }) + .map(|(owner_info, details)| { + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix( + &owner_info.owner, + ).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&owner_info.owner).map(|e| From::from(e.1)).collect(); + + kilt_runtime_api_did::RawDidLinkedInfo{ + identifier: owner_info.owner, + w3n: Some(name.into()), + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_by_account(account: LinkableAccountId) -> Option< + kilt_runtime_api_did::RawDidLinkedInfo< + DidIdentifier, + AccountId, + LinkableAccountId, + Balance, + Hash, + BlockNumber + > + > { + pallet_did_lookup::ConnectedDids::::get(account) + .and_then(|owner_info| { + did::Did::::get(&owner_info.did).map(|details| (owner_info, details)) + }) + .map(|(connection_record, details)| { + let w3n = pallet_web3_names::Names::::get(&connection_record.did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&connection_record.did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&connection_record.did).map(|e| From::from(e.1)).collect(); + + kilt_runtime_api_did::RawDidLinkedInfo { + identifier: connection_record.did, + w3n, + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query(did: DidIdentifier) -> Option< + kilt_runtime_api_did::RawDidLinkedInfo< + DidIdentifier, + AccountId, + LinkableAccountId, + Balance, + Hash, + BlockNumber + > + > { + let details = did::Did::::get(&did)?; + let w3n = pallet_web3_names::Names::::get(&did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&did).map(|e| From::from(e.1)).collect(); + + Some(kilt_runtime_api_did::RawDidLinkedInfo { + identifier: did, + w3n, + accounts, + service_endpoints, + details: details.into(), + }) + } + } + impl kilt_runtime_api_dip_provider::DipProvider>, runtime_api::DipProofError> for Runtime { fn generate_proof(request: runtime_api::DipProofRequest) -> Result>, runtime_api::DipProofError> { let identity_details = IdentityProviderOf::::retrieve(&request.identifier).map_err(runtime_api::DipProofError::IdentityProvider)?; From 5afb750bde0c5a8ab2bd32a1a2b2eeb761e78b4e Mon Sep 17 00:00:00 2001 From: Adel Golghalyani <48685760+Ad96el@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:38:31 +0100 Subject: [PATCH 18/28] feat: benchmarks (#585) ## fixes [KILTProtocol/ticket#2551](https://github.com/KILTprotocol/ticket/issues/2551) This pull request introduces benchmark logic for the DIP templates. To calculate default weights, a dummy implementation of the pallets in the Peregrine runtime is defined. After the PR is approved, I will remove the dummy implementation. ## Leftover tasks - [x] Make the provider and consumer binaries compiles for benchmarking. Currently the `VersionedSiblingParachainDipStateProof` type does not implement the `WorstCase` trait, which is a blocker to use it in benchmarks - [x] Adjust the `DipProofVerifier` logic to allow any calls to be dispatched when being benchmarked - [x] Make sure everything compiles - [x] Make sure benchmarks can actually be run for both provider and consumer runtimes --------- Co-authored-by: Antonio Antonino --- Cargo.lock | 920 +++++++------ crates/kilt-dip-support/Cargo.toml | 4 + crates/kilt-dip-support/src/did.rs | 94 +- crates/kilt-dip-support/src/export/child.rs | 11 +- crates/kilt-dip-support/src/export/common.rs | 36 +- crates/kilt-dip-support/src/export/sibling.rs | 61 +- crates/kilt-dip-support/src/lib.rs | 1 + crates/kilt-dip-support/src/merkle.rs | 113 +- crates/kilt-dip-support/src/state_proofs.rs | 73 +- crates/kilt-dip-support/src/utils.rs | 60 + dip-template/nodes/dip-consumer/Cargo.toml | 6 + dip-template/nodes/dip-consumer/src/cli.rs | 3 + .../nodes/dip-consumer/src/command.rs | 41 + dip-template/nodes/dip-provider/Cargo.toml | 6 + dip-template/nodes/dip-provider/src/cli.rs | 3 + .../nodes/dip-provider/src/command.rs | 41 + dip-template/runtimes/dip-consumer/Cargo.toml | 16 + dip-template/runtimes/dip-consumer/src/dip.rs | 10 +- dip-template/runtimes/dip-consumer/src/lib.rs | 58 +- .../dip-consumer/src/origin_adapter.rs | 3 +- .../dip-consumer/src/weights/frame_system.rs | 193 +++ .../runtimes/dip-consumer/src/weights/mod.rs | 21 + .../src/weights/pallet_dip_consumer.rs | 83 ++ .../src/weights/pallet_relay_store.rs | 83 ++ dip-template/runtimes/dip-provider/Cargo.toml | 21 +- dip-template/runtimes/dip-provider/src/dip.rs | 54 +- dip-template/runtimes/dip-provider/src/lib.rs | 67 +- .../runtimes/dip-provider/src/weights/did.rs | 1217 +++++++++++++++++ .../dip-provider/src/weights/frame_system.rs | 193 +++ .../runtimes/dip-provider/src/weights/mod.rs | 24 + .../src/weights/pallet_deposit_storage.rs | 85 ++ .../src/weights/pallet_did_lookup.rs | 321 +++++ .../src/weights/pallet_dip_provider.rs | 125 ++ .../src/weights/pallet_web3_names.rs | 265 ++++ pallets/ctype/src/lib.rs | 2 +- pallets/did/src/did_details.rs | 7 + pallets/pallet-deposit-storage/Cargo.toml | 28 +- .../src/benchmarking.rs | 85 ++ .../src/default_weights.rs | 73 + pallets/pallet-deposit-storage/src/lib.rs | 27 +- pallets/pallet-deposit-storage/src/mock.rs | 129 ++ pallets/pallet-deposit-storage/src/traits.rs | 36 + pallets/pallet-dip-consumer/Cargo.toml | 39 +- .../pallet-dip-consumer/src/benchmarking.rs | 85 ++ .../src/default_weights.rs | 65 + pallets/pallet-dip-consumer/src/lib.rs | 29 +- pallets/pallet-dip-consumer/src/mock.rs | 99 ++ pallets/pallet-dip-provider/Cargo.toml | 46 +- .../pallet-dip-provider/src/benchmarking.rs | 109 ++ .../src/default_weights.rs | 108 ++ pallets/pallet-dip-provider/src/lib.rs | 22 +- pallets/pallet-dip-provider/src/mock.rs | 100 ++ pallets/pallet-dip-provider/src/traits.rs | 13 +- pallets/pallet-migration/Cargo.toml | 11 +- pallets/pallet-migration/src/benchmarking.rs | 2 +- pallets/pallet-migration/src/mock.rs | 4 +- pallets/pallet-relay-store/Cargo.toml | 17 +- .../pallet-relay-store/src/benchmarking.rs | 93 ++ .../pallet-relay-store/src/default_weights.rs | 92 ++ pallets/pallet-relay-store/src/lib.rs | 26 +- pallets/pallet-relay-store/src/mock.rs | 102 ++ .../public-credentials/src/benchmarking.rs | 14 +- pallets/public-credentials/src/mock.rs | 4 +- runtimes/common/Cargo.toml | 6 +- runtimes/common/src/assets.rs | 4 +- runtimes/common/src/dip/did.rs | 141 +- runtimes/common/src/dip/merkle.rs | 22 +- support/src/benchmark.rs | 23 + support/src/lib.rs | 3 + support/src/mock.rs | 34 +- support/src/traits.rs | 32 +- 71 files changed, 5478 insertions(+), 566 deletions(-) create mode 100644 dip-template/runtimes/dip-consumer/src/weights/frame_system.rs create mode 100644 dip-template/runtimes/dip-consumer/src/weights/mod.rs create mode 100644 dip-template/runtimes/dip-consumer/src/weights/pallet_dip_consumer.rs create mode 100644 dip-template/runtimes/dip-consumer/src/weights/pallet_relay_store.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/did.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/frame_system.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/mod.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/pallet_deposit_storage.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/pallet_did_lookup.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/pallet_dip_provider.rs create mode 100644 dip-template/runtimes/dip-provider/src/weights/pallet_web3_names.rs create mode 100644 pallets/pallet-deposit-storage/src/benchmarking.rs create mode 100644 pallets/pallet-deposit-storage/src/default_weights.rs create mode 100644 pallets/pallet-deposit-storage/src/mock.rs create mode 100644 pallets/pallet-dip-consumer/src/benchmarking.rs create mode 100644 pallets/pallet-dip-consumer/src/default_weights.rs create mode 100644 pallets/pallet-dip-consumer/src/mock.rs create mode 100644 pallets/pallet-dip-provider/src/benchmarking.rs create mode 100644 pallets/pallet-dip-provider/src/default_weights.rs create mode 100644 pallets/pallet-dip-provider/src/mock.rs create mode 100644 pallets/pallet-relay-store/src/benchmarking.rs create mode 100644 pallets/pallet-relay-store/src/default_weights.rs create mode 100644 pallets/pallet-relay-store/src/mock.rs create mode 100644 support/src/benchmark.rs diff --git a/Cargo.lock b/Cargo.lock index d7c2a117ef..741ea0e370 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,25 +149,26 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.11", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -280,9 +281,9 @@ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "array-bytes" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" +checksum = "de17a919934ad8c5cc99a1a74de4e2dab95d6121a8f27f94755ff525b630382c" [[package]] name = "arrayref" @@ -382,28 +383,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-io" -version = "1.13.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" dependencies = [ - "async-lock", - "autocfg", + "async-lock 3.1.1", "cfg-if", "concurrent-queue", + "futures-io", "futures-lite", - "log", "parking", "polling", - "rustix 0.37.25", + "rustix 0.38.25", "slab", - "socket2 0.4.9", + "tracing", "waker-fn", + "windows-sys 0.48.0", ] [[package]] @@ -412,7 +413,18 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655b9c7fe787d3b25cc0f804a1a8401790f0c5bc395beb5a64dc77d8de079105" +dependencies = [ + "event-listener 3.1.0", + "event-listener-strategy", + "pin-project-lite 0.2.13", ] [[package]] @@ -423,7 +435,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -434,7 +446,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -541,9 +553,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -596,7 +608,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -751,9 +763,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" dependencies = [ "memchr", "serde", @@ -826,9 +838,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" dependencies = [ "serde", ] @@ -900,25 +912,24 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chacha20" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher 0.3.0", + "cipher 0.4.4", "cpufeatures", - "zeroize", ] [[package]] name = "chacha20poly1305" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead 0.4.3", + "aead 0.5.2", "chacha20", - "cipher 0.3.0", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -976,6 +987,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -1000,9 +1012,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -1010,9 +1022,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -1022,27 +1034,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "coarsetime" -version = "0.1.29" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a73ef0d00d14301df35d0f13f5ea32344de6b00837485c358458f1e7f2d27db4" +checksum = "71367d3385c716342014ad17e3d19f7788ae514885a1f4c24f500260fb365e1a" dependencies = [ "libc", "once_cell", @@ -1068,12 +1080,12 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comfy-table" -version = "7.0.1" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab77dbd8adecaf3f0db40581631b995f312a8a5ae3aa9993188bb8f23d83a5b" +checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" dependencies = [ - "strum", - "strum_macros", + "strum 0.25.0", + "strum_macros 0.25.3", "unicode-width", ] @@ -1113,23 +1125,21 @@ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "const-random" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" dependencies = [ "const-random-macro", - "proc-macro-hack", ] [[package]] name = "const-random-macro" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "once_cell", - "proc-macro-hack", "tiny-keccak", ] @@ -1191,9 +1201,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1307,9 +1317,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" @@ -1383,9 +1393,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -1737,7 +1747,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2044,20 +2054,20 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "cxx" -version = "1.0.109" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c390c123d671cc547244943ecad81bdaab756c6ea332d9ca9c1f48d952a24895" +checksum = "7129e341034ecb940c9072817cd9007974ea696844fc4dd582dc1653a7fbe2e8" dependencies = [ "cc", "cxxbridge-flags", @@ -2067,9 +2077,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.109" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d3d3ac9ffb900304edf51ca719187c779f4001bb544f26c4511d621de905cf" +checksum = "a2a24f3f5f8eed71936f21e570436f024f5c2e25628f7496aa7ccd03b90109d5" dependencies = [ "cc", "codespan-reporting", @@ -2077,24 +2087,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "cxxbridge-flags" -version = "1.0.109" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94415827ecfea0f0c74c8cad7d1a86ddb3f05354d6a6ddeda0adee5e875d2939" +checksum = "06fdd177fc61050d63f67f5bd6351fac6ab5526694ea8e359cd9cd3b75857f44" [[package]] name = "cxxbridge-macro" -version = "1.0.109" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33dbbe9f5621c9247f97ec14213b04f350bff4b6cebefe834c60055db266ecf" +checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2134,15 +2144,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2150,9 +2160,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" dependencies = [ "data-encoding", "syn 1.0.109", @@ -2310,7 +2320,7 @@ name = "did" version = "1.12.0-dev" dependencies = [ "ctype", - "env_logger 0.10.0", + "env_logger 0.10.1", "fluent-uri", "frame-benchmarking", "frame-support", @@ -2428,10 +2438,13 @@ dependencies = [ "cumulus-primitives-utility", "did", "dip-provider-runtime-template", + "frame-benchmarking", "frame-executive", "frame-support", "frame-system", + "frame-system-benchmarking", "frame-system-rpc-runtime-api", + "hex-literal 0.3.4", "kilt-dip-support", "pallet-aura", "pallet-authorship", @@ -2463,6 +2476,7 @@ dependencies = [ "sp-std", "sp-transaction-pool", "sp-version", + "sp-weights", "substrate-wasm-builder", ] @@ -2529,10 +2543,13 @@ dependencies = [ "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", + "frame-benchmarking", "frame-executive", "frame-support", "frame-system", + "frame-system-benchmarking", "frame-system-rpc-runtime-api", + "hex-literal 0.3.4", "kilt-dip-support", "kilt-runtime-api-did", "kilt-runtime-api-dip-provider", @@ -2567,6 +2584,7 @@ dependencies = [ "sp-std", "sp-transaction-pool", "sp-version", + "sp-weights", "substrate-wasm-builder", ] @@ -2619,7 +2637,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2643,7 +2661,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.38", + "syn 2.0.39", "termcolor", "walkdir", ] @@ -2683,9 +2701,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "ecdsa" @@ -2701,15 +2719,15 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der 0.7.8", "digest 0.10.7", - "elliptic-curve 0.13.6", + "elliptic-curve 0.13.8", "rfc6979 0.4.0", - "signature 2.1.0", + "signature 2.2.0", "spki 0.7.2", ] @@ -2729,7 +2747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8 0.10.2", - "signature 2.1.0", + "signature 2.2.0", ] [[package]] @@ -2746,15 +2764,16 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" dependencies = [ "curve25519-dalek 4.1.1", "ed25519 2.2.3", "rand_core 0.6.4", "serde", "sha2 0.10.8", + "subtle", "zeroize", ] @@ -2802,12 +2821,12 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", - "crypto-bigint 0.5.3", + "crypto-bigint 0.5.5", "digest 0.10.7", "ff 0.13.0", "generic-array 0.14.7", @@ -2854,7 +2873,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2865,7 +2884,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2883,9 +2902,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -2908,9 +2927,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", "windows-sys 0.48.0", @@ -2922,6 +2941,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "event-listener-strategy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +dependencies = [ + "event-listener 3.1.0", + "pin-project-lite 0.2.13", +] + [[package]] name = "exit-future" version = "0.2.0" @@ -2965,7 +3005,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2980,15 +3020,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -3051,9 +3082,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "file-per-thread-logger" @@ -3061,7 +3092,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger 0.10.0", + "env_logger 0.10.1", "log", ] @@ -3250,7 +3281,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3369,7 +3400,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3381,7 +3412,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3391,7 +3422,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3451,9 +3482,12 @@ dependencies = [ [[package]] name = "fs-err" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] [[package]] name = "fs2" @@ -3471,7 +3505,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" dependencies = [ - "rustix 0.38.19", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -3483,9 +3517,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -3498,9 +3532,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -3508,15 +3542,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -3526,34 +3560,29 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-lite" -version = "1.13.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" dependencies = [ - "fastrand 1.9.0", "futures-core", - "futures-io", - "memchr", - "parking", "pin-project-lite 0.2.13", - "waker-fn", ] [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3569,15 +3598,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-timer" @@ -3587,9 +3616,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -3655,9 +3684,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -3744,9 +3773,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -3754,7 +3783,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -3763,9 +3792,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39b3bc2a8f715298032cf5087e58573809374b08160aa7d750582bdb82d2683" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -3796,7 +3825,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", ] [[package]] @@ -3805,14 +3834,14 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", ] [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -3924,9 +3953,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -3985,7 +4014,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite 0.2.13", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -3994,19 +4023,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", "hyper", "log", - "rustls 0.21.7", + "rustls 0.21.9", "rustls-native-certs", "tokio", "tokio-rustls", - "webpki-roots 0.23.1", + "webpki-roots 0.25.2", ] [[package]] @@ -4061,19 +4090,19 @@ dependencies = [ [[package]] name = "if-addrs" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "if-watch" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb892e5777fe09e16f3d44de7802f4daa7267ecbe8c466f19d94e25bb0c303e" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ "async-io", "core-foundation", @@ -4130,12 +4159,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -4226,7 +4255,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.4", + "socket2 0.5.5", "widestring", "windows-sys 0.48.0", "winreg", @@ -4234,9 +4263,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" @@ -4245,7 +4274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", - "rustix 0.38.19", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -4284,9 +4313,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -4335,7 +4364,7 @@ checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803" dependencies = [ "anyhow", "arrayvec 0.7.4", - "async-lock", + "async-lock 2.8.0", "async-trait", "beef", "futures-channel", @@ -4437,13 +4466,13 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" dependencies = [ "cfg-if", - "ecdsa 0.16.8", - "elliptic-curve 0.13.6", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", ] @@ -4536,6 +4565,7 @@ dependencies = [ "frame-system", "hash-db", "hex-literal 0.3.4", + "kilt-support", "log", "pallet-did-lookup", "pallet-dip-consumer", @@ -4848,9 +4878,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -4871,7 +4901,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.10", + "getrandom 0.2.11", "instant", "libp2p-allow-block-list", "libp2p-connection-limits", @@ -4991,7 +5021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce" dependencies = [ "bs58", - "ed25519-dalek 2.0.0", + "ed25519-dalek 2.1.0", "log", "multiaddr", "multihash", @@ -5045,7 +5075,7 @@ dependencies = [ "log", "rand 0.8.5", "smallvec", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "trust-dns-proto", "void", @@ -5187,7 +5217,7 @@ dependencies = [ "libc", "libp2p-core", "log", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", ] @@ -5287,6 +5317,17 @@ dependencies = [ "yamux", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" @@ -5402,15 +5443,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" @@ -5493,7 +5528,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5507,7 +5542,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5518,7 +5553,7 @@ checksum = "d710e1214dffbab3b5dacb21475dde7d6ed84c69ff722b3a47a782668d44fbac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5529,7 +5564,7 @@ checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5591,7 +5626,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.19", + "rustix 0.38.25", ] [[package]] @@ -5679,9 +5714,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -6455,13 +6490,17 @@ dependencies = [ name = "pallet-deposit-storage" version = "1.12.0-dev" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "kilt-support", "log", + "pallet-balances", "pallet-dip-provider", "parity-scale-codec", "scale-info", + "sp-io", + "sp-keystore", "sp-runtime", "sp-std", ] @@ -6472,7 +6511,7 @@ version = "1.12.0-dev" dependencies = [ "base58", "blake2", - "env_logger 0.10.0", + "env_logger 0.10.1", "frame-benchmarking", "frame-support", "frame-system", @@ -6497,12 +6536,16 @@ dependencies = [ name = "pallet-dip-consumer" version = "1.12.0-dev" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "kilt-support", "parity-scale-codec", "scale-info", "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", "sp-std", ] @@ -6511,10 +6554,16 @@ name = "pallet-dip-provider" version = "1.12.0-dev" dependencies = [ "did", + "frame-benchmarking", "frame-support", "frame-system", + "kilt-support", "parity-scale-codec", "scale-info", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", "sp-std", ] @@ -6538,7 +6587,7 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-std", - "strum", + "strum 0.24.1", ] [[package]] @@ -6728,7 +6777,7 @@ dependencies = [ "ctype", "delegation", "did", - "env_logger 0.10.0", + "env_logger 0.10.1", "frame-benchmarking", "frame-support", "frame-system", @@ -6991,12 +7040,16 @@ name = "pallet-relay-store" version = "1.12.0-dev" dependencies = [ "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-benchmarking", "frame-support", "frame-system", "log", "parity-scale-codec", "scale-info", "sp-core", + "sp-io", + "sp-keystore", "sp-runtime", "sp-std", ] @@ -7105,7 +7158,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -7657,9 +7710,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -7668,9 +7721,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -7678,22 +7731,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "pest_meta" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -7707,7 +7760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.2", + "indexmap 2.1.0", ] [[package]] @@ -7727,7 +7780,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -7776,9 +7829,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" -version = "3.1.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" [[package]] name = "polkadot-approval-distribution" @@ -8395,7 +8448,7 @@ dependencies = [ "rand 0.8.5", "sc-authority-discovery", "sc-network", - "strum", + "strum 0.24.1", "thiserror", "tracing-gum", ] @@ -8968,29 +9021,27 @@ dependencies = [ [[package]] name = "polling" -version = "2.8.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" dependencies = [ - "autocfg", - "bitflags 1.3.2", "cfg-if", "concurrent-queue", - "libc", - "log", "pin-project-lite 0.2.13", + "rustix 0.38.25", + "tracing", "windows-sys 0.48.0", ] [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.5.1", ] [[package]] @@ -9019,9 +9070,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.4.3" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" [[package]] name = "powerfmt" @@ -9082,7 +9133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -9148,12 +9199,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro-warning" version = "0.4.2" @@ -9162,7 +9207,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -9208,7 +9253,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -9425,7 +9470,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", ] [[package]] @@ -9526,12 +9571,12 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.11", + "libredox", "thiserror", ] @@ -9565,7 +9610,7 @@ checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -9672,12 +9717,12 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce3045ffa7c981a6ee93f640b538952e155f1ae3a1a02b84547fc7a56b7059a" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" dependencies = [ "cc", - "getrandom 0.2.10", + "getrandom 0.2.11", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -9797,13 +9842,13 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.2.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -9834,12 +9879,12 @@ dependencies = [ [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -9866,6 +9911,7 @@ dependencies = [ "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", "did", + "frame-benchmarking", "frame-support", "frame-system", "kilt-asset-dids", @@ -9937,9 +9983,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.16" +version = "0.36.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3636faa25820d8648e0e31c5d519bbb01f72fdf57131f0f5f7da5fed36eab" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" dependencies = [ "bitflags 1.3.2", "errno", @@ -9951,28 +9997,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.25" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.10", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] @@ -9997,20 +10029,20 @@ checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring 0.16.20", - "sct 0.7.0", + "sct 0.7.1", "webpki 0.22.4", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", - "ring 0.16.20", - "rustls-webpki 0.101.6", - "sct 0.7.0", + "ring 0.17.5", + "rustls-webpki", + "sct 0.7.1", ] [[package]] @@ -10027,31 +10059,21 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", ] [[package]] name = "rustls-webpki" -version = "0.100.3" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -10199,7 +10221,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -10476,7 +10498,7 @@ name = "sc-consensus-grandpa" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948fbd2fd1233dc26dbb9f9bbc1d2cca2c03945d" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "array-bytes", "async-trait", "dyn-clone", @@ -10598,7 +10620,7 @@ dependencies = [ "cfg-if", "libc", "log", - "rustix 0.36.16", + "rustix 0.36.17", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -10719,7 +10741,7 @@ name = "sc-network-gossip" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948fbd2fd1233dc26dbb9f9bbc1d2cca2c03945d" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "futures", "futures-timer", "libp2p", @@ -11122,7 +11144,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -11184,9 +11206,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "bitvec", "cfg-if", @@ -11198,9 +11220,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -11223,7 +11245,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "cfg-if", "hashbrown 0.13.2", ] @@ -11270,12 +11292,12 @@ dependencies = [ [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -11394,29 +11416,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -11425,9 +11447,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -11538,9 +11560,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -11603,9 +11625,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "snap" @@ -11615,16 +11637,16 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +checksum = "58021967fd0a5eeeb23b08df6cc244a4d4a5b4aec1d27c9e02fad1a58b4cd74e" dependencies = [ - "aes-gcm 0.9.4", + "aes-gcm 0.10.3", "blake2", "chacha20poly1305", "curve25519-dalek 4.1.1", "rand_core 0.6.4", - "ring 0.16.20", + "ring 0.17.5", "rustc_version", "sha2 0.10.8", "subtle", @@ -11632,9 +11654,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -11642,9 +11664,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -11699,7 +11721,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -11838,7 +11860,7 @@ dependencies = [ "sp-mmr-primitives", "sp-runtime", "sp-std", - "strum", + "strum 0.24.1", ] [[package]] @@ -11936,7 +11958,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "quote", "sp-core-hashing", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -11955,7 +11977,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -12016,7 +12038,7 @@ dependencies = [ "lazy_static", "sp-core", "sp-runtime", - "strum", + "strum 0.24.1", ] [[package]] @@ -12162,7 +12184,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -12304,7 +12326,7 @@ name = "sp-trie" version = "22.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948fbd2fd1233dc26dbb9f9bbc1d2cca2c03945d" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "hash-db", "hashbrown 0.13.2", "lazy_static", @@ -12347,7 +12369,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -12392,13 +12414,13 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spinners" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" +checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82" dependencies = [ "lazy_static", "maplit", - "strum", + "strum 0.24.1", ] [[package]] @@ -12502,9 +12524,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" +checksum = "35935738370302d5e33963665b77541e4b990a3e919ec904c837a56cfc891de1" dependencies = [ "Inflector", "num-format", @@ -12613,9 +12635,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "strum_macros" version = "0.24.3" @@ -12629,6 +12657,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.39", +] + [[package]] name = "stun" version = "0.4.4" @@ -12650,9 +12691,9 @@ dependencies = [ [[package]] name = "substrate-bip39" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +checksum = "e620c7098893ba667438b47169c00aacdd9e7c10e042250ce2b60b087ec97328" dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", @@ -12738,7 +12779,7 @@ dependencies = [ "filetime", "parity-wasm", "sp-maybe-compressed-blob", - "strum", + "strum 0.24.1", "tempfile", "toml 0.7.8", "walkdir", @@ -12773,9 +12814,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -12823,28 +12864,28 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall 0.3.5", - "rustix 0.38.19", + "fastrand", + "redox_syscall 0.4.1", + "rustix 0.38.25", "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] @@ -12863,27 +12904,27 @@ checksum = "f66edd6b6cd810743c0c71e1d085e92b01ce6a72782032e3f794c8284fe4bcdd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -13029,9 +13070,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.33.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -13041,20 +13082,20 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite 0.2.13", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -13074,7 +13115,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.7", + "rustls 0.21.9", "tokio", ] @@ -13092,9 +13133,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -13128,9 +13169,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -13141,7 +13182,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -13191,9 +13232,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite 0.2.13", @@ -13209,7 +13250,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -13252,17 +13293,17 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -13339,7 +13380,7 @@ dependencies = [ "lazy_static", "rand 0.8.5", "smallvec", - "socket2 0.4.9", + "socket2 0.4.10", "thiserror", "tinyvec", "tokio", @@ -13566,11 +13607,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", ] [[package]] @@ -13645,9 +13686,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -13655,24 +13696,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -13682,9 +13723,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -13692,22 +13733,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasm-instrument" @@ -13726,8 +13767,8 @@ checksum = "87fef6d0d508f08334e0ab0e6877feb4c0ecb3956bcf2cb950699b22fedf3e9c" dependencies = [ "anyhow", "libc", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "tempfile", "thiserror", "wasm-opt-cxx-sys", @@ -13827,12 +13868,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64 0.21.4", + "base64 0.21.5", "bincode", "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.16", + "rustix 0.36.17", "serde", "sha2 0.10.8", "toml 0.5.11", @@ -13928,7 +13969,7 @@ checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ "object 0.30.4", "once_cell", - "rustix 0.36.16", + "rustix 0.36.17", ] [[package]] @@ -13959,7 +14000,7 @@ dependencies = [ "memoffset 0.8.0", "paste", "rand 0.8.5", - "rustix 0.36.16", + "rustix 0.36.17", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -13980,9 +14021,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -14004,7 +14045,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.4", + "ring 0.17.5", "untrusted 0.9.0", ] @@ -14017,15 +14058,6 @@ dependencies = [ "webpki 0.22.4", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.2" @@ -14159,7 +14191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" dependencies = [ "log", - "socket2 0.4.9", + "socket2 0.4.10", "thiserror", "tokio", "webrtc-util", @@ -14356,7 +14388,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.19", + "rustix 0.38.25", ] [[package]] @@ -14559,9 +14591,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.17" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] @@ -14711,7 +14743,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -14737,11 +14769,31 @@ dependencies = [ "time", ] +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -14754,7 +14806,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index d9d6048840..bbebdfa4f2 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -17,6 +17,7 @@ log.workspace = true # Internal dependencies did.workspace = true +kilt-support = {workspace = true, optional = true} pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true @@ -57,6 +58,7 @@ std = [ "hash-db/std", "log/std", "did/std", + "kilt-support?/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", @@ -80,6 +82,8 @@ std = [ "parachain-info/std", ] runtime-benchmarks = [ + "kilt-support/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", + "pallet-dip-provider/runtime-benchmarks", "rococo-runtime/runtime-benchmarks" ] diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 7b16bcbb8e..f598614925 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -20,7 +20,6 @@ use did::{ did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; -use frame_support::ensure; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; @@ -44,6 +43,20 @@ pub struct TimeBoundDidSignature { pub block_number: BlockNumber, } +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for TimeBoundDidSignature +where + DidSignature: kilt_support::traits::GetWorstCase, + BlockNumber: Default, +{ + fn worst_case(context: Context) -> Self { + Self { + signature: DidSignature::worst_case(context), + block_number: BlockNumber::default(), + } + } +} + pub enum RevealedDidKeysSignatureAndCallVerifierError { SignatureNotFresh, SignatureUnverifiable, @@ -121,6 +134,7 @@ impl< CallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, { + #[cfg(not(feature = "runtime-benchmarks"))] #[allow(clippy::result_unit_err)] pub(crate) fn verify_did_signature_for_call( call: &Call, @@ -131,6 +145,8 @@ impl< (DidVerificationKey, DidVerificationKeyRelationship), RevealedDidKeysSignatureAndCallVerifierError, > { + use frame_support::ensure; + let block_number = ContextProvider::block_number(); let is_signature_fresh = if let Some(blocks_ago_from_now) = block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) @@ -183,4 +199,80 @@ impl< .map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?; Ok((key.clone(), *relationship)) } + + #[cfg(feature = "runtime-benchmarks")] + #[allow(clippy::result_unit_err)] + pub(crate) fn verify_did_signature_for_call( + call: &Call, + submitter: &Submitter, + local_details: &mut Option, + merkle_revealed_did_signature: RevealedDidKeysAndSignature, + ) -> Result< + (DidVerificationKey, DidVerificationKeyRelationship), + RevealedDidKeysSignatureAndCallVerifierError, + > { + use sp_core::ed25519; + + let block_number = ContextProvider::block_number(); + // Computed but ignored + if let Some(blocks_ago_from_now) = + block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) + { + // False if the signature is too old. + blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + } else { + // Signature generated at a future time, not possible to verify. + false + }; + let encoded_payload = ( + call, + &local_details, + submitter, + &merkle_revealed_did_signature.did_signature.block_number, + ContextProvider::genesis_hash(), + ContextProvider::signed_extra(), + ) + .encode(); + // Only consider verification keys from the set of revealed keys. + let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = + merkle_revealed_did_signature + .merkle_leaves + .borrow() + .iter() + .filter_map( + |RevealedDidKey { + relationship, + details: DidPublicKeyDetails { key, .. }, + .. + }| { + let DidPublicKey::PublicVerificationKey(key) = key else { + return None; + }; + if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) { + Some(Ok((key.clone(), vr))) + } else { + None + } + }, + ) + .collect::>()?; + let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { + verification_key + .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) + .is_ok() + }); + let default = ( + DidVerificationKey::Ed25519(ed25519::Public::from_raw([0u8; 32])), + DidVerificationKeyRelationship::Authentication, + ); + let (key, relationship) = valid_signing_key.unwrap_or(&default); + if let Some(details) = local_details { + details.bump(); + } else { + *local_details = Some(DidLocalDetails::default()); + }; + // Ignore the result of this call + let _ = CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)); + Ok((key.clone(), *relationship)) + } } diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index 6df44c2e49..6fd215de33 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -27,7 +27,7 @@ use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; use scale_info::TypeInfo; use sp_core::{RuntimeDebug, U256}; use sp_runtime::traits::{AtLeast32BitUnsigned, Get, Hash, MaybeDisplay, Member, SimpleBitOps}; -use sp_std::{marker::PhantomData, vec::Vec}; +use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, @@ -38,7 +38,7 @@ use crate::{ RelayChainStorageInfo, }, utils::OutputOf, - FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, + BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -209,7 +209,7 @@ impl< type Proof = VersionedChildParachainDipStateProof< ::BlockNumber, ::Hasher, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< KeyIdOf, KiltRuntime::AccountId, @@ -376,7 +376,7 @@ impl< type Proof = VersionedChildParachainDipStateProof< ::BlockNumber, ::Hasher, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< ProviderDidKeyId, ProviderAccountId, @@ -592,7 +592,7 @@ mod v0 { type Proof = ChildParachainDipStateProof< ::BlockNumber, ::Hasher, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< ProviderDidKeyId, ProviderAccountId, @@ -629,7 +629,6 @@ mod v0 { // 1.2 If so, extract the state root from the header let state_root_at_height = proof.relay_header.state_root; - // FIXME: Compilation error // 2. Verify relay chain proof let provider_parachain_header = ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-support/src/export/common.rs index d0eff66d68..fa98de6f16 100644 --- a/crates/kilt-dip-support/src/export/common.rs +++ b/crates/kilt-dip-support/src/export/common.rs @@ -24,14 +24,27 @@ pub mod v0 { use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; - use sp_std::vec::Vec; - use crate::{did::TimeBoundDidSignature, merkle::DidMerkleProof}; + use crate::{did::TimeBoundDidSignature, merkle::DidMerkleProof, BoundedBlindedValue}; #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] pub struct ParachainRootStateProof { pub(crate) relay_block_height: RelayBlockHeight, - pub(crate) proof: Vec>, + pub(crate) proof: BoundedBlindedValue, + } + + #[cfg(feature = "runtime-benchmarks")] + impl kilt_support::traits::GetWorstCase + for ParachainRootStateProof + where + RelayBlockHeight: Default, + { + fn worst_case(context: Context) -> Self { + Self { + relay_block_height: RelayBlockHeight::default(), + proof: BoundedBlindedValue::worst_case(context), + } + } } #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -39,4 +52,21 @@ pub mod v0 { pub(crate) leaves: DidMerkleProof, pub(crate) signature: TimeBoundDidSignature, } + + #[cfg(feature = "runtime-benchmarks")] + impl kilt_support::traits::GetWorstCase + for DipMerkleProofAndDidSignature + where + BlindedValues: kilt_support::traits::GetWorstCase, + Leaf: Default + Clone, + BlockNumber: Default, + Context: Clone, + { + fn worst_case(context: Context) -> Self { + Self { + leaves: DidMerkleProof::worst_case(context.clone()), + signature: TimeBoundDidSignature::worst_case(context), + } + } + } } diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index 5f82560104..0fd6ec989e 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -27,7 +27,7 @@ use parity_scale_codec::{Decode, Encode, HasCompact}; use scale_info::TypeInfo; use sp_core::{RuntimeDebug, U256}; use sp_runtime::traits::Get; -use sp_std::{marker::PhantomData, vec::Vec}; +use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, @@ -35,7 +35,7 @@ use crate::{ state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, traits::{self, Bump, DidSignatureVerifierContext, DipCallOriginFilter}, utils::OutputOf, - FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, + BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -56,6 +56,26 @@ pub enum VersionedSiblingParachainDipStateProof< ), } +#[cfg(feature = "runtime-benchmarks")] +impl + kilt_support::traits::GetWorstCase + for VersionedSiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, + > where + RelayBlockHeight: Default, + DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, + DipMerkleProofRevealedLeaf: Default + Clone, + LocalBlockNumber: Default, + Context: Clone, +{ + fn worst_case(context: Context) -> Self { + Self::V0(v0::SiblingParachainDipStateProof::worst_case(context)) + } +} + pub enum DipSiblingProviderStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, @@ -183,7 +203,7 @@ impl< >; type Proof = VersionedSiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< KeyIdOf, KiltRuntime::AccountId, @@ -336,7 +356,7 @@ impl< >; type Proof = VersionedSiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< ProviderDidKeyId, ProviderAccountId, @@ -413,9 +433,34 @@ mod v0 { DipMerkleProofRevealedLeaf, LocalBlockNumber, > { - para_state_root: ParachainRootStateProof, - dip_identity_commitment: Vec>, - did: DipMerkleProofAndDidSignature, + pub(crate) para_state_root: ParachainRootStateProof, + pub(crate) dip_identity_commitment: BoundedBlindedValue, + pub(crate) did: + DipMerkleProofAndDidSignature, + } + + #[cfg(feature = "runtime-benchmarks")] + impl + kilt_support::traits::GetWorstCase + for SiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, + > where + DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, + DipMerkleProofRevealedLeaf: Default + Clone, + RelayBlockHeight: Default, + LocalBlockNumber: Default, + Context: Clone, + { + fn worst_case(context: Context) -> Self { + Self { + para_state_root: ParachainRootStateProof::worst_case(context.clone()), + dip_identity_commitment: BoundedBlindedValue::worst_case(context.clone()), + did: DipMerkleProofAndDidSignature::worst_case(context), + } + } } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] @@ -518,7 +563,7 @@ mod v0 { >; type Proof = SiblingParachainDipStateProof< RelayChainStateInfo::BlockNumber, - Vec>, + BoundedBlindedValue, RevealedDidMerkleProofLeaf< ProviderDidKeyId, ProviderAccountId, diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 0322801680..9d7a415616 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -34,3 +34,4 @@ pub use state_proofs::{ relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, }; pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; +pub use utils::BoundedBlindedValue; diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 2f23c952e5..68e30e9917 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -16,10 +16,14 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use did::{ + did_details::{DidPublicKeyDetails, DidVerificationKey}, + DidVerificationKeyRelationship, +}; use frame_support::{traits::ConstU32, DefaultNoBound, RuntimeDebug}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +use sp_core::ed25519; use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; @@ -31,6 +35,20 @@ pub struct DidMerkleProof { pub revealed: Vec, } +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for DidMerkleProof +where + BlindedValues: kilt_support::traits::GetWorstCase, + Leaf: Default + Clone, +{ + fn worst_case(context: Context) -> Self { + Self { + blinded: BlindedValues::worst_case(context), + revealed: sp_std::vec![Leaf::default(); 64], + } + } +} + #[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] pub enum DidKeyRelationship { Encryption, @@ -119,6 +137,25 @@ pub enum RevealedDidMerkleProofLeaf, LinkedAccountMerkleValue), } +#[cfg(feature = "runtime-benchmarks")] +impl Default + for RevealedDidMerkleProofLeaf +where + KeyId: Default, + BlockNumber: Default, +{ + fn default() -> Self { + Self::DidKey( + (KeyId::default(), DidVerificationKeyRelationship::Authentication.into()).into(), + DidPublicKeyDetails { + key: DidVerificationKey::Ed25519(ed25519::Public::from_raw([0u8; 32])).into(), + block_number: BlockNumber::default(), + } + .into(), + ) + } +} + impl RevealedDidMerkleProofLeaf where @@ -259,7 +296,7 @@ impl< LinkedAccountId: Encode + Clone, Web3Name: Encode + Clone, { - #[allow(clippy::result_unit_err)] + #[cfg(not(feature = "runtime-benchmarks"))] #[allow(clippy::type_complexity)] pub(crate) fn verify_dip_merkle_proof( identity_commitment: &Hasher::Out, @@ -338,4 +375,76 @@ impl< linked_accounts, }) } + + #[cfg(feature = "runtime-benchmarks")] + #[allow(clippy::type_complexity)] + pub(crate) fn verify_dip_merkle_proof( + identity_commitment: &Hasher::Out, + proof: DidMerkleProof< + crate::BoundedBlindedValue, + RevealedDidMerkleProofLeaf, + >, + ) -> Result< + RevealedDidMerkleProofLeaves< + KeyId, + AccountId, + BlockNumber, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >, + DidMerkleProofVerifierError, + > { + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + // Ignore result + let _ = verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves); + + #[allow(clippy::type_complexity)] + let (did_keys, web3_name, linked_accounts): ( + BoundedVec, ConstU32>, + Option>, + BoundedVec>, + ) = proof.revealed.iter().try_fold( + ( + BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), + None, + BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), + ), + |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { + RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { + // Ignore error, just discard anything in excess. + let _ = keys.try_push(RevealedDidKey { + id: key_id.0.clone(), + relationship: key_id.1, + details: key_value.0.clone(), + }); + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + } + RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( + keys, + Some(RevealedWeb3Name { + web3_name: revealed_web3_name.0.clone(), + claimed_at: details.0.clone(), + }), + linked_accounts, + )), + RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { + // Ignore error, just discard anything in excess. + let _ = linked_accounts.try_push(account_id.0.clone()); + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + } + }, + )?; + + Ok(RevealedDidMerkleProofLeaves { + did_keys, + web3_name, + linked_accounts, + }) + } } diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 0156143ce9..019161c27f 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -121,6 +121,7 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { + #[cfg(not(feature = "runtime-benchmarks"))] pub fn verify_proof_for_parachain_with_root( para_id: &RelayChainState::ParaId, root: &OutputOf<::Hasher>, @@ -143,6 +144,38 @@ pub(super) mod relay_chain { let mut unwrapped_head = &encoded_head[2..]; Header::decode(&mut unwrapped_head).map_err(|_| ParachainHeadProofVerifierError::HeaderDecode) } + + // Ignores any errors returned by the `read_proof_check` function and returns a + // default Header in case of failure. + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_parachain_with_root( + para_id: &RelayChainState::ParaId, + root: &OutputOf<::Hasher>, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> { + let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) + .unwrap_or_default(); + let encoded_head = if let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) { + encoded_head.clone() + } else { + sp_std::vec![0u8; 3] + }; + let mut unwrapped_head = &encoded_head[2..]; + let header = Header::decode(&mut unwrapped_head).unwrap_or(Header { + number: U256::from(0u64) + .try_into() + .map_err(|_| "Block number should be created from a u64") + .unwrap(), + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }); + Ok(header) + } } // Relies on the `RelayChainState::state_root_for_block` to retrieve the state @@ -154,7 +187,7 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { - #[allow(clippy::result_unit_err)] + #[cfg(not(feature = "runtime-benchmarks"))] pub fn verify_proof_for_parachain( para_id: &RelayChainState::ParaId, relay_height: &RelayChainState::BlockNumber, @@ -164,6 +197,16 @@ pub(super) mod relay_chain { .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) } + + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_parachain( + para_id: &RelayChainState::ParaId, + relay_height: &RelayChainState::BlockNumber, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> { + let relay_state_root = RelayChainState::state_root_for_block(relay_height).unwrap_or_default(); + Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) + } } pub struct RelayStateRootsViaRelayStorePallet(PhantomData); @@ -296,6 +339,7 @@ pub(super) mod parachain { ParaInfo::Commitment: Decode, ParaInfo::Key: AsRef<[u8]>, { + #[cfg(not(feature = "runtime-benchmarks"))] #[allow(clippy::result_unit_err)] pub fn verify_proof_for_identifier( identifier: &ParaInfo::Identifier, @@ -321,6 +365,33 @@ pub(super) mod parachain { ParaInfo::Commitment::decode(&mut &encoded_commitment[..]) .map_err(|_| DipIdentityCommitmentProofVerifierError::CommitmentDecode) } + + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_identifier( + identifier: &ParaInfo::Identifier, + state_root: OutputOf, + proof: impl IntoIterator>, + ) -> Result + where + ParaInfo::Commitment: Default, + { + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = read_proof_check::( + state_root, + storage_proof, + [&dip_commitment_storage_key].iter(), + ) + .unwrap_or_default(); + let encoded_commitment = + if let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) { + encoded_commitment.clone() + } else { + Vec::default() + }; + let commitment = ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).unwrap_or_default(); + Ok(commitment) + } } #[cfg(test)] diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs index e382093b93..825826ce74 100644 --- a/crates/kilt-dip-support/src/utils.rs +++ b/crates/kilt-dip-support/src/utils.rs @@ -16,4 +16,64 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_std::vec::Vec; + pub type OutputOf = ::Output; + +#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] +pub struct BoundedBlindedValue(Vec>); + +impl BoundedBlindedValue { + pub fn into_inner(self) -> Vec> { + self.0 + } +} + +impl From for BoundedBlindedValue +where + C: Iterator>, +{ + fn from(value: C) -> Self { + Self(value.into_iter().collect()) + } +} + +impl sp_std::ops::Deref for BoundedBlindedValue { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for BoundedBlindedValue { + type IntoIter = > as IntoIterator>::IntoIter; + type Item = > as IntoIterator>::Item; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for BoundedBlindedValue +where + T: Default + Clone, +{ + fn worst_case(_context: Context) -> Self { + Self(sp_std::vec![sp_std::vec![T::default(); 128]; 64]) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for BoundedBlindedValue +where + T: Default + Clone, +{ + fn default() -> Self { + Self(sp_std::vec![sp_std::vec![T::default(); 128]; 64]) + } +} diff --git a/dip-template/nodes/dip-consumer/Cargo.toml b/dip-template/nodes/dip-consumer/Cargo.toml index b48e55d0db..1f5435f436 100644 --- a/dip-template/nodes/dip-consumer/Cargo.toml +++ b/dip-template/nodes/dip-consumer/Cargo.toml @@ -68,3 +68,9 @@ cumulus-relay-chain-interface.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true + +[features] +runtime-benchmarks = [ + "frame-benchmarking-cli/runtime-benchmarks", + "dip-consumer-runtime-template/runtime-benchmarks", +] diff --git a/dip-template/nodes/dip-consumer/src/cli.rs b/dip-template/nodes/dip-consumer/src/cli.rs index 5e5d10525a..da4f536121 100644 --- a/dip-template/nodes/dip-consumer/src/cli.rs +++ b/dip-template/nodes/dip-consumer/src/cli.rs @@ -25,6 +25,7 @@ use sc_service::Configuration; use crate::chain_spec::Extensions; +#[allow(clippy::large_enum_variant)] #[derive(Debug, clap::Subcommand)] pub enum Subcommand { BuildSpec(BuildSpecCmd), @@ -44,6 +45,8 @@ pub enum Subcommand { ExportGenesisState(ExportGenesisStateCommand), ExportGenesisWasm(ExportGenesisWasmCommand), + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), } #[derive(Debug, clap::Parser)] diff --git a/dip-template/nodes/dip-consumer/src/command.rs b/dip-template/nodes/dip-consumer/src/command.rs index e560839502..36898a0190 100644 --- a/dip-template/nodes/dip-consumer/src/command.rs +++ b/dip-template/nodes/dip-consumer/src/command.rs @@ -19,6 +19,8 @@ use std::{fs::create_dir_all, net::SocketAddr}; use cumulus_primitives_core::ParaId; +use dip_consumer_runtime_template::Block; +use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::{info, warn}; use sc_cli::{ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, LoggerBuilder, @@ -189,6 +191,45 @@ pub fn run() -> Result<()> { cmd.run(&*spec) }) } + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => { + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + .into()) + } + } + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let partials = new_partial(&config)?; + cmd.run(partials.client) + }), + #[cfg(not(feature = "runtime-benchmarks"))] + BenchmarkCmd::Storage(_) => { + return Err(sc_cli::Error::Input( + "Compile with --features=runtime-benchmarks \ + to enable storage benchmarks." + .into(), + ) + .into()) + } + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let partials = new_partial(&config)?; + let db = partials.backend.expose_db(); + let storage = partials.backend.expose_storage(); + cmd.run(config, partials.client.clone(), db, storage) + }), + BenchmarkCmd::Machine(cmd) => { + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())) + } + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported".into()), + } + } None => { let runner = cli.create_runner(&cli.run.normalize())?; let collator_options = cli.run.collator_options(); diff --git a/dip-template/nodes/dip-provider/Cargo.toml b/dip-template/nodes/dip-provider/Cargo.toml index 0bc866dddf..584bca26ee 100644 --- a/dip-template/nodes/dip-provider/Cargo.toml +++ b/dip-template/nodes/dip-provider/Cargo.toml @@ -68,3 +68,9 @@ cumulus-relay-chain-interface.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true + +[features] +runtime-benchmarks = [ + "frame-benchmarking-cli/runtime-benchmarks", + "dip-provider-runtime-template/runtime-benchmarks" +] diff --git a/dip-template/nodes/dip-provider/src/cli.rs b/dip-template/nodes/dip-provider/src/cli.rs index 5e5d10525a..da4f536121 100644 --- a/dip-template/nodes/dip-provider/src/cli.rs +++ b/dip-template/nodes/dip-provider/src/cli.rs @@ -25,6 +25,7 @@ use sc_service::Configuration; use crate::chain_spec::Extensions; +#[allow(clippy::large_enum_variant)] #[derive(Debug, clap::Subcommand)] pub enum Subcommand { BuildSpec(BuildSpecCmd), @@ -44,6 +45,8 @@ pub enum Subcommand { ExportGenesisState(ExportGenesisStateCommand), ExportGenesisWasm(ExportGenesisWasmCommand), + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), } #[derive(Debug, clap::Parser)] diff --git a/dip-template/nodes/dip-provider/src/command.rs b/dip-template/nodes/dip-provider/src/command.rs index 6e508956e0..976c311898 100644 --- a/dip-template/nodes/dip-provider/src/command.rs +++ b/dip-template/nodes/dip-provider/src/command.rs @@ -19,6 +19,8 @@ use std::{fs::create_dir_all, net::SocketAddr}; use cumulus_primitives_core::ParaId; +use dip_provider_runtime_template::Block; +use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::{info, warn}; use sc_cli::{ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, LoggerBuilder, @@ -189,6 +191,45 @@ pub fn run() -> Result<()> { cmd.run(&*spec) }) } + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => { + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + .into()) + } + } + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let partials = new_partial(&config)?; + cmd.run(partials.client) + }), + #[cfg(not(feature = "runtime-benchmarks"))] + BenchmarkCmd::Storage(_) => { + return Err(sc_cli::Error::Input( + "Compile with --features=runtime-benchmarks \ + to enable storage benchmarks." + .into(), + ) + .into()) + } + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let partials = new_partial(&config)?; + let db = partials.backend.expose_db(); + let storage = partials.backend.expose_storage(); + cmd.run(config, partials.client.clone(), db, storage) + }), + BenchmarkCmd::Machine(cmd) => { + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())) + } + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported".into()), + } + } None => { let runner = cli.create_runner(&cli.run.normalize())?; let collator_options = cli.run.collator_options(); diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index 078f172253..ca55d7dcad 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -53,6 +53,7 @@ sp-session.workspace = true sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true +sp-weights.workspace = true # Cumulus cumulus-pallet-aura-ext.workspace = true @@ -64,6 +65,11 @@ cumulus-primitives-utility.workspace = true pallet-collator-selection.workspace = true parachain-info.workspace = true +# Benchmarks +frame-benchmarking = {workspace = true, optional = true} +frame-system-benchmarking = {workspace = true, optional = true} +hex-literal = {workspace = true, optional = true} + [features] default = [ "std", @@ -104,6 +110,7 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", + "sp-weights/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", @@ -118,8 +125,17 @@ runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", "kilt-dip-support/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", + "pallet-relay-store/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "hex-literal" ] diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index db25e19333..03ef8efd18 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -27,7 +27,7 @@ use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_core::ConstU32; use sp_runtime::traits::BlakeTwo256; -use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; +use crate::{weights, AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; pub type MerkleProofVerifierOutput = >::VerificationResult; pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< @@ -49,11 +49,13 @@ impl pallet_dip_consumer::Config for Runtime { type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; + type WeightInfo = weights::pallet_dip_consumer::WeightInfo; } pub struct PreliminaryDipOriginFilter; impl Contains for PreliminaryDipOriginFilter { + #[cfg(not(feature = "runtime-benchmarks"))] fn contains(t: &RuntimeCall) -> bool { matches!( t, @@ -63,6 +65,11 @@ impl Contains for PreliminaryDipOriginFilter { | RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) ) } + + #[cfg(feature = "runtime-benchmarks")] + fn contains(_t: &RuntimeCall) -> bool { + true + } } fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { @@ -123,4 +130,5 @@ impl DipCallOriginFilter for DipCallFilter { impl pallet_relay_store::Config for Runtime { type MaxRelayBlocksStored = ConstU32<100>; + type WeightInfo = weights::pallet_relay_store::WeightInfo; } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index 953b179b9b..daf21c15fa 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -65,6 +65,7 @@ use sp_version::RuntimeVersion; mod dip; mod origin_adapter; +mod weights; pub use crate::{dip::*, origin_adapter::*}; #[cfg(any(feature = "std", test))] @@ -234,7 +235,7 @@ impl frame_system::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type SS58Prefix = ConstU16; - type SystemWeightInfo = (); + type SystemWeightInfo = weights::frame_system::WeightInfo; type Version = Version; } @@ -358,6 +359,15 @@ impl pallet_postit::Config for Runtime { type Username = Web3Name; } +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, SystemBench::] + [pallet_dip_consumer, DipConsumer] + [pallet_relay_store, RelayStore] + ); +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> SlotDuration { @@ -501,4 +511,50 @@ impl_runtime_apis! { ParachainSystem::collect_collation_info(header) } } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } } diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index d973367b33..ebde676e7a 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -35,8 +35,7 @@ impl EnsureOrigin for EnsureDipOriginAdapter { #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - // TODO: Replace with actual DIP origin upon benchmarking - Ok(RuntimeOrigin::root()) + EnsureDipOrigin::::try_successful_origin() } } diff --git a/dip-template/runtimes/dip-consumer/src/weights/frame_system.rs b/dip-template/runtimes/dip-consumer/src/weights/frame_system.rs new file mode 100644 index 0000000000..3c04d0f214 --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/weights/frame_system.rs @@ -0,0 +1,193 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `frame_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-consumer-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=frame-system +// --extrinsic=* +// --output=./dip-template/runtimes/dip-consumer/src/weights/frame_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `frame_system`. +pub struct WeightInfo(PhantomData); +impl frame_system::WeightInfo for WeightInfo { + /// The range of component `b` is `[0, 3932160]`. + fn remark(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_378_000 picoseconds. + Weight::from_parts(90_413_067, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(196, 0).saturating_mul(b.into())) + } + /// The range of component `b` is `[0, 3932160]`. + fn remark_with_event(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_818_000 picoseconds. + Weight::from_parts(4_882_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 15 + .saturating_add(Weight::from_parts(1_874, 0).saturating_mul(b.into())) + } + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + fn set_heap_pages() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 5_882_000 picoseconds. + Weight::from_parts(6_631_000, 0) + .saturating_add(Weight::from_parts(0, 1485)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `119` + // Estimated: `1604` + // Minimum execution time: 141_893_625_000 picoseconds. + Weight::from_parts(162_618_687_000, 0) + .saturating_add(Weight::from_parts(0, 1604)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn set_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_512_000 picoseconds. + Weight::from_parts(2_972_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 7_153 + .saturating_add(Weight::from_parts(1_100_784, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn kill_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_526_000 picoseconds. + Weight::from_parts(67_203_020, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 8_216 + .saturating_add(Weight::from_parts(391_130, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `p` is `[0, 1000]`. + fn kill_prefix(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `73 + p * (69 ±0)` + // Estimated: `66 + p * (70 ±0)` + // Minimum execution time: 2_821_000 picoseconds. + Weight::from_parts(9_760_367, 0) + .saturating_add(Weight::from_parts(0, 66)) + // Standard Error: 14_170 + .saturating_add(Weight::from_parts(886_262, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_set_heap_pages() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 1485 + ); + } + #[test] + fn test_set_code() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 1604 + ); + } + #[test] + fn test_kill_prefix() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 66 + ); + } +} diff --git a/dip-template/runtimes/dip-consumer/src/weights/mod.rs b/dip-template/runtimes/dip-consumer/src/weights/mod.rs new file mode 100644 index 0000000000..a0fbf6b5fa --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/weights/mod.rs @@ -0,0 +1,21 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +pub mod frame_system; +pub mod pallet_dip_consumer; +pub mod pallet_relay_store; diff --git a/dip-template/runtimes/dip-consumer/src/weights/pallet_dip_consumer.rs b/dip-template/runtimes/dip-consumer/src/weights/pallet_dip_consumer.rs new file mode 100644 index 0000000000..625c63b1c0 --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/weights/pallet_dip_consumer.rs @@ -0,0 +1,83 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_dip_consumer` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-consumer-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-dip-consumer +// --extrinsic=* +// --output=./dip-template/runtimes/dip-consumer/src/weights/pallet_dip_consumer.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_dip_consumer`. +pub struct WeightInfo(PhantomData); +impl pallet_dip_consumer::WeightInfo for WeightInfo { + /// Storage: `DipConsumer::IdentityEntries` (r:1 w:1) + /// Proof: `DipConsumer::IdentityEntries` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `RelayStore::LatestRelayHeads` (r:1 w:0) + /// Proof: `RelayStore::LatestRelayHeads` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `66` + // Estimated: `3521` + // Minimum execution time: 88_110_000 picoseconds. + Weight::from_parts(89_742_000, 0) + .saturating_add(Weight::from_parts(0, 3521)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_dispatch_as() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 3521 + ); + } +} diff --git a/dip-template/runtimes/dip-consumer/src/weights/pallet_relay_store.rs b/dip-template/runtimes/dip-consumer/src/weights/pallet_relay_store.rs new file mode 100644 index 0000000000..ae83009997 --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/weights/pallet_relay_store.rs @@ -0,0 +1,83 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_relay_store` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-consumer-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-relay-store +// --extrinsic=* +// --output=./dip-template/runtimes/dip-consumer/src/weights/pallet_relay_store.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_relay_store`. +pub struct WeightInfo(PhantomData); +impl pallet_relay_store::WeightInfo for WeightInfo { + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `RelayStore::LatestBlockHeights` (r:1 w:1) + /// Proof: `RelayStore::LatestBlockHeights` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `RelayStore::LatestRelayHeads` (r:0 w:2) + /// Proof: `RelayStore::LatestRelayHeads` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `540` + // Estimated: `2025` + // Minimum execution time: 9_067_000 picoseconds. + Weight::from_parts(9_467_000, 0) + .saturating_add(Weight::from_parts(0, 2025)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_on_finalize() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 2025 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 58db107592..d1f7c7bcde 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -55,6 +55,7 @@ sp-session.workspace = true sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true +sp-weights.workspace = true # Cumulus cumulus-pallet-aura-ext.workspace = true @@ -66,6 +67,11 @@ cumulus-primitives-utility.workspace = true pallet-collator-selection.workspace = true parachain-info.workspace = true +# Benchmarks +frame-benchmarking = {workspace = true, optional = true} +frame-system-benchmarking = {workspace = true, optional = true} +hex-literal = {workspace = true, optional = true} + [features] default = [ "std", @@ -108,6 +114,7 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", + "sp-weights/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", @@ -115,16 +122,26 @@ std = [ "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", "pallet-collator-selection/std", - "parachain-info/std" + "parachain-info/std", + "frame-benchmarking?/std", ] runtime-benchmarks = [ "did/runtime-benchmarks", "kilt-dip-support/runtime-benchmarks", + "pallet-deposit-storage/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", "pallet-web3-names/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", - "pallet-utility/runtime-benchmarks" + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "hex-literal" ] diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 91c52a8c1b..66f8ee88ec 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -31,9 +31,11 @@ use sp_std::vec::Vec; use crate::{ deposit::{DepositHooks, DepositNamespaces}, - AccountId, Balances, DidIdentifier, Runtime, RuntimeEvent, RuntimeHoldReason, + weights, AccountId, Balances, DidIdentifier, Runtime, RuntimeEvent, RuntimeHoldReason, }; +const MAX_LINKED_ACCOUNTS: u32 = 20; + pub mod runtime_api { use super::*; @@ -60,7 +62,8 @@ pub mod deposit { use frame_support::traits::Get; use pallet_deposit_storage::{ - traits::DepositStorageHooks, DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, + traits::{BenchmarkHooks, DepositStorageHooks}, + DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, }; use parity_scale_codec::MaxEncodedLen; use sp_core::{ConstU128, RuntimeDebug}; @@ -123,9 +126,52 @@ pub mod deposit { Ok(()) } } + + #[cfg(feature = "runtime-benchmarks")] + pub struct PalletDepositStorageBenchmarkHooks; + + #[cfg(feature = "runtime-benchmarks")] + impl BenchmarkHooks for PalletDepositStorageBenchmarkHooks { + fn pre_reclaim_deposit() -> ( + ::AccountId, + ::Namespace, + sp_runtime::BoundedVec::MaxKeyLength>, + ) { + let submitter = AccountId::from([100u8; 32]); + let namespace = DepositNamespaces::DipProvider; + let did_identifier = DidIdentifier::from([200u8; 32]); + let commitment_version = 0u16; + let key: DepositKeyOf = (did_identifier.clone(), 0) + .encode() + .try_into() + .expect("Should not fail to create a key for a DIP commitment."); + + pallet_dip_provider::IdentityCommitments::::insert( + &did_identifier, + commitment_version, + ::Hash::default(), + ); + + assert!( + pallet_dip_provider::IdentityCommitments::::get(did_identifier, commitment_version).is_some() + ); + + (submitter, namespace, key) + } + + fn post_reclaim_deposit() { + let did_identifier = DidIdentifier::from([200u8; 32]); + let commitment_version = 0u16; + assert!( + pallet_dip_provider::IdentityCommitments::::get(did_identifier, commitment_version).is_none() + ); + } + } } impl pallet_deposit_storage::Config for Runtime { + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHooks = deposit::PalletDepositStorageBenchmarkHooks; type CheckOrigin = EnsureSigned; type Currency = Balances; type DepositHooks = DepositHooks; @@ -133,6 +179,7 @@ impl pallet_deposit_storage::Config for Runtime { type Namespace = DepositNamespaces; type RuntimeEvent = RuntimeEvent; type RuntimeHoldReason = RuntimeHoldReason; + type WeightInfo = weights::pallet_deposit_storage::WeightInfo; } impl pallet_dip_provider::Config for Runtime { @@ -140,7 +187,8 @@ impl pallet_dip_provider::Config for Runtime { type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; type IdentityCommitmentGenerator = DidMerkleRootGenerator; - type IdentityProvider = LinkedDidInfoProvider; + type IdentityProvider = LinkedDidInfoProvider; type ProviderHooks = deposit::DepositCollectorHooks; type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_dip_provider::WeightInfo; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index a0f474b817..90e96c9a94 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -76,6 +76,7 @@ pub use sp_runtime::BuildStorage; use sp_version::NativeVersion; mod dip; +mod weights; pub use crate::dip::*; pub type AccountId = AccountId32; @@ -240,7 +241,7 @@ impl frame_system::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type SS58Prefix = ConstU16; - type SystemWeightInfo = (); + type SystemWeightInfo = weights::frame_system::WeightInfo; type Version = Version; } @@ -401,7 +402,7 @@ impl did::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeOrigin = RuntimeOrigin; type ServiceEndpointDeposit = ConstU128; - type WeightInfo = (); + type WeightInfo = weights::did::WeightInfo; } impl pallet_did_lookup::Config for Runtime { @@ -413,7 +414,7 @@ impl pallet_did_lookup::Config for Runtime { type OriginSuccess = DidRawOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeHoldReason = RuntimeHoldReason; - type WeightInfo = (); + type WeightInfo = weights::pallet_did_lookup::WeightInfo; } pub type Web3Name = AsciiWeb3Name; @@ -431,7 +432,19 @@ impl pallet_web3_names::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type Web3Name = Web3Name; type Web3NameOwner = DidIdentifier; - type WeightInfo = (); + type WeightInfo = weights::pallet_web3_names::WeightInfo; +} + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, SystemBench::] + [did, Did] + [pallet_did_lookup, DidLookup] + [pallet_web3_names, Web3Names] + [pallet_deposit_storage, DepositStorage] + [pallet_dip_provider, DipProvider] + ); } impl_runtime_apis! { @@ -678,4 +691,50 @@ impl_runtime_apis! { DidMerkleRootGenerator::::generate_proof(&identity_details, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(runtime_api::DipProofError::MerkleProof) } } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } } diff --git a/dip-template/runtimes/dip-provider/src/weights/did.rs b/dip-template/runtimes/dip-provider/src/weights/did.rs new file mode 100644 index 0000000000..7557791b93 --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/did.rs @@ -0,0 +1,1217 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `did` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=did +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/did.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `did`. +pub struct WeightInfo(PhantomData); +impl did::WeightInfo for WeightInfo { + /// Storage: Did DidBlacklist (r:1 w:0) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:0 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:0 w:25) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// The range of component `n` is `[1, 10]`. + /// The range of component `c` is `[1, 25]`. + fn create_ed25519_keys(n: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1131` + // Estimated: `6204` + // Minimum execution time: 179_404_000 picoseconds. + Weight::from_parts(170_230_985, 0) + .saturating_add(Weight::from_parts(0, 6204)) + // Standard Error: 40_649 + .saturating_add(Weight::from_parts(296_994, 0).saturating_mul(n.into())) + // Standard Error: 15_716 + .saturating_add(Weight::from_parts(10_509_026, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + } + /// Storage: Did DidBlacklist (r:1 w:0) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:0 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:0 w:25) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// The range of component `n` is `[1, 10]`. + /// The range of component `c` is `[1, 25]`. + fn create_sr25519_keys(n: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1131` + // Estimated: `6204` + // Minimum execution time: 181_049_000 picoseconds. + Weight::from_parts(161_298_980, 0) + .saturating_add(Weight::from_parts(0, 6204)) + // Standard Error: 36_193 + .saturating_add(Weight::from_parts(1_411_802, 0).saturating_mul(n.into())) + // Standard Error: 13_993 + .saturating_add(Weight::from_parts(11_432_937, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + } + /// Storage: Did DidBlacklist (r:1 w:0) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:0 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:0 w:25) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// The range of component `n` is `[1, 10]`. + /// The range of component `c` is `[1, 25]`. + fn create_ecdsa_keys(n: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1131` + // Estimated: `6204` + // Minimum execution time: 164_997_000 picoseconds. + Weight::from_parts(143_724_779, 0) + .saturating_add(Weight::from_parts(0, 6204)) + // Standard Error: 46_012 + .saturating_add(Weight::from_parts(1_689_629, 0).saturating_mul(n.into())) + // Standard Error: 17_790 + .saturating_add(Weight::from_parts(9_993_575, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + } + /// Storage: Did DidEndpointsCount (r:1 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:25 w:25) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Did DidBlacklist (r:0 w:1) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// The range of component `c` is `[1, 25]`. + fn delete(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `969 + c * (105 ±0)` + // Estimated: `5777 + c * (3090 ±0)` + // Minimum execution time: 53_190_000 picoseconds. + Weight::from_parts(54_199_033, 0) + .saturating_add(Weight::from_parts(0, 5777)) + // Standard Error: 4_988 + .saturating_add(Weight::from_parts(1_376_408, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 3090).saturating_mul(c.into())) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:25 w:25) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Did DidBlacklist (r:0 w:1) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// The range of component `c` is `[1, 25]`. + fn reclaim_deposit(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `969 + c * (105 ±0)` + // Estimated: `5777 + c * (3090 ±0)` + // Minimum execution time: 55_729_000 picoseconds. + Weight::from_parts(56_557_707, 0) + .saturating_add(Weight::from_parts(0, 5777)) + // Standard Error: 4_643 + .saturating_add(Weight::from_parts(1_397_138, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 3090).saturating_mul(c.into())) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + fn submit_did_call_ed25519_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `5777` + // Minimum execution time: 76_132_000 picoseconds. + Weight::from_parts(76_814_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + fn submit_did_call_sr25519_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `5777` + // Minimum execution time: 77_295_000 picoseconds. + Weight::from_parts(78_438_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + fn submit_did_call_ecdsa_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `5777` + // Minimum execution time: 64_288_000 picoseconds. + Weight::from_parts(65_482_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ed25519_authentication_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2170` + // Estimated: `5777` + // Minimum execution time: 67_708_000 picoseconds. + Weight::from_parts(70_147_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_sr25519_authentication_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2170` + // Estimated: `5777` + // Minimum execution time: 68_784_000 picoseconds. + Weight::from_parts(70_860_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ecdsa_authentication_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2107` + // Estimated: `5777` + // Minimum execution time: 68_261_000 picoseconds. + Weight::from_parts(69_565_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ed25519_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2038` + // Estimated: `5777` + // Minimum execution time: 67_556_000 picoseconds. + Weight::from_parts(69_662_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_sr25519_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2165` + // Estimated: `5777` + // Minimum execution time: 67_837_000 picoseconds. + Weight::from_parts(69_180_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ecdsa_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2173` + // Estimated: `5777` + // Minimum execution time: 68_540_000 picoseconds. + Weight::from_parts(69_956_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ed25519_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2104` + // Estimated: `5777` + // Minimum execution time: 65_127_000 picoseconds. + Weight::from_parts(66_929_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_sr25519_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2104` + // Estimated: `5777` + // Minimum execution time: 64_797_000 picoseconds. + Weight::from_parts(65_517_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ecdsa_delegation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2173` + // Estimated: `5777` + // Minimum execution time: 65_807_000 picoseconds. + Weight::from_parts(67_122_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ed25519_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2105` + // Estimated: `5777` + // Minimum execution time: 68_688_000 picoseconds. + Weight::from_parts(69_835_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_sr25519_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2137` + // Estimated: `5777` + // Minimum execution time: 67_241_000 picoseconds. + Weight::from_parts(68_767_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn set_ecdsa_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2177` + // Estimated: `5777` + // Minimum execution time: 66_623_000 picoseconds. + Weight::from_parts(68_381_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ed25519_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2137` + // Estimated: `5777` + // Minimum execution time: 64_750_000 picoseconds. + Weight::from_parts(67_215_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_sr25519_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2132` + // Estimated: `5777` + // Minimum execution time: 65_858_000 picoseconds. + Weight::from_parts(67_393_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ecdsa_attestation_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2112` + // Estimated: `5777` + // Minimum execution time: 64_552_000 picoseconds. + Weight::from_parts(66_076_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn add_ed25519_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2026` + // Estimated: `5777` + // Minimum execution time: 67_691_000 picoseconds. + Weight::from_parts(69_179_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn add_sr25519_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `1966` + // Estimated: `5777` + // Minimum execution time: 67_454_000 picoseconds. + Weight::from_parts(69_136_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn add_ecdsa_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2062` + // Estimated: `5777` + // Minimum execution time: 66_065_000 picoseconds. + Weight::from_parts(68_090_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ed25519_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2104` + // Estimated: `5777` + // Minimum execution time: 64_706_000 picoseconds. + Weight::from_parts(67_009_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_sr25519_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2170` + // Estimated: `5777` + // Minimum execution time: 65_290_000 picoseconds. + Weight::from_parts(67_110_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_ecdsa_key_agreement_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `2107` + // Estimated: `5777` + // Minimum execution time: 65_011_000 picoseconds. + Weight::from_parts(67_020_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:1 w:1) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn add_service_endpoint() -> Weight { + // Proof Size summary in bytes: + // Measured: `1504` + // Estimated: `5777` + // Minimum execution time: 75_496_000 picoseconds. + Weight::from_parts(76_809_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did ServiceEndpoints (r:1 w:1) + /// Proof: Did ServiceEndpoints (max_values: None, max_size: Some(615), added: 3090, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:1) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn remove_service_endpoint() -> Weight { + // Proof Size summary in bytes: + // Measured: `2154` + // Estimated: `5777` + // Minimum execution time: 74_014_000 picoseconds. + Weight::from_parts(75_355_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: Did Did (r:1 w:0) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// The range of component `l` is `[1, 5242880]`. + fn signature_verification_sr25519(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1626` + // Estimated: `5777` + // Minimum execution time: 63_042_000 picoseconds. + Weight::from_parts(41_623_028, 0) + .saturating_add(Weight::from_parts(0, 5777)) + // Standard Error: 0 + .saturating_add(Weight::from_parts(4_301, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: Did Did (r:1 w:0) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// The range of component `l` is `[1, 5242880]`. + fn signature_verification_ed25519(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1626` + // Estimated: `5777` + // Minimum execution time: 61_831_000 picoseconds. + Weight::from_parts(38_865_643, 0) + .saturating_add(Weight::from_parts(0, 5777)) + // Standard Error: 0 + .saturating_add(Weight::from_parts(2_522, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: Did Did (r:1 w:0) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// The range of component `l` is `[1, 5242880]`. + fn signature_verification_ecdsa(l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1627` + // Estimated: `5777` + // Minimum execution time: 50_295_000 picoseconds. + Weight::from_parts(27_300_442, 0) + .saturating_add(Weight::from_parts(0, 5777)) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_153, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn change_deposit_owner() -> Weight { + // Proof Size summary in bytes: + // Measured: `822` + // Estimated: `5777` + // Minimum execution time: 73_863_000 picoseconds. + Weight::from_parts(76_051_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn update_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `5777` + // Minimum execution time: 45_375_000 picoseconds. + Weight::from_parts(46_464_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + + /// Storage: Did Did (r:1 w:0) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `5777` + // Minimum execution time: 186_836_000 picoseconds. + Weight::from_parts(189_377_000, 0) + .saturating_add(Weight::from_parts(0, 5777)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: Did DidBlacklist (r:1 w:0) + /// Proof: Did DidBlacklist (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Did Did (r:1 w:1) + /// Proof: Did Did (max_values: None, max_size: Some(2312), added: 4787, mode: MaxEncodedLen) + /// Storage: Did DidEndpointsCount (r:1 w:0) + /// Proof: Did DidEndpointsCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(132), added: 2607, mode: MaxEncodedLen) + /// Storage: Balances Holds (r:1 w:1) + /// Proof: Balances Holds (max_values: None, max_size: Some(949), added: 3424, mode: MaxEncodedLen) + fn create_from_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `999` + // Estimated: `6204` + // Minimum execution time: 1_008_739_000 picoseconds. + Weight::from_parts(1_020_810_000, 0) + .saturating_add(Weight::from_parts(0, 6204)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_create_ed25519_keys() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 6204 + ); + } + #[test] + fn test_create_sr25519_keys() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 6204 + ); + } + #[test] + fn test_create_ecdsa_keys() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 6204 + ); + } + #[test] + fn test_delete() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_reclaim_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_submit_did_call_ed25519_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_submit_did_call_sr25519_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_submit_did_call_ecdsa_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ed25519_authentication_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_sr25519_authentication_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ecdsa_authentication_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ed25519_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_sr25519_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ecdsa_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ed25519_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_sr25519_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ecdsa_delegation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ed25519_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_sr25519_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_set_ecdsa_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ed25519_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_sr25519_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ecdsa_attestation_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_add_ed25519_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_add_sr25519_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_add_ecdsa_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ed25519_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_sr25519_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_ecdsa_key_agreement_key() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_add_service_endpoint() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_remove_service_endpoint() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_signature_verification_sr25519() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_signature_verification_ed25519() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_signature_verification_ecdsa() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_change_deposit_owner() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_update_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + + #[test] + fn test_dispatch_as() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 5777 + ); + } + #[test] + fn test_create_from_account() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 6204 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/src/weights/frame_system.rs b/dip-template/runtimes/dip-provider/src/weights/frame_system.rs new file mode 100644 index 0000000000..3e821789ca --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/frame_system.rs @@ -0,0 +1,193 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `frame_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=frame-system +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/frame_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `frame_system`. +pub struct WeightInfo(PhantomData); +impl frame_system::WeightInfo for WeightInfo { + /// The range of component `b` is `[0, 3932160]`. + fn remark(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_320_000 picoseconds. + Weight::from_parts(1_367_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 0 + .saturating_add(Weight::from_parts(223, 0).saturating_mul(b.into())) + } + /// The range of component `b` is `[0, 3932160]`. + fn remark_with_event(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_819_000 picoseconds. + Weight::from_parts(4_960_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_147, 0).saturating_mul(b.into())) + } + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + fn set_heap_pages() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1485` + // Minimum execution time: 2_633_000 picoseconds. + Weight::from_parts(2_766_000, 0) + .saturating_add(Weight::from_parts(0, 1485)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `119` + // Estimated: `1604` + // Minimum execution time: 89_656_515_000 picoseconds. + Weight::from_parts(92_710_625_000, 0) + .saturating_add(Weight::from_parts(0, 1604)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn set_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_379_000 picoseconds. + Weight::from_parts(1_438_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 1_236 + .saturating_add(Weight::from_parts(591_504, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `i` is `[0, 1000]`. + fn kill_storage(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_377_000 picoseconds. + Weight::from_parts(1_419_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 526 + .saturating_add(Weight::from_parts(413_184, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `p` is `[0, 1000]`. + fn kill_prefix(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `64 + p * (69 ±0)` + // Estimated: `65 + p * (70 ±0)` + // Minimum execution time: 2_657_000 picoseconds. + Weight::from_parts(2_737_000, 0) + .saturating_add(Weight::from_parts(0, 65)) + // Standard Error: 793 + .saturating_add(Weight::from_parts(806_524, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_set_heap_pages() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 1485 + ); + } + #[test] + fn test_set_code() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 1604 + ); + } + #[test] + fn test_kill_prefix() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 65 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/src/weights/mod.rs b/dip-template/runtimes/dip-provider/src/weights/mod.rs new file mode 100644 index 0000000000..22be9a259b --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/mod.rs @@ -0,0 +1,24 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +pub mod did; +pub mod frame_system; +pub mod pallet_deposit_storage; +pub mod pallet_did_lookup; +pub mod pallet_dip_provider; +pub mod pallet_web3_names; diff --git a/dip-template/runtimes/dip-provider/src/weights/pallet_deposit_storage.rs b/dip-template/runtimes/dip-provider/src/weights/pallet_deposit_storage.rs new file mode 100644 index 0000000000..e675d6ec00 --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/pallet_deposit_storage.rs @@ -0,0 +1,85 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_deposit_storage` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-deposit-storage +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/pallet_deposit_storage.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_deposit_storage`. +pub struct WeightInfo(PhantomData); +impl pallet_deposit_storage::WeightInfo for WeightInfo { + /// Storage: `DepositStorage::Deposits` (r:1 w:1) + /// Proof: `DepositStorage::Deposits` (`max_values`: None, `max_size`: Some(325), added: 2800, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `DipProvider::IdentityCommitments` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) + fn reclaim_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `443` + // Estimated: `4414` + // Minimum execution time: 39_267_000 picoseconds. + Weight::from_parts(39_811_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_reclaim_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/src/weights/pallet_did_lookup.rs b/dip-template/runtimes/dip-provider/src/weights/pallet_did_lookup.rs new file mode 100644 index 0000000000..4a9366ad62 --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/pallet_did_lookup.rs @@ -0,0 +1,321 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_did_lookup` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-did-lookup +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/pallet_did_lookup.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_did_lookup`. +pub struct WeightInfo(PhantomData); +impl pallet_did_lookup::WeightInfo for WeightInfo { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:2) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn associate_account_multisig_sr25519() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 115_816_000 picoseconds. + Weight::from_parts(150_087_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:2) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn associate_account_multisig_ed25519() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 119_599_000 picoseconds. + Weight::from_parts(175_393_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:2) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn associate_account_multisig_ecdsa() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 119_023_000 picoseconds. + Weight::from_parts(176_847_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:2) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn associate_eth_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `377` + // Estimated: `4414` + // Minimum execution time: 115_485_000 picoseconds. + Weight::from_parts(170_435_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:2) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn associate_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 70_488_000 picoseconds. + Weight::from_parts(75_207_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:1) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn remove_sender_association() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 37_873_000 picoseconds. + Weight::from_parts(40_493_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:0 w:1) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + fn remove_account_association() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 38_071_000 picoseconds. + Weight::from_parts(39_343_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn change_deposit_owner() -> Weight { + // Proof Size summary in bytes: + // Measured: `493` + // Estimated: `7838` + // Minimum execution time: 64_007_000 picoseconds. + Weight::from_parts(66_141_000, 0) + .saturating_add(Weight::from_parts(0, 7838)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `DidLookup::ConnectedDids` (r:1 w:1) + /// Proof: `DidLookup::ConnectedDids` (`max_values`: None, `max_size`: Some(129), added: 2604, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn update_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `390` + // Estimated: `4414` + // Minimum execution time: 58_458_000 picoseconds. + Weight::from_parts(59_907_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_associate_account_multisig_sr25519() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_associate_account_multisig_ed25519() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_associate_account_multisig_ecdsa() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_associate_eth_account() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_associate_sender() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_remove_sender_association() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_remove_account_association() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_change_deposit_owner() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 7838 + ); + } + #[test] + fn test_update_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/src/weights/pallet_dip_provider.rs b/dip-template/runtimes/dip-provider/src/weights/pallet_dip_provider.rs new file mode 100644 index 0000000000..ad46d99ebb --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/pallet_dip_provider.rs @@ -0,0 +1,125 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_dip_provider` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-dip-provider +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/pallet_dip_provider.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_dip_provider`. +pub struct WeightInfo(PhantomData); +impl pallet_dip_provider::WeightInfo for WeightInfo { + /// Storage: `Did::Did` (r:1 w:0) + /// Proof: `Did::Did` (`max_values`: None, `max_size`: Some(1447), added: 3922, mode: `MaxEncodedLen`) + /// Storage: `Did::DidBlacklist` (r:1 w:0) + /// Proof: `Did::DidBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Names` (r:1 w:0) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:0) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:21 w:0) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + /// Storage: `DipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `DipProvider::IdentityCommitments` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) + /// Storage: `DepositStorage::Deposits` (r:1 w:1) + /// Proof: `DepositStorage::Deposits` (`max_values`: None, `max_size`: Some(325), added: 2800, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn commit_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `3190` + // Estimated: `55002` + // Minimum execution time: 236_589_000 picoseconds. + Weight::from_parts(266_691_000, 0) + .saturating_add(Weight::from_parts(0, 55002)) + .saturating_add(T::DbWeight::get().reads(29)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `DipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `DipProvider::IdentityCommitments` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) + /// Storage: `DepositStorage::Deposits` (r:1 w:1) + /// Proof: `DepositStorage::Deposits` (`max_values`: None, `max_size`: Some(325), added: 2800, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn delete_identity_commitment() -> Weight { + // Proof Size summary in bytes: + // Measured: `498` + // Estimated: `4414` + // Minimum execution time: 51_471_000 picoseconds. + Weight::from_parts(56_814_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_commit_identity() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 55002 + ); + } + #[test] + fn test_delete_identity_commitment() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } +} diff --git a/dip-template/runtimes/dip-provider/src/weights/pallet_web3_names.rs b/dip-template/runtimes/dip-provider/src/weights/pallet_web3_names.rs new file mode 100644 index 0000000000..e489a91e52 --- /dev/null +++ b/dip-template/runtimes/dip-provider/src/weights/pallet_web3_names.rs @@ -0,0 +1,265 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for `pallet_web3_names` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/dip-provider-node-template +// benchmark +// pallet +// --template=.maintain/runtime-weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --pallet=pallet-web3-names +// --extrinsic=* +// --output=./dip-template/runtimes/dip-provider/src/weights/pallet_web3_names.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_web3_names`. +pub struct WeightInfo(PhantomData); +impl pallet_web3_names::WeightInfo for WeightInfo { + /// Storage: `Web3Names::Names` (r:1 w:1) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Banned` (r:1 w:0) + /// Proof: `Web3Names::Banned` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// The range of component `n` is `[3, 32]`. + fn claim(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `107` + // Estimated: `4414` + // Minimum execution time: 113_246_000 picoseconds. + Weight::from_parts(132_782_588, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Web3Names::Names` (r:1 w:1) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn release_by_owner() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `4414` + // Minimum execution time: 101_512_000 picoseconds. + Weight::from_parts(111_009_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Names` (r:0 w:1) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// The range of component `n` is `[3, 32]`. + fn reclaim_deposit(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `324 + n * (1 ±0)` + // Estimated: `4414` + // Minimum execution time: 36_756_000 picoseconds. + Weight::from_parts(94_263_229, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Web3Names::Banned` (r:1 w:1) + /// Proof: `Web3Names::Banned` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Names` (r:0 w:1) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// The range of component `n` is `[3, 32]`. + fn ban(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `324 + n * (1 ±0)` + // Estimated: `4414` + // Minimum execution time: 36_341_000 picoseconds. + Weight::from_parts(44_569_163, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `Web3Names::Banned` (r:1 w:1) + /// Proof: `Web3Names::Banned` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// The range of component `n` is `[3, 32]`. + fn unban(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `47 + n * (1 ±0)` + // Estimated: `3514` + // Minimum execution time: 8_620_000 picoseconds. + Weight::from_parts(9_336_817, 0) + .saturating_add(Weight::from_parts(0, 3514)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Web3Names::Names` (r:1 w:0) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn change_deposit_owner() -> Weight { + // Proof Size summary in bytes: + // Measured: `531` + // Estimated: `7838` + // Minimum execution time: 60_074_000 picoseconds. + Weight::from_parts(61_305_000, 0) + .saturating_add(Weight::from_parts(0, 7838)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `Web3Names::Owner` (r:1 w:1) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(133), added: 2608, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn update_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `4414` + // Minimum execution time: 55_049_000 picoseconds. + Weight::from_parts(55_580_000, 0) + .saturating_add(Weight::from_parts(0, 4414)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_claim() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_release_by_owner() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_reclaim_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_ban() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } + #[test] + fn test_unban() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 3514 + ); + } + #[test] + fn test_change_deposit_owner() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 7838 + ); + } + #[test] + fn test_update_deposit() { + assert!( + ::BlockWeights::get() + .per_class + .get(frame_support::dispatch::DispatchClass::Normal) + .max_extrinsic + .unwrap_or_else(::max_value) + .proof_size() + > 4414 + ); + } +} diff --git a/pallets/ctype/src/lib.rs b/pallets/ctype/src/lib.rs index a4c27d8f6d..5936cd5587 100644 --- a/pallets/ctype/src/lib.rs +++ b/pallets/ctype/src/lib.rs @@ -94,7 +94,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type EnsureOrigin: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; + type EnsureOrigin: EnsureOrigin; type OverarchingOrigin: EnsureOrigin<::RuntimeOrigin>; type OriginSuccess: CallSources, CtypeCreatorOf>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index f7046b6ae4..586f41525c 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -191,6 +191,13 @@ impl From for DidSignature { } } +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for DidSignature { + fn worst_case(_context: Context) -> Self { + Self::Sr25519(sp_core::sr25519::Signature::from_raw([0u8; 64])) + } +} + pub trait DidVerifiableIdentifier { /// Allows a verifiable identifier to verify a signature it produces and /// return the public key diff --git a/pallets/pallet-deposit-storage/Cargo.toml b/pallets/pallet-deposit-storage/Cargo.toml index 914806a69e..21f4ca1653 100644 --- a/pallets/pallet-deposit-storage/Cargo.toml +++ b/pallets/pallet-deposit-storage/Cargo.toml @@ -1,30 +1,38 @@ [package] authors.workspace = true +description = "Stores all deposits under a single pallet, with suport for namespacing different deposit contexts." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true +name = "pallet-deposit-storage" readme.workspace = true repository.workspace = true version.workspace = true -name = "pallet-deposit-storage" -description = "Stores all deposits under a single pallet, with suport for namespacing different deposit contexts." [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +[dev-dependencies] +pallet-balances = {workspace = true, features = ["std"]} +sp-io = {workspace = true, features = ["std"]} +sp-keystore = {workspace = true, features = ["std"]} + [dependencies] # Substrate dependencies frame-support.workspace = true frame-system.workspace = true kilt-support.workspace = true +log.workspace = true pallet-dip-provider.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sp-runtime.workspace = true sp-std.workspace = true -log.workspace = true +# Benchmarking +frame-benchmarking = {workspace = true, optional = true} +pallet-balances = {workspace = true, optional = true} [features] default = ["std"] @@ -32,10 +40,22 @@ std = [ "frame-support/std", "frame-system/std", "kilt-support/std", + "log/std", "pallet-dip-provider/std", "parity-scale-codec/std", "scale-info/std", "sp-runtime/std", "sp-std/std", - "log/std", + "frame-benchmarking?/std", + "pallet-balances?/std", +] + +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "kilt-support/runtime-benchmarks", + "pallet-dip-provider/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", ] diff --git a/pallets/pallet-deposit-storage/src/benchmarking.rs b/pallets/pallet-deposit-storage/src/benchmarking.rs new file mode 100644 index 0000000000..eb69d6a6fd --- /dev/null +++ b/pallets/pallet-deposit-storage/src/benchmarking.rs @@ -0,0 +1,85 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_benchmarking::v2::*; + +#[benchmarks( + where + T: Config + pallet_balances::Config, +)] +mod benchmarks { + use frame_support::traits::fungible::Mutate; + use frame_system::RawOrigin; + use kilt_support::Deposit; + use sp_runtime::SaturatedConversion; + + use crate::{traits::BenchmarkHooks, Call, Config, DepositEntryOf, Deposits, HoldReason, Pallet}; + + use super::*; + + const KILT: u128 = 10u128.pow(15); + + #[benchmark] + fn reclaim_deposit() { + let (submitter, namespace, key) = T::BenchmarkHooks::pre_reclaim_deposit(); + + assert!(Deposits::::get(&namespace, &key).is_none()); + + let entry = DepositEntryOf:: { + deposit: Deposit { + amount: KILT.saturated_into(), + owner: submitter.clone(), + }, + reason: ::RuntimeHoldReason::from(HoldReason::Deposit), + }; + + let amount = KILT * 100; + + as Mutate<::AccountId>>::set_balance( + &submitter, + amount.saturated_into(), + ); + + Pallet::::add_deposit(namespace.clone(), key.clone(), entry).expect("Creating Deposit should not fail."); + + assert!(Deposits::::get(&namespace, &key).is_some()); + + let origin = RawOrigin::Signed(submitter); + let cloned_namespace = namespace.clone(); + let cloned_key = key.clone(); + + #[extrinsic_call] + Pallet::::reclaim_deposit(origin, cloned_namespace, cloned_key); + + assert!(Deposits::::get(&namespace, &key).is_none()); + + T::BenchmarkHooks::post_reclaim_deposit(); + } + + #[cfg(test)] + mod benchmarks_tests { + use crate::Pallet; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().build_with_keystore(), + crate::mock::TestRuntime, + ); + } +} diff --git a/pallets/pallet-deposit-storage/src/default_weights.rs b/pallets/pallet-deposit-storage/src/default_weights.rs new file mode 100644 index 0000000000..6d06180676 --- /dev/null +++ b/pallets/pallet-deposit-storage/src/default_weights.rs @@ -0,0 +1,73 @@ + +//! Autogenerated weights for pallet_deposit_storage +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-24 +//! STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/kilt-parachain +// benchmark +// pallet +// --pallet +// pallet-deposit-storage +// --extrinsic +// * +// --template +// ./.maintain/weight-template.hbs +// --output +// ./pallets/pallet-deposit-storage/src/defaul_weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_deposit_storage. +pub trait WeightInfo { + fn reclaim_deposit() -> Weight; +} + +/// Weights for pallet_deposit_storage using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `StorageDeposit::Deposits` (r:1 w:1) + /// Proof: `StorageDeposit::Deposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn reclaim_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `907` + // Estimated: `4414` + // Minimum execution time: 704_964 nanoseconds. + Weight::from_parts(1_003_107_000, 4414) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `StorageDeposit::Deposits` (r:1 w:1) + /// Proof: `StorageDeposit::Deposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(949), added: 3424, mode: `MaxEncodedLen`) + fn reclaim_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `907` + // Estimated: `4414` + // Minimum execution time: 704_964 nanoseconds. + Weight::from_parts(1_003_107_000, 4414) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs index e683b27eac..eeca637f53 100644 --- a/pallets/pallet-deposit-storage/src/lib.rs +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -19,16 +19,25 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] +mod default_weights; mod deposit; pub mod traits; -pub use deposit::FixedDepositCollectorViaDepositsPallet; -pub use pallet::*; -pub use traits::NoopDepositStorageHooks; +#[cfg(test)] +mod mock; -#[frame_support::pallet(dev_mode)] +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub use crate::{ + default_weights::WeightInfo, deposit::FixedDepositCollectorViaDepositsPallet, pallet::*, + traits::NoopDepositStorageHooks, +}; + +#[frame_support::pallet] pub mod pallet { use crate::{ + default_weights::WeightInfo, deposit::{free_deposit, reserve_deposit, DepositEntry}, traits::DepositStorageHooks, }; @@ -61,12 +70,15 @@ pub mod pallet { #[pallet::constant] type MaxKeyLength: Get; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHooks: crate::traits::BenchmarkHooks; type CheckOrigin: EnsureOrigin; type Currency: Mutate; type DepositHooks: DepositStorageHooks; - type Namespace: Parameter; + type Namespace: Parameter + MaxEncodedLen; type RuntimeEvent: From> + IsType<::RuntimeEvent>; type RuntimeHoldReason: From + Clone + PartialEq + Debug + FullCodec + MaxEncodedLen + TypeInfo; + type WeightInfo: WeightInfo; } #[pallet::composite_enum] @@ -110,8 +122,9 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - // TODO: Update weight - #[pallet::weight(0)] + #[pallet::weight({ + ::WeightInfo::reclaim_deposit() + })] pub fn reclaim_deposit(origin: OriginFor, namespace: T::Namespace, key: DepositKeyOf) -> DispatchResult { let dispatcher = T::CheckOrigin::ensure_origin(origin)?; diff --git a/pallets/pallet-deposit-storage/src/mock.rs b/pallets/pallet-deposit-storage/src/mock.rs new file mode 100644 index 0000000000..f53c2ae312 --- /dev/null +++ b/pallets/pallet-deposit-storage/src/mock.rs @@ -0,0 +1,129 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{ + construct_runtime, parameter_types, + sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, + }, + traits::{ConstU16, ConstU32, ConstU64, Everything}, +}; +use frame_system::{mocking::MockBlock, EnsureSigned}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; + +use crate::{self as storage_deposit_pallet, NoopDepositStorageHooks}; + +pub(crate) type Balance = u128; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug, Default)] +pub enum DepositNamespaces { + #[default] + ExampleNameSpaces, +} + +construct_runtime!( + pub struct TestRuntime { + System: frame_system, + StorageDepositPallet: storage_deposit_pallet, + Balances: pallet_balances, + } +); + +impl frame_system::Config for TestRuntime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId32; + type BaseCallFilter = Everything; + type Block = MockBlock; + type BlockHashCount = ConstU64<256>; + type BlockLength = (); + type BlockWeights = (); + type DbWeight = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type MaxConsumers = ConstU32<16>; + type Nonce = u64; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16<1>; + type SystemWeightInfo = (); + type Version = (); +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 500; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; + pub const MaxHolds: u32 = 50; + pub const MaxFreezes: u32 = 50; +} + +impl pallet_balances::Config for TestRuntime { + type FreezeIdentifier = RuntimeFreezeReason; + type RuntimeHoldReason = RuntimeHoldReason; + type MaxFreezes = MaxFreezes; + type MaxHolds = MaxHolds; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl crate::Config for TestRuntime { + type CheckOrigin = EnsureSigned; + type Currency = Balances; + type DepositHooks = NoopDepositStorageHooks; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + type MaxKeyLength = ConstU32<256>; + type Namespace = DepositNamespaces; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHooks = (); + type WeightInfo = (); +} + +#[derive(Default)] +pub(crate) struct ExtBuilder; + +impl ExtBuilder { + pub fn _build(self) -> sp_io::TestExternalities { + sp_io::TestExternalities::default() + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn build_with_keystore(self) -> sp_io::TestExternalities { + let mut ext = self._build(); + let keystore = sp_keystore::testing::MemoryKeystore::new(); + ext.register_extension(sp_keystore::KeystoreExt(sp_std::sync::Arc::new(keystore))); + ext + } +} diff --git a/pallets/pallet-deposit-storage/src/traits.rs b/pallets/pallet-deposit-storage/src/traits.rs index f514eee108..b4e179937a 100644 --- a/pallets/pallet-deposit-storage/src/traits.rs +++ b/pallets/pallet-deposit-storage/src/traits.rs @@ -47,3 +47,39 @@ where Ok(()) } } + +// Could be expanded to include traits to set up stuff before all benchmarks, +// and before each benchmark case specifically. +#[cfg(feature = "runtime-benchmarks")] +pub trait BenchmarkHooks +where + Runtime: Config, +{ + fn pre_reclaim_deposit() -> ( + Runtime::AccountId, + Runtime::Namespace, + sp_runtime::BoundedVec, + ); + fn post_reclaim_deposit(); +} + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHooks for () +where + Runtime: Config, + Runtime::AccountId: From<[u8; 32]>, + Runtime::Namespace: Default, +{ + fn pre_reclaim_deposit() -> ( + Runtime::AccountId, + Runtime::Namespace, + sp_runtime::BoundedVec, + ) { + ( + Runtime::AccountId::from([100u8; 32]), + Runtime::Namespace::default(), + sp_runtime::BoundedVec::default(), + ) + } + fn post_reclaim_deposit() {} +} diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index 5025ad90dd..5217db0458 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -13,6 +13,11 @@ version.workspace = true [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +[dev-dependencies] +sp-io = {workspace = true, features = ["std"]} +sp-keystore = {workspace = true, features = ["std"]} +sp-runtime = {workspace = true, features = ["std"]} + [dependencies] frame-support.workspace = true frame-system.workspace = true @@ -22,18 +27,28 @@ scale-info = {workspace = true, features = ["derive"]} sp-core.workspace = true sp-std.workspace = true +# Benchmarks +frame-benchmarking = {workspace = true, optional = true} +sp-runtime = {workspace = true, optional = true} + [features] -default = ["std"] -std = [ - "frame-support/std", - "frame-system/std", - "kilt-support/std", - "parity-scale-codec/std", - "scale-info/std", - "sp-core/std", - "sp-std/std", -] +default = [ "std" ] + runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "kilt-support/runtime-benchmarks", + "frame-benchmarking", + "sp-runtime/runtime-benchmarks", +] +std = [ + "frame-support/std", + "frame-system/std", + "kilt-support/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-std/std", + "frame-benchmarking?/std", + "sp-runtime?/std", ] diff --git a/pallets/pallet-dip-consumer/src/benchmarking.rs b/pallets/pallet-dip-consumer/src/benchmarking.rs new file mode 100644 index 0000000000..78782b4d03 --- /dev/null +++ b/pallets/pallet-dip-consumer/src/benchmarking.rs @@ -0,0 +1,85 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{traits::IdentityProofVerifier, Call, Config, IdentityEntries, Pallet}; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; +use kilt_support::{ + benchmark::IdentityContext, + traits::{GetWorstCase, Instanciate}, +}; + +#[benchmarks( + where + T::AccountId: Instanciate, + T::Identifier: Instanciate, + <::ProofVerifier as IdentityProofVerifier>::Proof: GetWorstCase>, + ::RuntimeCall: From>, +)] +mod benchmarks { + + use super::*; + + type IdentityContextOf = + IdentityContext<::Identifier, ::AccountId>; + + #[benchmark] + fn dispatch_as() { + let submitter = T::AccountId::new(1); + let subject = T::Identifier::new(1); + + let context = IdentityContext:: { + did: subject.clone(), + submitter: submitter.clone(), + }; + + assert!(IdentityEntries::::get(&subject).is_none()); + + let origin = RawOrigin::Signed(submitter); + + let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + + let boxed_call = Box::from(call); + + let proof = <<::ProofVerifier as IdentityProofVerifier>::Proof as GetWorstCase< + IdentityContextOf, + >>::worst_case(context); + + let origin = ::RuntimeOrigin::from(origin); + + #[extrinsic_call] + Pallet::::dispatch_as( + origin as ::RuntimeOrigin, + subject, + proof, + boxed_call, + ); + } + + #[cfg(test)] + mod benchmarks_tests { + use crate::Pallet; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().build_with_keystore(), + crate::mock::TestRuntime, + ); + } +} diff --git a/pallets/pallet-dip-consumer/src/default_weights.rs b/pallets/pallet-dip-consumer/src/default_weights.rs new file mode 100644 index 0000000000..f425e7da3f --- /dev/null +++ b/pallets/pallet-dip-consumer/src/default_weights.rs @@ -0,0 +1,65 @@ + +//! Autogenerated weights for pallet_dip_consumer +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-23 +//! STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/kilt-parachain +// benchmark +// pallet +// --pallet +// pallet-dip-consumer +// --extrinsic +// * +// --template +// ./.maintain/weight-template.hbs +// --output +// ./pallets/pallet-dip-consumer/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_dip_consumer. +pub trait WeightInfo { + fn dispatch_as() -> Weight; +} + +/// Weights for pallet_dip_consumer using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `PalletDipConsumer::IdentityEntries` (r:1 w:1) + /// Proof: `PalletDipConsumer::IdentityEntries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 127_413 nanoseconds. + Weight::from_parts(129_497_000, 3612) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `PalletDipConsumer::IdentityEntries` (r:1 w:1) + /// Proof: `PalletDipConsumer::IdentityEntries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 127_413 nanoseconds. + Weight::from_parts(129_497_000, 3612) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 0c90942b23..ccdd1e0883 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -20,19 +20,26 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod default_weights; pub mod identity; pub mod traits; +#[cfg(test)] +pub mod mock; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + mod origin; -pub use crate::{origin::*, pallet::*, traits::SuccessfulProofVerifier}; +pub use crate::{default_weights::WeightInfo, origin::*, pallet::*, traits::SuccessfulProofVerifier}; -#[frame_support::pallet(dev_mode)] +#[frame_support::pallet] pub mod pallet { use super::*; use frame_support::{ - dispatch::Dispatchable, + dispatch::{Dispatchable, GetDispatchInfo}, pallet_prelude::*, traits::{Contains, EnsureOriginWithArg}, Twox64Concat, @@ -73,8 +80,9 @@ pub mod pallet { /// The logic of the proof verifier, called upon each execution of the /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier; - type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin> + GetDispatchInfo; type RuntimeOrigin: From> + From<::RuntimeOrigin>; + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -95,12 +103,15 @@ pub mod pallet { pub type Origin = DipOrigin<::Identifier, ::AccountId, VerificationResultOf>; - // TODO: Benchmarking #[pallet::call] impl Pallet { // TODO: Replace with a SignedExtra. #[pallet::call_index(0)] - #[pallet::weight(0)] + #[pallet::weight({ + let extrinsic_weight = ::WeightInfo::dispatch_as(); + let call_weight = call.get_dispatch_info().weight; + extrinsic_weight.saturating_add(call_weight) + })] pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, @@ -124,7 +135,11 @@ pub mod pallet { account_address: submitter, details: proof_verification_result, }; - // TODO: Use dispatch info for weight calculation + + // TODO: Maybe find a nicer way to exclude the call dispatched from the + // benchmarks while making sure the call is actually dispatched and passes any + // filters the consumer proof verifier has set. + #[cfg(not(feature = "runtime-benchmark"))] let _ = call.dispatch(did_origin.into()).map_err(|e| e.error)?; Ok(()) } diff --git a/pallets/pallet-dip-consumer/src/mock.rs b/pallets/pallet-dip-consumer/src/mock.rs new file mode 100644 index 0000000000..07d4186250 --- /dev/null +++ b/pallets/pallet-dip-consumer/src/mock.rs @@ -0,0 +1,99 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{ + construct_runtime, + sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, + }, + traits::{ConstU16, ConstU32, ConstU64, Contains, Everything}, +}; +use frame_system::{mocking::MockBlock, EnsureSigned}; + +use crate::traits::SuccessfulProofVerifier; + +construct_runtime!( + pub struct TestRuntime { + System: frame_system, + DipConsumer: crate, + } +); + +impl frame_system::Config for TestRuntime { + type AccountData = (); + type AccountId = AccountId32; + type BaseCallFilter = Everything; + type Block = MockBlock; + type BlockHashCount = ConstU64<256>; + type BlockLength = (); + type BlockWeights = (); + type DbWeight = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type MaxConsumers = ConstU32<16>; + type Nonce = u64; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16<1>; + type SystemWeightInfo = (); + type Version = (); +} + +pub struct CallFilter; + +impl Contains for CallFilter { + fn contains(t: &RuntimeCall) -> bool { + matches!(t, RuntimeCall::System { .. }) + } +} + +impl crate::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type ProofVerifier = SuccessfulProofVerifier; + type LocalIdentityInfo = u128; + type Identifier = AccountId32; + type DispatchOriginCheck = EnsureSigned; + type DipCallOriginFilter = CallFilter; + type WeightInfo = (); +} + +#[derive(Default)] +pub(crate) struct ExtBuilder; + +impl ExtBuilder { + pub fn _build(self) -> sp_io::TestExternalities { + sp_io::TestExternalities::default() + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn build_with_keystore(self) -> sp_io::TestExternalities { + let mut ext = self._build(); + let keystore = sp_keystore::testing::MemoryKeystore::new(); + ext.register_extension(sp_keystore::KeystoreExt(sp_std::sync::Arc::new(keystore))); + ext + } +} diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 5e22e357a1..6c3c0942d3 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -13,26 +13,48 @@ version.workspace = true [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +[dev-dependencies] +kilt-support = {workspace = true, features = ["std", "mock", "try-runtime"]} +sp-io = {workspace = true, features = ["std"]} +sp-keystore = {workspace = true, features = ["std"]} +sp-runtime = {workspace = true, features = ["std"]} + [dependencies] did.workspace = true frame-support.workspace = true frame-system.workspace = true +kilt-support.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} +sp-core.workspace = true sp-std.workspace = true +# Benchmarks +frame-benchmarking = {workspace = true, optional = true} +sp-runtime = {workspace = true, optional = true} + [features] -default = ["std"] -std = [ - "did/std", - "frame-support/std", - "frame-system/std", - "parity-scale-codec/std", - "scale-info/std", - "sp-std/std", -] +default = [ "std" ] + runtime-benchmarks = [ - "did/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks" + "did/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "kilt-support/runtime-benchmarks", + "frame-benchmarking", + "sp-runtime/runtime-benchmarks", +] +std = [ + "did/std", + "frame-support/std", + "frame-system/std", + "kilt-support/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-std/std", + "frame-benchmarking?/std", + "sp-runtime?/std", ] + +try-runtime = [ "did/try-runtime", "kilt-support/try-runtime" ] diff --git a/pallets/pallet-dip-provider/src/benchmarking.rs b/pallets/pallet-dip-provider/src/benchmarking.rs new file mode 100644 index 0000000000..28a73c5cb8 --- /dev/null +++ b/pallets/pallet-dip-provider/src/benchmarking.rs @@ -0,0 +1,109 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{traits::IdentityProvider, Call, Config, Pallet}; +use frame_benchmarking::v2::*; +use kilt_support::{ + benchmark::IdentityContext, + traits::{GenerateBenchmarkOrigin, GetWorstCase, Instanciate}, +}; + +#[benchmarks( + where + T::CommitOriginCheck: GenerateBenchmarkOrigin, + T::AccountId: Instanciate, + T::Identifier: Instanciate, + <::IdentityProvider as IdentityProvider>::Success: GetWorstCase> +)] +mod benchmarks { + + type IdentityContextOf = + IdentityContext<::Identifier, ::AccountId>; + + use crate::IdentityOf; + + use super::*; + + #[benchmark] + fn commit_identity() { + let submitter = T::AccountId::new(1); + let subject = T::Identifier::new(1); + let commitment_version = 0; + + let context = IdentityContext:: { + did: subject.clone(), + submitter: submitter.clone(), + }; + + assert!(Pallet::::identity_commitments(&subject, commitment_version).is_none()); + + let origin: T::RuntimeOrigin = T::CommitOriginCheck::generate_origin(submitter, subject.clone()); + + as GetWorstCase>>::worst_case(context); + + let cloned_subject = subject.clone(); + + #[extrinsic_call] + Pallet::::commit_identity(origin as T::RuntimeOrigin, cloned_subject, Some(commitment_version)); + + assert!(Pallet::::identity_commitments(&subject, commitment_version).is_some()); + } + + #[benchmark] + fn delete_identity_commitment() { + let submitter = T::AccountId::new(1); + let subject = T::Identifier::new(1); + let commitment_version = 0; + + let origin: T::RuntimeOrigin = T::CommitOriginCheck::generate_origin(submitter.clone(), subject.clone()); + + let context = IdentityContext:: { + did: subject.clone(), + submitter, + }; + + as GetWorstCase>>::worst_case(context); + let cloned_subject = subject.clone(); + + Pallet::::commit_identity( + origin.clone() as T::RuntimeOrigin, + subject.clone(), + Some(commitment_version), + ) + .expect("Inserting Identity should not fail."); + + assert!(Pallet::::identity_commitments(&subject, commitment_version).is_some()); + + #[extrinsic_call] + Pallet::::delete_identity_commitment(origin as T::RuntimeOrigin, cloned_subject, Some(commitment_version)); + + assert!(Pallet::::identity_commitments(&subject, commitment_version).is_none()); + } + + #[cfg(test)] + mod benchmarks_tests { + use crate::Pallet; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().build_with_keystore(), + crate::mock::TestRuntime, + ); + } +} diff --git a/pallets/pallet-dip-provider/src/default_weights.rs b/pallets/pallet-dip-provider/src/default_weights.rs new file mode 100644 index 0000000000..51c54bbe09 --- /dev/null +++ b/pallets/pallet-dip-provider/src/default_weights.rs @@ -0,0 +1,108 @@ + +//! Autogenerated weights for pallet_dip_provider +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-23 +//! STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/kilt-parachain +// benchmark +// pallet +// --pallet +// pallet-dip-provider +// --extrinsic +// * +// --template +// ./.maintain/weight-template.hbs +// --output +// ./pallets/pallet-dip-provider/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_dip_provider. +pub trait WeightInfo { + fn commit_identity() -> Weight; + fn delete_identity_commitment() -> Weight; +} + +/// Weights for pallet_dip_provider using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Did::Did` (r:1 w:0) + /// Proof: `Did::Did` (`max_values`: None, `max_size`: Some(2312), added: 4787, mode: `MaxEncodedLen`) + /// Storage: `Did::DidBlacklist` (r:1 w:0) + /// Proof: `Did::DidBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Names` (r:1 w:0) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:0) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:11 w:0) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + /// Storage: `PalletDipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `PalletDipProvider::IdentityCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn commit_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `2781` + // Estimated: `29282` + // Minimum execution time: 1_244_624 nanoseconds. + Weight::from_parts(1_401_381_000, 29282) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `PalletDipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `PalletDipProvider::IdentityCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn delete_identity_commitment() -> Weight { + // Proof Size summary in bytes: + // Measured: `250` + // Estimated: `3715` + // Minimum execution time: 169_415 nanoseconds. + Weight::from_parts(190_131_000, 3715) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `Did::Did` (r:1 w:0) + /// Proof: `Did::Did` (`max_values`: None, `max_size`: Some(2312), added: 4787, mode: `MaxEncodedLen`) + /// Storage: `Did::DidBlacklist` (r:1 w:0) + /// Proof: `Did::DidBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Names` (r:1 w:0) + /// Proof: `Web3Names::Names` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Web3Names::Owner` (r:1 w:0) + /// Proof: `Web3Names::Owner` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `DidLookup::ConnectedAccounts` (r:11 w:0) + /// Proof: `DidLookup::ConnectedAccounts` (`max_values`: None, `max_size`: Some(97), added: 2572, mode: `MaxEncodedLen`) + /// Storage: `PalletDipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `PalletDipProvider::IdentityCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn commit_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `2781` + // Estimated: `29282` + // Minimum execution time: 1_244_624 nanoseconds. + Weight::from_parts(1_401_381_000, 29282) + .saturating_add(RocksDbWeight::get().reads(16_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `PalletDipProvider::IdentityCommitments` (r:1 w:1) + /// Proof: `PalletDipProvider::IdentityCommitments` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn delete_identity_commitment() -> Weight { + // Proof Size summary in bytes: + // Measured: `250` + // Estimated: `3715` + // Minimum execution time: 169_415 nanoseconds. + Weight::from_parts(190_131_000, 3715) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index b9819b51bd..a3672eded9 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -20,17 +20,26 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod default_weights; pub mod traits; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(test)] +mod mock; + pub use crate::{ + default_weights::WeightInfo, pallet::*, traits::{DefaultIdentityCommitmentGenerator, DefaultIdentityProvider, NoopHooks}, }; -#[frame_support::pallet(dev_mode)] +#[frame_support::pallet] pub mod pallet { use super::*; + use default_weights::WeightInfo; use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; use frame_system::pallet_prelude::*; @@ -54,6 +63,7 @@ pub mod pallet { type IdentityProvider: IdentityProvider; type ProviderHooks: ProviderHooks; type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type WeightInfo: WeightInfo; } #[pallet::storage] @@ -96,8 +106,9 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - // TODO: Update weight - #[pallet::weight(0)] + #[pallet::weight({ + ::WeightInfo::commit_identity() + })] pub fn commit_identity( origin: OriginFor, identifier: T::Identifier, @@ -141,8 +152,9 @@ pub mod pallet { } #[pallet::call_index(1)] - // TODO: Update weight - #[pallet::weight(0)] + #[pallet::weight({ + ::WeightInfo::delete_identity_commitment() + })] pub fn delete_identity_commitment( origin: OriginFor, identifier: T::Identifier, diff --git a/pallets/pallet-dip-provider/src/mock.rs b/pallets/pallet-dip-provider/src/mock.rs new file mode 100644 index 0000000000..af57977aff --- /dev/null +++ b/pallets/pallet-dip-provider/src/mock.rs @@ -0,0 +1,100 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::{ + construct_runtime, + sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, + }, + traits::{ConstU16, ConstU32, ConstU64, Everything}, +}; + +use frame_system::mocking::MockBlock; +use kilt_support::mock::mock_origin::{self as mock_origin, DoubleOrigin, EnsureDoubleOrigin}; + +use crate::{DefaultIdentityCommitmentGenerator, DefaultIdentityProvider, NoopHooks}; + +construct_runtime!( + pub struct TestRuntime { + System: frame_system, + DipProvider: crate, + MockOrigin: mock_origin, + } +); + +impl frame_system::Config for TestRuntime { + type AccountData = (); + type AccountId = AccountId32; + type BaseCallFilter = Everything; + type Block = MockBlock; + type BlockHashCount = ConstU64<256>; + type BlockLength = (); + type BlockWeights = (); + type DbWeight = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type MaxConsumers = ConstU32<16>; + type Nonce = u64; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16<1>; + type SystemWeightInfo = (); + type Version = (); +} + +impl crate::Config for TestRuntime { + type CommitOrigin = DoubleOrigin; + type CommitOriginCheck = EnsureDoubleOrigin; + type Identifier = AccountId32; + type IdentityCommitmentGenerator = DefaultIdentityCommitmentGenerator; + type IdentityProvider = DefaultIdentityProvider; + type ProviderHooks = NoopHooks; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl mock_origin::Config for TestRuntime { + type AccountId = ::AccountId; + type RuntimeOrigin = RuntimeOrigin; + type SubjectId = ::Identifier; +} + +#[derive(Default)] +pub(crate) struct ExtBuilder; + +impl ExtBuilder { + pub fn _build(self) -> sp_io::TestExternalities { + sp_io::TestExternalities::default() + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn build_with_keystore(self) -> sp_io::TestExternalities { + let mut ext = self._build(); + let keystore = sp_keystore::testing::MemoryKeystore::new(); + ext.register_extension(sp_keystore::KeystoreExt(sp_std::sync::Arc::new(keystore))); + ext + } +} diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 8cf5c09146..a475c0c9ed 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -37,7 +37,7 @@ pub mod identity_provision { fn retrieve(identifier: &Runtime::Identifier) -> Result; } - // Return the `Default` value if `Identity` adn `Details` both implement it. + // Return the `Default` value if `Identity` and `Details` both implement it. pub struct DefaultIdentityProvider(PhantomData); impl IdentityProvider for DefaultIdentityProvider @@ -126,6 +126,17 @@ where } } +#[cfg(any(feature = "runtime-benchmarks", test))] +impl SubmitterInfo for kilt_support::mock::mock_origin::DoubleOrigin +where + AccountId: Clone, +{ + type Submitter = AccountId; + fn submitter(&self) -> Self::Submitter { + self.0.clone() + } +} + pub trait ProviderHooks where Runtime: Config, diff --git a/pallets/pallet-migration/Cargo.toml b/pallets/pallet-migration/Cargo.toml index 378b7baaa8..905b0d4a7a 100644 --- a/pallets/pallet-migration/Cargo.toml +++ b/pallets/pallet-migration/Cargo.toml @@ -20,12 +20,13 @@ frame-benchmarking = {workspace = true, features = ["std"]} pallet-aura = {workspace = true, features = ["std"]} pallet-timestamp = {workspace = true, features = ["std"]} sp-consensus-aura = {workspace = true, features = ["std"]} +sp-keystore = {workspace = true, features = ["std"]} pallet-session = {workspace = true, features = ["std"]} attestation = {workspace = true, features = ["mock"]} ctype = {workspace = true, features = ["mock"]} -delegation = {workspace = true, features = ["mock"]} -did = {workspace = true, features = ["mock"]} -public-credentials = {workspace = true, features = ["mock"]} +delegation = {workspace = true, features = ["mock"]} +did = {workspace = true, features = ["mock"]} +public-credentials = {workspace = true, features = ["mock"]} [dependencies] # Internal dependencies @@ -36,7 +37,7 @@ pallet-did-lookup.workspace = true pallet-web3-names.workspace = true parachain-staking.workspace = true public-credentials.workspace = true -runtime-common.workspace = true +runtime-common.workspace = true kilt-support.workspace = true ctype = {workspace = true, optional = true} @@ -69,7 +70,7 @@ runtime-benchmarks = [ "pallet-web3-names/runtime-benchmarks", "parachain-staking/runtime-benchmarks", "public-credentials/runtime-benchmarks", - "runtime-common/runtime-benchmarks", + "runtime-common/runtime-benchmarks", "ctype/runtime-benchmarks", "kilt-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", diff --git a/pallets/pallet-migration/src/benchmarking.rs b/pallets/pallet-migration/src/benchmarking.rs index 68b748cdd5..6ced3cd012 100644 --- a/pallets/pallet-migration/src/benchmarking.rs +++ b/pallets/pallet-migration/src/benchmarking.rs @@ -203,7 +203,7 @@ benchmarks! { let sender: AccountIdOf = account("sender", 0, SEED); let attester: ::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); diff --git a/pallets/pallet-migration/src/mock.rs b/pallets/pallet-migration/src/mock.rs index 2567b1bd8b..e07fe2e905 100644 --- a/pallets/pallet-migration/src/mock.rs +++ b/pallets/pallet-migration/src/mock.rs @@ -375,9 +375,9 @@ pub mod runtime { } #[cfg(feature = "runtime-benchmarks")] - impl kilt_support::traits::GetWorstCase for TestSubjectId { + impl kilt_support::traits::GetWorstCase for TestSubjectId { // Only used for benchmark testing, not really relevant. - fn worst_case() -> Self { + fn worst_case(_context: Context) -> Self { crate::mock::TestSubjectId::default() } } diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml index c5398f052f..96f1051b04 100644 --- a/pallets/pallet-relay-store/Cargo.toml +++ b/pallets/pallet-relay-store/Cargo.toml @@ -13,6 +13,12 @@ version.workspace = true [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +[dev-dependencies] +cumulus-primitives-core = { workspace = true, features = ["std"] } +sp-io = {workspace = true, features = ["std"]} +sp-keystore = {workspace = true, features = ["std"]} +sp-runtime = {workspace = true, features = ["std"]} + [dependencies] cumulus-pallet-parachain-system.workspace = true frame-support.workspace = true @@ -21,9 +27,12 @@ log.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sp-core.workspace = true -sp-runtime.workspace = true sp-std.workspace = true +# Benchmarks +frame-benchmarking = {workspace = true, optional = true} +sp-runtime = {workspace = true, optional = true} + [features] default = ["std"] std = [ @@ -33,10 +42,14 @@ std = [ "parity-scale-codec/std", "scale-info/std", "sp-core/std", - "sp-runtime/std", "sp-std/std", + "frame-benchmarking?/std", + "sp-runtime?/std", ] runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "frame-benchmarking", + "sp-runtime/runtime-benchmarks", ] diff --git a/pallets/pallet-relay-store/src/benchmarking.rs b/pallets/pallet-relay-store/src/benchmarking.rs new file mode 100644 index 0000000000..3520016ba4 --- /dev/null +++ b/pallets/pallet-relay-store/src/benchmarking.rs @@ -0,0 +1,93 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{Config, Pallet}; +use frame_benchmarking::v2::*; + +#[benchmarks( + where + T: cumulus_pallet_parachain_system::Config +)] +mod benchmarks { + use cumulus_pallet_parachain_system::RelaychainDataProvider; + use sp_core::H256; + use sp_runtime::{ + traits::{BlockNumberProvider, Get}, + BoundedVec, + }; + + use crate::{relay::RelayParentInfo, LatestBlockHeights, LatestRelayHeads}; + + use super::*; + + #[benchmark] + fn on_finalize() { + let max_blocks_stored = T::MaxRelayBlocksStored::get(); + let latest_block_heights: BoundedVec = (1..=max_blocks_stored) + .collect::>() + .try_into() + .expect("Should not fail to build BoundedVec for LatestBlockHeights"); + let latest_block_heads_iter = latest_block_heights.iter().map(|block_height| { + ( + block_height, + RelayParentInfo { + relay_parent_storage_root: H256::default(), + }, + ) + }); + latest_block_heads_iter + .for_each(|(block_height, parent_info)| LatestRelayHeads::::insert(block_height, parent_info)); + LatestBlockHeights::::put(latest_block_heights); + + assert_eq!( + LatestRelayHeads::::iter().count(), + max_blocks_stored as usize, + "The maximum allowed number of blocks should be stored in storage." + ); + + let new_block_number = max_blocks_stored + 1; + frame_system::Pallet::::set_block_number(new_block_number.into()); + RelaychainDataProvider::::set_block_number(new_block_number); + + #[block] + { + Pallet::::on_finalize_internal(new_block_number.into()) + } + + assert!( + LatestBlockHeights::::get().contains(&new_block_number), + "LatestBlockHeights should contain the information about the new block" + ); + assert!( + LatestRelayHeads::::contains_key(new_block_number), + "LatestRelayHeads should contain the information about the new block" + ); + } + + #[cfg(test)] + mod benchmarks_tests { + use crate::Pallet; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().build_with_keystore(), + crate::mock::TestRuntime, + ); + } +} diff --git a/pallets/pallet-relay-store/src/default_weights.rs b/pallets/pallet-relay-store/src/default_weights.rs new file mode 100644 index 0000000000..ab5ae9ce4d --- /dev/null +++ b/pallets/pallet-relay-store/src/default_weights.rs @@ -0,0 +1,92 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Autogenerated weights for pallet_relay_store +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-01 +//! STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rust-2`, CPU: `12th Gen Intel(R) Core(TM) i9-12900K` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/kilt-parachain +// benchmark +// pallet +// --template=.maintain/weight-template.hbs +// --header=HEADER-GPL +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --chain=dev +// --pallet=pallet-relay-store +// --extrinsic=* +// --output=./pallets/pallet-relay-store/src/default_weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_relay_store. +pub trait WeightInfo { + fn on_finalize() -> Weight; +} + +/// Weights for pallet_relay_store using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `RelayStore::LatestBlockHeights` (r:1 w:1) + /// Proof: `RelayStore::LatestBlockHeights` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `RelayStore::LatestRelayHeads` (r:0 w:2) + /// Proof: `RelayStore::LatestRelayHeads` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `577` + // Estimated: `2062` + // Minimum execution time: 132_937 nanoseconds. + Weight::from_parts(146_340_000, 2062) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `RelayStore::LatestBlockHeights` (r:1 w:1) + /// Proof: `RelayStore::LatestBlockHeights` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `RelayStore::LatestRelayHeads` (r:0 w:2) + /// Proof: `RelayStore::LatestRelayHeads` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `577` + // Estimated: `2062` + // Minimum execution time: 132_937 nanoseconds. + Weight::from_parts(146_340_000, 2062) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs index d5ca7bc1cf..bebe071c46 100644 --- a/pallets/pallet-relay-store/src/lib.rs +++ b/pallets/pallet-relay-store/src/lib.rs @@ -20,9 +20,16 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod default_weights; mod relay; -pub use crate::pallet::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(test)] +mod mock; + +pub use crate::{default_weights::WeightInfo, pallet::*}; #[frame_support::pallet] pub mod pallet { @@ -50,6 +57,7 @@ pub mod pallet { pub trait Config: frame_system::Config { #[pallet::constant] type MaxRelayBlocksStored: Get; + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -62,13 +70,19 @@ pub mod pallet { T: cumulus_pallet_parachain_system::Config, { fn on_initialize(_n: BlockNumberFor) -> Weight { - // Reserve weight to update the last relay state root - // TODO: Replace with benchmarked version of `on_finalize(` - ::DbWeight::get().writes(2) + ::WeightInfo::on_finalize() + } + + fn on_finalize(n: BlockNumberFor) { + Self::on_finalize_internal(n) } + } - // TODO: Benchmarks - fn on_finalize(_n: BlockNumberFor) { + impl Pallet + where + T: cumulus_pallet_parachain_system::Config, + { + pub(crate) fn on_finalize_internal(_n: BlockNumberFor) { // Called before the validation data is cleaned in the // parachain_system::on_finalize hook let Some(new_validation_data) = cumulus_pallet_parachain_system::Pallet::::validation_data() else { diff --git a/pallets/pallet-relay-store/src/mock.rs b/pallets/pallet-relay-store/src/mock.rs new file mode 100644 index 0000000000..2aea08ef70 --- /dev/null +++ b/pallets/pallet-relay-store/src/mock.rs @@ -0,0 +1,102 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_pallet_parachain_system::{ParachainSetCode, RelayNumberStrictlyIncreases}; +use cumulus_primitives_core::ParaId; +use frame_support::{ + construct_runtime, parameter_types, + sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, + }, + traits::{ConstU16, ConstU32, ConstU64, Everything}, +}; +use frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct TestRuntime { + System: frame_system, + ParachainSystem: cumulus_pallet_parachain_system, + RelayStore: crate, + } +); + +impl frame_system::Config for TestRuntime { + type AccountData = (); + type AccountId = AccountId32; + type BaseCallFilter = Everything; + type Block = MockBlock; + type BlockHashCount = ConstU64<256>; + type BlockLength = (); + type BlockWeights = (); + type DbWeight = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type MaxConsumers = ConstU32<16>; + type Nonce = u64; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = ParachainSetCode; + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16<1>; + type SystemWeightInfo = (); + type Version = (); +} + +parameter_types! { + pub const ParachainId: ParaId = ParaId::new(2_000); +} + +impl cumulus_pallet_parachain_system::Config for TestRuntime { + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; + type DmpMessageHandler = (); + type OnSystemEvent = (); + type OutboundXcmpMessageSource = (); + type ReservedDmpWeight = (); + type ReservedXcmpWeight = (); + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainId; + type XcmpMessageHandler = (); +} + +impl crate::Config for TestRuntime { + type MaxRelayBlocksStored = ConstU32<5>; + type WeightInfo = (); +} + +#[derive(Default)] +pub(crate) struct ExtBuilder; + +impl ExtBuilder { + pub fn _build(self) -> sp_io::TestExternalities { + sp_io::TestExternalities::default() + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn build_with_keystore(self) -> sp_io::TestExternalities { + let mut ext = self._build(); + let keystore = sp_keystore::testing::MemoryKeystore::new(); + ext.register_extension(sp_keystore::KeystoreExt(sp_std::sync::Arc::new(keystore))); + ext + } +} diff --git a/pallets/public-credentials/src/benchmarking.rs b/pallets/public-credentials/src/benchmarking.rs index 1153bdf63a..087f2bfec2 100644 --- a/pallets/public-credentials/src/benchmarking.rs +++ b/pallets/public-credentials/src/benchmarking.rs @@ -71,7 +71,7 @@ benchmarks! { let sender: T::AccountId = account("sender", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; c as usize]).expect("Contents should not fail."); let creation_op = Box::new(generate_base_public_credential_creation_op::( @@ -98,7 +98,7 @@ benchmarks! { let sender: T::AccountId = account("sender", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -127,7 +127,7 @@ benchmarks! { let sender: T::AccountId = account("sender", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -156,7 +156,7 @@ benchmarks! { let sender: T::AccountId = account("sender", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -185,7 +185,7 @@ benchmarks! { let sender: T::AccountId = account("sender", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -216,7 +216,7 @@ benchmarks! { let deposit_owner_new: AccountIdOf = account("caller", 1, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let contents = BoundedVec::try_from(vec![0; ::MaxEncodedClaimsLength::get() as usize]).expect("Contents should not fail."); let origin = ::EnsureOrigin::generate_origin(deposit_owner_old.clone(), attester.clone()); @@ -252,7 +252,7 @@ benchmarks! { let deposit_owner: AccountIdOf = account("caller", 0, SEED); let attester: T::AttesterId = account("attester", 0, SEED); let ctype_hash: T::Hash = T::Hash::default(); - let subject_id = ::SubjectId::worst_case(); + let subject_id = ::SubjectId::worst_case(()); let origin = ::EnsureOrigin::generate_origin(deposit_owner.clone(), attester.clone()); reserve_balance::(&deposit_owner); diff --git a/pallets/public-credentials/src/mock.rs b/pallets/public-credentials/src/mock.rs index 07f53d6e3d..3afa5c3346 100644 --- a/pallets/public-credentials/src/mock.rs +++ b/pallets/public-credentials/src/mock.rs @@ -247,9 +247,9 @@ pub(crate) mod runtime { } #[cfg(feature = "runtime-benchmarks")] - impl kilt_support::traits::GetWorstCase for TestSubjectId { + impl kilt_support::traits::GetWorstCase for TestSubjectId { // Only used for benchmark testing, not really relevant. - fn worst_case() -> Self { + fn worst_case(_context: Context) -> Self { crate::mock::TestSubjectId::default() } } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 7134a22ed9..cfba19e88b 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -60,6 +60,9 @@ xcm-builder.workspace = true xcm-executor.workspace = true xcm.workspace = true +#benchmarking +frame-benchmarking = { workspace = true, optional = true } + [features] default = ["std"] fast-gov = [] @@ -71,7 +74,6 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "kilt-dip-support/runtime-benchmarks", "kilt-support/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -82,6 +84,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", ] std = [ "cumulus-pallet-parachain-system/std", @@ -122,6 +125,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "frame-benchmarking?/std", ] try-runtime = [ "cumulus-pallet-parachain-system/try-runtime", diff --git a/runtimes/common/src/assets.rs b/runtimes/common/src/assets.rs index 784bc692df..21368e6ca3 100644 --- a/runtimes/common/src/assets.rs +++ b/runtimes/common/src/assets.rs @@ -104,8 +104,8 @@ mod benchmarks { } } - impl kilt_support::traits::GetWorstCase for AssetDid { - fn worst_case() -> Self { + impl kilt_support::traits::GetWorstCase for AssetDid { + fn worst_case(_context: Context) -> Self { // Returns the worst case for an AssetDID, which is represented by the longest // identifier according to the spec. Self::try_from( diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 134c65de9b..4b6ca4d0e3 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -17,18 +17,25 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::did_details::DidDetails; +use frame_support::ensure; use frame_system::pallet_prelude::BlockNumberFor; use kilt_dip_support::merkle::RevealedWeb3Name; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; +use sp_core::ConstU32; +use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::vec::Vec; -#[derive(Encode, Decode, TypeInfo)] +#[cfg(feature = "runtime-benchmarks")] +use kilt_support::{benchmark::IdentityContext, traits::GetWorstCase}; + +#[derive(Encode, Decode, TypeInfo, Debug)] pub enum LinkedDidInfoProviderError { DidNotFound, DidDeleted, + TooManyLinkedAccounts, Internal, } @@ -37,6 +44,7 @@ impl From for u16 { match value { LinkedDidInfoProviderError::DidNotFound => 0, LinkedDidInfoProviderError::DidDeleted => 1, + LinkedDidInfoProviderError::TooManyLinkedAccounts => 2, LinkedDidInfoProviderError::Internal => u16::MAX, } } @@ -45,18 +53,18 @@ impl From for u16 { pub type Web3OwnershipOf = RevealedWeb3Name<::Web3Name, BlockNumberFor>; -pub struct LinkedDidInfoOf +pub struct LinkedDidInfoOf where Runtime: did::Config + pallet_web3_names::Config, { pub did_details: DidDetails, pub web3_name_details: Option>, - pub linked_accounts: Vec, + pub linked_accounts: BoundedVec>, } -pub struct LinkedDidInfoProvider; +pub struct LinkedDidInfoProvider; -impl IdentityProvider for LinkedDidInfoProvider +impl IdentityProvider for LinkedDidInfoProvider where Runtime: did::Config::Identifier> + pallet_web3_names::Config::Identifier> @@ -64,7 +72,7 @@ where + pallet_dip_provider::Config, { type Error = LinkedDidInfoProviderError; - type Success = LinkedDidInfoOf; + type Success = LinkedDidInfoOf; fn retrieve(identifier: &Runtime::Identifier) -> Result { let did_details = match ( @@ -90,7 +98,26 @@ where } else { Ok(None) }?; - let linked_accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier).collect(); + + // Check if the user has too many linked accounts. If they have more than + // [MAX_LINKED_ACCOUNTS], we throw an error. + let are_linked_accounts_within_limit = + pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) + .nth(MAX_LINKED_ACCOUNTS.saturated_into()) + .is_none(); + + ensure!( + are_linked_accounts_within_limit, + LinkedDidInfoProviderError::TooManyLinkedAccounts + ); + + let linked_accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) + .take(MAX_LINKED_ACCOUNTS.saturated_into()) + .collect::>() + .try_into() + // Should never happen since we checked above. + .map_err(|_| LinkedDidInfoProviderError::TooManyLinkedAccounts)?; + Ok(LinkedDidInfoOf { did_details, web3_name_details, @@ -98,3 +125,103 @@ where }) } } + +#[cfg(feature = "runtime-benchmarks")] +impl GetWorstCase> + for LinkedDidInfoOf +where + Runtime: did::Config::Identifier> + + pallet_web3_names::Config::Identifier> + + pallet_did_lookup::Config::Identifier> + + pallet_dip_provider::Config + + pallet_balances::Config, + ::AccountId: Into + From, + ::AccountId: AsRef<[u8; 32]> + From<[u8; 32]>, +{ + fn worst_case(context: IdentityContext) -> Self { + use did::{ + did_details::DidVerificationKey, + mock_utils::{generate_base_did_creation_details, get_key_agreement_keys}, + }; + use frame_benchmarking::{vec, Zero}; + use frame_support::traits::fungible::Mutate; + use sp_io::crypto::{ed25519_generate, sr25519_generate}; + use sp_runtime::{traits::Get, KeyTypeId}; + + use crate::constants::KILT; + + // Did Details. + + let submitter = context.submitter; + let did = context.did; + + let amount = KILT * 100; + + // give some money + as Mutate<::AccountId>>::set_balance( + &submitter, + amount.saturated_into(), + ); + + let max_new_keys = ::MaxNewKeyAgreementKeys::get(); + + let new_key_agreement_keys = get_key_agreement_keys::(max_new_keys); + + let mut did_creation_details = generate_base_did_creation_details(did.clone(), submitter.clone()); + + let attestation_key = ed25519_generate(KeyTypeId(*b"0001"), None); + let delegation_key = ed25519_generate(KeyTypeId(*b"0002"), None); + let auth_key = ed25519_generate(KeyTypeId(*b"0003"), None); + did_creation_details.new_attestation_key = Some(DidVerificationKey::from(attestation_key)); + did_creation_details.new_delegation_key = Some(DidVerificationKey::from(delegation_key)); + did_creation_details.new_key_agreement_keys = new_key_agreement_keys; + + let did_details = did::did_details::DidDetails::new_with_creation_details( + did_creation_details, + DidVerificationKey::from(auth_key), + ) + .expect("Creation of DID details should not fail."); + + // add to storage. + did::Pallet::::try_insert_did(did.clone(), did_details.clone(), submitter.clone()) + .expect("Inserting Did should not fail."); + + let max_name_length = ::MaxNameLength::get() + .try_into() + .expect("max name length should not fail."); + + let web3_name_input: BoundedVec::MaxNameLength> = + BoundedVec::try_from(vec![b'1'; max_name_length]).expect("BoundedVec creation should not fail."); + + let web3_name = pallet_web3_names::Web3NameOf::::try_from(web3_name_input.to_vec()) + .expect("Creation of w3n from w3n input should not fail."); + + pallet_web3_names::Pallet::::register_name(web3_name.clone(), did.clone(), submitter.clone()) + .expect("Inserting w3n into storage should not fail."); + + let web3_name_details = Some(RevealedWeb3Name { + web3_name, + claimed_at: BlockNumberFor::::zero(), + }); + + let mut linked_accounts = vec![]; + + (0..MAX_LINKED_ACCOUNTS).for_each(|index| { + let connected_acc = sr25519_generate(KeyTypeId(index.to_be_bytes()), None); + let connected_acc_id: ::AccountId = connected_acc.into(); + let linkable_id: LinkableAccountId = connected_acc_id.into(); + pallet_did_lookup::Pallet::::add_association(submitter.clone(), did.clone(), linkable_id.clone()) + .expect("association should not fail."); + + linked_accounts.push(linkable_id); + }); + + LinkedDidInfoOf { + did_details, + linked_accounts: linked_accounts + .try_into() + .expect("BoundedVec creation of linked accounts should not fail."), + web3_name_details, + } + } +} diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index ec62b3d6a8..5eb5829a8d 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -83,8 +83,8 @@ pub mod v0 { LinkableAccountId, >; - pub(super) fn calculate_root_with_db( - identity: &LinkedDidInfoOf, + pub(super) fn calculate_root_with_db( + identity: &LinkedDidInfoOf, db: &mut MemoryDB, ) -> Result where @@ -232,8 +232,8 @@ pub mod v0 { Ok(trie_builder.root().to_owned()) } - pub(super) fn generate_proof<'a, Runtime, K, A>( - identity: &LinkedDidInfoOf, + pub(super) fn generate_proof<'a, Runtime, K, A, const MAX_LINKED_ACCOUNT: u32>( + identity: &LinkedDidInfoOf, key_ids: K, should_include_web3_name: bool, account_ids: A, @@ -323,12 +323,12 @@ pub mod v0 { }) } - pub(super) fn generate_commitment( + pub(super) fn generate_commitment( identity: &IdentityOf, ) -> Result where Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config + pallet_dip_provider::Config, - Runtime::IdentityProvider: IdentityProvider>, + Runtime::IdentityProvider: IdentityProvider>, { let mut db = MemoryDB::default(); calculate_root_with_db(identity, &mut db) @@ -337,10 +337,10 @@ pub mod v0 { pub struct DidMerkleRootGenerator(PhantomData); -impl IdentityCommitmentGenerator for DidMerkleRootGenerator +impl IdentityCommitmentGenerator for DidMerkleRootGenerator where Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config + pallet_dip_provider::Config, - Runtime::IdentityProvider: IdentityProvider>, + Runtime::IdentityProvider: IdentityProvider>, { type Error = DidMerkleProofError; type Output = Runtime::Hash; @@ -351,7 +351,7 @@ where version: IdentityCommitmentVersion, ) -> Result { match version { - 0 => v0::generate_commitment::(identity), + 0 => v0::generate_commitment::(identity), _ => Err(DidMerkleProofError::UnsupportedVersion), } } @@ -361,8 +361,8 @@ impl DidMerkleRootGenerator where Runtime: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, { - pub fn generate_proof<'a, K, A>( - identity: &LinkedDidInfoOf, + pub fn generate_proof<'a, K, A, const MAX_LINKED_ACCOUNT: u32>( + identity: &LinkedDidInfoOf, version: IdentityCommitmentVersion, key_ids: K, should_include_web3_name: bool, diff --git a/support/src/benchmark.rs b/support/src/benchmark.rs new file mode 100644 index 0000000000..b194969126 --- /dev/null +++ b/support/src/benchmark.rs @@ -0,0 +1,23 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/// Manly used for the DIP benchmarking. The worst case can be created for the +/// did and submitter. +#[derive(Clone)] +pub struct IdentityContext { + pub did: DidIdentifier, + pub submitter: AccountId, +} diff --git a/support/src/lib.rs b/support/src/lib.rs index 758062f925..e156ddc020 100644 --- a/support/src/lib.rs +++ b/support/src/lib.rs @@ -28,3 +28,6 @@ pub mod mock; #[cfg(any(feature = "try-runtime", test))] pub mod test_utils; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark; diff --git a/support/src/mock.rs b/support/src/mock.rs index df757cfd0e..a6b5fdde5f 100644 --- a/support/src/mock.rs +++ b/support/src/mock.rs @@ -32,7 +32,10 @@ use sp_runtime::AccountId32; pub mod mock_origin { use sp_std::marker::PhantomData; - use frame_support::{traits::EnsureOrigin, Parameter}; + use frame_support::{ + traits::{EnsureOrigin, EnsureOriginWithArg}, + Parameter, + }; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::AccountId32; @@ -105,6 +108,35 @@ pub mod mock_origin { } } + impl EnsureOriginWithArg + for EnsureDoubleOrigin + where + OuterOrigin: Into, OuterOrigin>> + + From> + + Clone, + SubjectId: PartialEq + Clone, + AccountId: Clone + Decode, + { + type Success = DoubleOrigin; + + fn try_origin(o: OuterOrigin, a: &SubjectId) -> Result { + let did_origin: DoubleOrigin = o.clone().into()?; + if did_origin.1 == *a { + Ok(did_origin) + } else { + Err(o) + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &SubjectId) -> Result { + let zero_account_id = AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"); + + Ok(OuterOrigin::from(DoubleOrigin(zero_account_id, a.clone()))) + } + } + #[cfg(feature = "runtime-benchmarks")] impl crate::traits::GenerateBenchmarkOrigin for EnsureDoubleOrigin diff --git a/support/src/traits.rs b/support/src/traits.rs index 2c2fa6a668..b2a9f8f17f 100644 --- a/support/src/traits.rs +++ b/support/src/traits.rs @@ -81,8 +81,36 @@ pub trait GenerateBenchmarkOrigin { /// Trait that allows types to implement a worst case value for a type, /// only when running benchmarks. #[cfg(feature = "runtime-benchmarks")] -pub trait GetWorstCase { - fn worst_case() -> Self; +pub trait GetWorstCase { + fn worst_case(context: Context) -> Self; +} + +#[cfg(feature = "runtime-benchmarks")] +impl GetWorstCase for u32 { + fn worst_case(_context: T) -> Self { + u32::MAX + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl GetWorstCase for () { + fn worst_case(_context: T) -> Self {} +} + +/// Trait that allows instanciating multiple instances of a type. +#[cfg(feature = "runtime-benchmarks")] +pub trait Instanciate { + fn new(instance: u32) -> Self; +} + +#[cfg(feature = "runtime-benchmarks")] +impl Instanciate for sp_runtime::AccountId32 { + fn new(instance: u32) -> Self { + use sp_runtime::traits::Hash; + sp_runtime::AccountId32::from(<[u8; 32]>::from(sp_runtime::traits::BlakeTwo256::hash( + &instance.to_be_bytes(), + ))) + } } /// Generic filter. From 8de6e8b6855c4c82b881ed38ee07ce4e47a32f3d Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 6 Dec 2023 11:19:58 +0000 Subject: [PATCH 19/28] chore: add DIP-related comments (#584) Fixes https://github.com/KILTprotocol/ticket/issues/2560. - [x] ~WIP.~ To rebase on top of https://github.com/KILTprotocol/kilt-node/pull/583 once merged. --------- Co-authored-by: Chris Chinchilla --- crates/kilt-dip-support/src/did.rs | 40 ++++- crates/kilt-dip-support/src/export/child.rs | 152 ++++++++++++++++-- crates/kilt-dip-support/src/export/common.rs | 5 + crates/kilt-dip-support/src/export/mod.rs | 2 + crates/kilt-dip-support/src/export/sibling.rs | 146 +++++++++++++++-- crates/kilt-dip-support/src/lib.rs | 8 +- crates/kilt-dip-support/src/merkle.rs | 63 +++++++- crates/kilt-dip-support/src/state_proofs.rs | 36 ++++- crates/kilt-dip-support/src/traits.rs | 60 ++++++- crates/kilt-dip-support/src/utils.rs | 3 + dip-template/nodes/dip-consumer/Cargo.toml | 1 + .../nodes/dip-consumer/src/command.rs | 8 +- dip-template/nodes/dip-provider/Cargo.toml | 1 + .../nodes/dip-provider/src/command.rs | 8 +- dip-template/runtimes/dip-consumer/src/dip.rs | 24 +++ dip-template/runtimes/dip-consumer/src/lib.rs | 11 ++ .../dip-consumer/src/origin_adapter.rs | 7 + dip-template/runtimes/dip-provider/src/dip.rs | 28 ++++ dip-template/runtimes/dip-provider/src/lib.rs | 6 + pallets/pallet-deposit-storage/src/deposit.rs | 6 + pallets/pallet-deposit-storage/src/lib.rs | 50 +++++- pallets/pallet-deposit-storage/src/traits.rs | 5 + pallets/pallet-dip-consumer/Cargo.toml | 2 +- pallets/pallet-dip-consumer/README.md | 60 +++++++ pallets/pallet-dip-consumer/src/identity.rs | 43 ----- pallets/pallet-dip-consumer/src/lib.rs | 70 +++++--- pallets/pallet-dip-consumer/src/origin.rs | 8 + pallets/pallet-dip-consumer/src/traits.rs | 14 +- pallets/pallet-dip-provider/Cargo.toml | 2 +- pallets/pallet-dip-provider/README.md | 50 ++++++ pallets/pallet-dip-provider/src/lib.rs | 42 ++++- pallets/pallet-dip-provider/src/traits.rs | 21 ++- pallets/pallet-relay-store/Cargo.toml | 2 +- pallets/pallet-relay-store/src/lib.rs | 11 +- pallets/pallet-relay-store/src/relay.rs | 2 + runtime-api/dip-provider/src/lib.rs | 2 + runtimes/common/src/dip/README.md | 16 ++ runtimes/common/src/dip/did.rs | 9 ++ runtimes/common/src/dip/merkle.rs | 18 +++ runtimes/common/src/dip/mod.rs | 4 + 40 files changed, 924 insertions(+), 122 deletions(-) create mode 100644 pallets/pallet-dip-consumer/README.md delete mode 100644 pallets/pallet-dip-consumer/src/identity.rs create mode 100644 pallets/pallet-dip-provider/README.md create mode 100644 runtimes/common/src/dip/README.md diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index f598614925..dfdce46dba 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -16,6 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Module to deal with cross-chain KILT DIDs. + use did::{ did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, @@ -31,15 +33,24 @@ use crate::{ traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, }; +/// Type returned by the Merkle proof verifier component of the DIP consumer +/// after verifying a DIP Merkle proof. #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] pub(crate) struct RevealedDidKeysAndSignature { + /// The keys revelaed in the Merkle proof. pub merkle_leaves: RevealedDidKeys, + /// The [`DIDSignature`] + consumer chain block number to which the DID + /// signature is anchored. pub did_signature: TimeBoundDidSignature, } +/// A DID signature anchored to a specific block height. #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] pub struct TimeBoundDidSignature { + /// The signature. pub signature: DidSignature, + /// The block number, in the context of the local executor, to which the + /// signature is anchored. pub block_number: BlockNumber, } @@ -75,6 +86,30 @@ impl From for u8 { } } +/// Proof verifier that tries to verify a DID signature over a given payload by +/// using one of the DID keys revealed in the Merkle proof. This verifier is +/// typically used in conjunction with a verifier that takes a user-provided +/// input Merkle proof, verifies it, and transforms it into a struct that this +/// and other verifiers can easily consume, e.g., a list of DID keys. +/// The generic types are the following: +/// * `Call`: The call to be dispatched on the local chain after verifying the +/// DID signature. +/// * `Submitter`: The blockchain account (**not** the identity subject) +/// submitting the cross-chain transaction (and paying for its execution +/// fees). +/// * `DidLocalDetails`: Any information associated to the identity subject that +/// is stored locally, e.g., under the `IdentityEntries` map of the +/// `pallet-dip-consumer` pallet. +/// * `MerkleProofEntries`: The type returned by the Merkle proof verifier that +/// includes the identity parts revealed in the Merkle proof. +/// * `ContextProvider`: Provides additional local context (e.g., current block +/// number) to verify the DID signature. +/// * `RemoteKeyId`: Definition of a DID key ID as specified by the provider. +/// * `RemoteAccountId`: Definition of a linked account ID as specified by the +/// provider. +/// * `RemoteBlockNumber`: Definition of a block number on the provider chain. +/// * `CallVerifier`: A type specifying whether the provided `Call` can be +/// dispatched with the information provided in the DIP proof. pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< Call, Submitter, @@ -135,7 +170,6 @@ impl< DipCallOriginFilter, DidVerificationKeyRelationship)>, { #[cfg(not(feature = "runtime-benchmarks"))] - #[allow(clippy::result_unit_err)] pub(crate) fn verify_did_signature_for_call( call: &Call, submitter: &Submitter, @@ -147,7 +181,7 @@ impl< > { use frame_support::ensure; - let block_number = ContextProvider::block_number(); + let block_number = ContextProvider::current_block_number(); let is_signature_fresh = if let Some(blocks_ago_from_now) = block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) { @@ -213,7 +247,7 @@ impl< > { use sp_core::ed25519; - let block_number = ContextProvider::block_number(); + let block_number = ContextProvider::current_block_number(); // Computed but ignored if let Some(blocks_ago_from_now) = block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index 6fd215de33..00a897b538 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -34,13 +34,17 @@ use crate::{ merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, traits::{ - Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; +/// A KILT-specific DIP identity proof for a parent consumer that supports +/// versioning. +/// +/// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] #[non_exhaustive] pub enum VersionedChildParachainDipStateProof< @@ -118,8 +122,48 @@ where } } -// Implements the same `IdentityProvider` trait, but it is internally configured -// by receiving the runtime definitions of both the provider and the receiver. +/// Proof verifier configured given a specific KILT runtime implementation. +/// +/// A specialization of the +/// [`GenericVersionedDipChildProviderStateProofVerifier`] type, with +/// configurations derived from the provided KILT runtime. +/// +/// The generic types are the following: +/// * `KiltRuntime`: A KILT runtime definition. +/// * `KiltParachainId`: The ID of the specific KILT parachain instance. +/// * `RelayChainInfo`: The type providing information about the consumer +/// (relay)chain. +/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain +/// for the generation of the DIP identity commitment. +/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key +/// relationship. This information is used once the Merkle proof is verified, +/// to filter only the revealed keys that match the provided relationship. +/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will +/// accept revealed as part of the DIP identity proof. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the +/// verifier will accept revealed as part of the DIP identity proof. +/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID +/// signature is considered fresh. +/// +/// It specializes the [`GenericVersionedDipChildProviderStateProofVerifier`] +/// type by using the following types for its generics: +/// * `RelayChainInfo`: The provided `RelayChainInfo`. +/// * `ChildProviderParachainId`: The provided `KiltParachainId`. +/// * `ChildProviderStateInfo`: The +/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the +/// provided `KiltRuntime`. +/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. +/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided +/// `KiltRuntime`. +/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. +/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. +/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. +/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. +/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type +/// configured with the provided `KiltRuntime` and +/// `MAX_DID_SIGNATURE_DURATION`. +/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. pub struct KiltVersionedChildProviderVerifier< KiltRuntime, KiltParachainId, @@ -258,10 +302,15 @@ impl< } } -// More generic version compared to `VersionedChildKiltProviderVerifier`, to be -// used in cases in which it is not possible or not desirable to depend on the -// whole provider runtime definition. Hence, required types must be filled in -// manually. +/// Generic proof verifier for KILT-specific DIP identity proofs of different +/// versions coming from a child provider running one of the available KILT +/// runtimes. +/// +/// It expects the DIP proof to be a [`VersionedChildParachainDipStateProof`], +/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully +/// verified. +/// +/// For more info, refer to the version-specific proof identifiers. pub struct GenericVersionedDipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, @@ -346,8 +395,10 @@ impl< ChildProviderParachainId: Get, - ChildProviderStateInfo: - ProviderParachainStateInfo, + ChildProviderStateInfo: ProviderParachainStorageInfo< + Identifier = ConsumerRuntime::Identifier, + Commitment = ProviderDipMerkleHasher::Out, + >, OutputOf: Ord + From::Hasher>>, ChildProviderStateInfo::BlockNumber: Parameter + 'static, ChildProviderStateInfo::Commitment: Decode, @@ -431,7 +482,7 @@ pub mod latest { pub use super::v0::ChildParachainDipStateProof; } -mod v0 { +pub mod v0 { use super::*; use parity_scale_codec::Codec; @@ -457,11 +508,15 @@ mod v0 { }, traits::{ Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, - ProviderParachainStateInfo, RelayChainStorageInfo, + ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, }; + /// The expected format of a cross-chain DIP identity proof when the + /// identity information is bridged from a provider that is a child of + /// the chain where the information is consumed (i.e., consumer + /// chain). #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct ChildParachainDipStateProof< ParentBlockHeight: Copy + Into + TryFrom, @@ -469,12 +524,85 @@ mod v0 { DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, > { + /// The state proof for the given parachain head. para_state_root: ParachainRootStateProof, + /// The relaychain header for the relaychain block specified in the + /// `para_state_root`. relay_header: Header, + /// The raw state proof for the DIP commitment of the given subject. dip_identity_commitment: Vec>, + /// The cross-chain DID signature. did: DipMerkleProofAndDidSignature, } + /// Generic proof verifier for KILT-specific DIP identity proofs coming from + /// a child provider running one of the available KILT runtimes. + /// The proof verification step is performed on every request, and this + /// specific verifier has no knowledge of caching or storing state about the + /// subject. It only takes the provided + /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is + /// successfully verified, to prevent replay attacks. If additional logic is + /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a + /// different verifier or a wrapper around this verifier must be built. + /// + /// It expects the DIP proof to be a + /// [`VersionedChildParachainDipStateProof`], and returns + /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. + /// This information is then made availabe as an origin to the downstream + /// call dispatched. + /// + /// The verifier performs the following steps: + /// 1. Verifies the state proof about the state root of the relaychain block + /// at the provided height. The state root is retrieved from the provided + /// relaychain header, which is checked to be the header of a + /// previously-finalized relaychain block. + /// 2. Verifies the state proof about the DIP commitment value on the + /// provider parachain at the block finalized at the given relaychain + /// block, using the relay state root validated in the previous step. + /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID + /// Document against the retrieved DIP commitment validated in the + /// previous step. + /// 4. Verifies the cross-chain DID signature over the payload composed by + /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: + /// * `C`: The `RuntimeCall` to dispatch after performing DIP + /// verification. + /// * `D`: The local details associated to the DID subject as stored in + /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. + /// * `S`: The tx submitter's address. + /// * `B`: The block number of the consumer chain provided in the + /// cross-chain DID signature. + /// * `G`: The genesis hash of the consumer chain. + /// * `E`: Any additional information provided by the + /// `LocalContextProvider` implementation. + /// The generic types + /// indicate the following: + /// * `RelayChainInfo`: The type providing information about the consumer + /// (relay)chain. + /// * `ChildProviderParachainId`: The parachain ID of the provider KILT + /// child parachain. + /// * `ChildProviderStateInfo`: The type providing storage and state + /// information about the provider KILT child parachain. + /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT + /// parachain for the generation of the DIP identity commitment. + /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the + /// KILT child parachain. + /// * `ProviderAccountId`: The runtime type of an account ID as defined by + /// the KILT child parachain. + /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the + /// KILT child parachain. + /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as + /// defined by the KILT child parachain. + /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier + /// will accept revealed as part of the DIP identity proof. + /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the + /// verifier will accept revealed as part of the DIP identity proof. + /// * `LocalContextProvider`: The type providing context of the consumer + /// chain (e.g., current block number) for the sake of cross-chain DID + /// signature verification. + /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID + /// key relationship. This information is used once the Merkle proof is + /// verified, to filter only the revealed keys that match the provided + /// relationship. pub struct DipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, @@ -559,7 +687,7 @@ mod v0 { ChildProviderParachainId: Get, - ChildProviderStateInfo: ProviderParachainStateInfo< + ChildProviderStateInfo: ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-support/src/export/common.rs index fa98de6f16..455c5848a9 100644 --- a/crates/kilt-dip-support/src/export/common.rs +++ b/crates/kilt-dip-support/src/export/common.rs @@ -29,7 +29,9 @@ pub mod v0 { #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] pub struct ParachainRootStateProof { + /// The relaychain block height for which the proof has been generated. pub(crate) relay_block_height: RelayBlockHeight, + /// The raw state proof. pub(crate) proof: BoundedBlindedValue, } @@ -49,7 +51,10 @@ pub mod v0 { #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] pub struct DipMerkleProofAndDidSignature { + /// The DIP Merkle proof revealing some leaves about the DID subject's + /// identity. pub(crate) leaves: DidMerkleProof, + /// The cross-chain DID signature. pub(crate) signature: TimeBoundDidSignature, } diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-support/src/export/mod.rs index 8d2f289926..01fd5ad2a1 100644 --- a/crates/kilt-dip-support/src/export/mod.rs +++ b/crates/kilt-dip-support/src/export/mod.rs @@ -16,7 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +/// Verification logic to integrate a child chain as a DIP provider. pub mod child; +/// Verification logic to integrate a sibling chain as a DIP provider. pub mod sibling; mod common; diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index 0fd6ec989e..daf3759881 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -38,6 +38,10 @@ use crate::{ BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; +/// A KILT-specific DIP identity proof for a sibling consumer that supports +/// versioning. +/// +/// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] #[non_exhaustive] pub enum VersionedSiblingParachainDipStateProof< @@ -130,8 +134,49 @@ where } } } -// Implements the same `IdentityProvider` trait, but it is internally configured -// by receiving the runtime definitions of both the provider and the receiver. + +/// Proof verifier configured given a specific KILT runtime implementation. +/// +/// It is a specialization of the +/// [`GenericVersionedDipSiblingProviderStateProofVerifier`] type, with +/// configurations derived from the provided KILT runtime. +/// +/// The generic types +/// indicate the following: +/// * `KiltRuntime`: A KILT runtime definition. +/// * `KiltParachainId`: The ID of the specific KILT parachain instance. +/// * `RelayChainInfo`: The type providing information about the relaychain. +/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain +/// for the generation of the DIP identity commitment. +/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key +/// relationship. This information is used once the Merkle proof is verified, +/// to filter only the revealed keys that match the provided relationship. +/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will +/// accept revealed as part of the DIP identity proof. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the +/// verifier will accept revealed as part of the DIP identity proof. +/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID +/// signature is considered fresh. +/// +/// It specializes the [`GenericVersionedDipSiblingProviderStateProofVerifier`] +/// type by using the following types for its generics: +/// * `RelayChainInfo`: The provided `RelayChainInfo`. +/// * `ChildProviderParachainId`: The provided `KiltParachainId`. +/// * `ChildProviderStateInfo`: The +/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the +/// provided `KiltRuntime`. +/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. +/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided +/// `KiltRuntime`. +/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. +/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. +/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. +/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. +/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type +/// configured with the provided `KiltRuntime` and +/// `MAX_DID_SIGNATURE_DURATION`. +/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. pub struct KiltVersionedSiblingProviderVerifier< KiltRuntime, KiltParachainId, @@ -253,10 +298,15 @@ impl< } } -// More generic version compared to `VersionedSiblingKiltProviderVerifier`, to -// be used in cases in which it is not possible or not desirable to depend on -// the whole provider runtime definition. Hence, required types must be filled -// in manually. +/// Generic proof verifier for KILT-specific DIP identity proofs of different +/// versions coming from a sibling provider running one of the available KILT +/// runtimes. +/// +/// It expects the DIP proof to be a [`VersionedSiblingParachainDipStateProof`], +/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully +/// verified. +/// +/// For more info, refer to the version-specific proof identifiers. pub struct GenericVersionedDipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, @@ -325,7 +375,7 @@ impl< SiblingProviderParachainId: Get, - SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, @@ -412,7 +462,7 @@ pub mod latest { pub use super::v0::SiblingParachainDipStateProof; } -mod v0 { +pub mod v0 { use super::*; use frame_support::Parameter; @@ -423,9 +473,13 @@ mod v0 { export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, merkle::DidMerkleProofVerifier, state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, - traits::ProviderParachainStateInfo, + traits::ProviderParachainStorageInfo, }; + /// The expected format of a cross-chain DIP identity proof when the + /// identity information is bridged from a provider that is a sibling + /// of the chain where the information is consumed (i.e., consumer + /// chain). #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] pub struct SiblingParachainDipStateProof< RelayBlockHeight, @@ -433,8 +487,11 @@ mod v0 { DipMerkleProofRevealedLeaf, LocalBlockNumber, > { + /// The state proof for the given parachain head. pub(crate) para_state_root: ParachainRootStateProof, + /// The raw state proof for the DIP commitment of the given subject. pub(crate) dip_identity_commitment: BoundedBlindedValue, + /// The cross-chain DID signature. pub(crate) did: DipMerkleProofAndDidSignature, } @@ -463,6 +520,73 @@ mod v0 { } } + /// Generic proof verifier for KILT-specific DIP identity proofs coming from + /// a sibling provider running one of the available KILT runtimes. + /// + /// The proof verification step is performed on every request, and this + /// specific verifier has no knowledge of caching or storing state about the + /// subject. It only takes the provided + /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is + /// successfully verified, to prevent replay attacks. If additional logic is + /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a + /// different verifier or a wrapper around this verifier must be built. + /// + /// It expects the DIP proof to be a + /// [`VersionedSiblingParachainDipStateProof`], and returns + /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. + /// This information is then made availabe as an origin to the downstream + /// call dispatched. + /// + /// The verifier performs the following steps: + /// 1. Verifies the state proof about the state root of the relaychain block + /// at the provided height. The state root is provided by the + /// `RelayChainInfo` type. + /// 2. Verifies the state proof about the DIP commitment value on the + /// provider parachain at the block finalized at the given relaychain + /// block, using the relay state root validated in the previous step. + /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID + /// Document against the retrieved DIP commitment validated in the + /// previous step. + /// 4. Verifies the cross-chain DID signature over the payload composed by + /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: + /// * `C`: The `RuntimeCall` to dispatch after performing DIP + /// verification. + /// * `D`: The local details associated to the DID subject as stored in + /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. + /// * `S`: The tx submitter's address. + /// * `B`: The block number of the consumer chain provided in the + /// cross-chain DID signature. + /// * `G`: The genesis hash of the consumer chain. + /// * `E`: Any additional information provided by the + /// `LocalContextProvider` implementation. + /// The generic types + /// indicate the following: + /// * `RelayChainInfo`: The type providing information about the relaychain. + /// * `SiblingProviderParachainId`: The parachain ID of the provider KILT + /// sibling parachain. + /// * `SiblingProviderStateInfo`: The type providing storage and state + /// information about the provider KILT sibling parachain. + /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT + /// parachain for the generation of the DIP identity commitment. + /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the + /// KILT child parachain. + /// * `ProviderAccountId`: The runtime type of an account ID as defined by + /// the KILT child parachain. + /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the + /// KILT child parachain. + /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as + /// defined by the KILT child parachain. + /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier + /// will accept revealed as part of the DIP identity proof. + /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the + /// verifier will accept revealed as part of the DIP identity proof. + /// * `LocalContextProvider`: The type providing context of the consumer + /// chain (e.g., current block number) for the sake of cross-chain DID + /// signature verification. + /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID + /// key relationship. This information is used once the Merkle proof is + /// verified, to filter only the revealed keys that match the provided + /// relationship. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct DipSiblingProviderStateProofVerifier< RelayChainStateInfo, @@ -532,7 +656,7 @@ mod v0 { SiblingProviderParachainId: Get, - SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, @@ -612,7 +736,7 @@ mod v0 { let proof_leaves: RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, - ::BlockNumber, + ::BlockNumber, ProviderWeb3Name, ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 9d7a415616..66ec155cdc 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -16,7 +16,13 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Crate documentation +//! Collection of support traits, types, and functions for integrating KILT as +//! an identity provider following the Decentralized Identity Provider (DIP) +//! protocol. +//! +//! Consumers of KILT identities should prefer directly using +//! [`KiltVersionedChildProviderVerifier`] for consumer relaychains and +//! [`KiltVersionedSiblingProviderVerifier`] for consumer sibling parachains. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 68e30e9917..ebe2826c8d 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -16,6 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Module to deal with cross-chain Merkle proof as generated by the KILT chain. + use did::{ did_details::{DidPublicKeyDetails, DidVerificationKey}, DidVerificationKeyRelationship, @@ -28,6 +30,7 @@ use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; +/// Type of a Merkle proof containing DID-related information. #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, Default, TypeInfo)] pub struct DidMerkleProof { pub blinded: BlindedValues, @@ -49,6 +52,7 @@ where } } +/// Relationship of a key to a DID Document. #[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] pub enum DidKeyRelationship { Encryption, @@ -73,6 +77,7 @@ impl TryFrom for DidVerificationKeyRelationship { } } +/// The key of a Merkle leaf revealing a DID key for a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct DidKeyMerkleKey(pub KeyId, pub DidKeyRelationship); @@ -81,7 +86,7 @@ impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey { Self(value.0, value.1) } } - +/// The value of a Merkle leaf revealing a DID key for a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct DidKeyMerkleValue(pub DidPublicKeyDetails); @@ -93,6 +98,7 @@ impl From> } } +/// The key of a Merkle leaf revealing the web3name linked to a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct Web3NameMerkleKey(pub Web3Name); @@ -101,6 +107,7 @@ impl From for Web3NameMerkleKey { Self(value) } } +/// The value of a Merkle leaf revealing the web3name linked to a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct Web3NameMerkleValue(pub BlockNumber); @@ -110,6 +117,7 @@ impl From for Web3NameMerkleValue { } } +/// The key of a Merkle leaf revealing an account linked to a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct LinkedAccountMerkleKey(pub AccountId); @@ -118,7 +126,8 @@ impl From for LinkedAccountMerkleKey { Self(value) } } - +/// The value of a Merkle leaf revealing an account linked to a DID +/// Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct LinkedAccountMerkleValue; @@ -128,10 +137,10 @@ impl From<()> for LinkedAccountMerkleValue { } } +/// All possible Merkle leaf types that can be revealed as part of a DIP +/// identity Merkle proof. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub enum RevealedDidMerkleProofLeaf { - // The key and value for the leaves of a merkle proof that contain a reference - // (by ID) to the key details, provided in a separate leaf. DidKey(DidKeyMerkleKey, DidKeyMerkleValue), Web3Name(Web3NameMerkleKey, Web3NameMerkleValue), LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), @@ -187,19 +196,32 @@ where } } +/// The details of a DID key after it has been successfully verified in a Merkle +/// proof. #[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct RevealedDidKey { + /// The key ID, according to the provider's definition. pub id: KeyId, + /// The key relationship to the subject's DID Document. pub relationship: DidKeyRelationship, + /// The details of the DID Key, including its creation block number on the + /// provider chain. pub details: DidPublicKeyDetails, } +/// The details of a web3name after it has been successfully verified in a +/// Merkle proof. #[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct RevealedWeb3Name { + /// The web3name. pub web3_name: Web3Name, + /// The block number on the provider chain in which it was linked to the DID + /// subject. pub claimed_at: BlockNumber, } +/// The complete set of information that is provided by the DIP Merkle proof +/// verifier upon successful verification of a DIP Merkle proof. #[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, DefaultNoBound)] pub struct RevealedDidMerkleProofLeaves< KeyId, @@ -210,8 +232,13 @@ pub struct RevealedDidMerkleProofLeaves< const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, > { + /// The list of [`RevealedDidKey`]s revealed in the Merkle proof, up to a + /// maximum of `MAX_REVEALED_KEYS_COUNT`. pub did_keys: BoundedVec, ConstU32>, + /// The optional [`RevealedWeb3Name`] revealed in the Merkle proof. pub web3_name: Option>, + /// The list of linked accounts revealed in the Merkle proof, up to a + /// maximum of `MAX_REVEALED_ACCOUNTS_COUNT`. pub linked_accounts: BoundedVec>, } @@ -255,9 +282,30 @@ impl From for u8 { } } -/// A type that verifies a Merkle proof that reveals some leaves representing -/// keys in a DID Document. -/// Can also be used on its own, without any DID signature verification. +/// A type that verifies a DIP Merkle proof revealing some leaves representing +/// parts of a KILT DID identity stored on the KILT chain. +/// If cross-chain DID signatures are not required for the specific use case, +/// this verifier can also be used on its own, without any DID signature +/// verification. +/// The Merkle proof is assumed to have been generated using one of the +/// versioned identity commitment generators, as shown in the [KILT runtime +/// definitions](../../../runtimes/common/src/dip/README.md). +/// The generic types are the following: +/// * `Hasher`: The hasher used by the producer to hash the Merkle leaves and +/// produce the identity commitment. +/// * `KeyId`: The type of a DID key ID according to the producer's definition. +/// * `AccountId`: The type of an account ID according to the producer's +/// definition. +/// * `BlockNumber`: The type of a block number according to the producer's +/// definition. +/// * `Web3Name`: The type of a web3names according to the producer's +/// definition. +/// * `LinkedAccountId`: The type of a DID-linked account ID according to the +/// producer's definition. +/// * `MAX_REVEALED_KEYS_COUNT`: The maximum number of DID keys that are +/// supported when verifying the Merkle proof. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The maximum number of linked accounts that +/// are supported when verifying the Merkle proof. pub(crate) struct DidMerkleProofVerifier< Hasher, KeyId, @@ -297,7 +345,6 @@ impl< Web3Name: Encode + Clone, { #[cfg(not(feature = "runtime-benchmarks"))] - #[allow(clippy::type_complexity)] pub(crate) fn verify_dip_merkle_proof( identity_commitment: &Hasher::Out, proof: DidMerkleProof< diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 019161c27f..58619eb6bb 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -16,6 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Module to deal with cross-chain state proofs. + use parity_scale_codec::{Decode, Encode, HasCompact}; use sp_core::{storage::StorageKey, RuntimeDebug, U256}; use sp_runtime::generic::Header; @@ -85,6 +87,7 @@ mod substrate_no_std_port { } } +/// Relaychain-related state proof logic. pub(super) mod relay_chain { use super::*; @@ -111,6 +114,12 @@ pub(super) mod relay_chain { } } + /// Verifier of state proofs that reveal the value of a parachain head at a + /// given relaychain block number. + /// The generic types are the following: + /// * `RelayChainState`: defines the relaychain runtime types relevant for + /// state proof verification, and returns the relaychain runtime's storage + /// key identifying a parachain with a given ID. pub struct ParachainHeadProofVerifier(PhantomData); // Uses the provided `root` to verify the proof. @@ -121,6 +130,8 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { + /// Given a relaychain state root, verify a state proof for the + /// parachain with the provided ID. #[cfg(not(feature = "runtime-benchmarks"))] pub fn verify_proof_for_parachain_with_root( para_id: &RelayChainState::ParaId, @@ -187,6 +198,9 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { + /// Given a relaychain state root provided by the `RelayChainState` + /// generic type, verify a state proof for the parachain with the + /// provided ID. #[cfg(not(feature = "runtime-benchmarks"))] pub fn verify_proof_for_parachain( para_id: &RelayChainState::ParaId, @@ -209,6 +223,12 @@ pub(super) mod relay_chain { } } + /// Implementor of the [`RelayChainStorageInfo`] trait that return the state + /// root of a relaychain block with a given number by retrieving it from the + /// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the + /// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the + /// ones used by Polkadot-based relaychains. This type cannot be used with + /// relaychains that adopt a different definition for any on those types. pub struct RelayStateRootsViaRelayStorePallet(PhantomData); impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet @@ -308,10 +328,11 @@ pub(super) mod relay_chain { } } +/// Parachain-related state proof logic. pub(super) mod parachain { use super::*; - use crate::traits::ProviderParachainStateInfo; + use crate::traits::ProviderParachainStorageInfo; #[derive(RuntimeDebug)] pub enum DipIdentityCommitmentProofVerifierError { @@ -330,15 +351,24 @@ pub(super) mod parachain { } } + /// Verifier of state proofs that reveal the value of the DIP commitment for + /// a given subject on the provider chain. The generic types indicate the + /// following: + /// * `ParaInfo`: defines the provider parachain runtime types relevant for + /// state proof verification, and returns the provider's runtime storage + /// key identifying the identity commitment for a subject with the given + /// identifier. pub struct DipIdentityCommitmentProofVerifier(PhantomData); impl DipIdentityCommitmentProofVerifier where - ParaInfo: ProviderParachainStateInfo, + ParaInfo: ProviderParachainStorageInfo, OutputOf: Ord, ParaInfo::Commitment: Decode, ParaInfo::Key: AsRef<[u8]>, { + /// Given a parachain state root, verify a state proof for the + /// commitment of a given subject identifier. #[cfg(not(feature = "runtime-benchmarks"))] #[allow(clippy::result_unit_err)] pub fn verify_proof_for_identifier( @@ -408,7 +438,7 @@ pub(super) mod parachain { struct StaticSpiritnetInfoProvider; // We use the `system::eventCount()` storage entry as a unit test here. - impl ProviderParachainStateInfo for StaticSpiritnetInfoProvider { + impl ProviderParachainStorageInfo for StaticSpiritnetInfoProvider { type BlockNumber = u32; // The type of the `eventCount()` storage entry. type Commitment = u32; diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index aa426aae17..7d0967c797 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -45,6 +45,8 @@ where /// A trait for types that implement some sort of access control logic on the /// provided input `Call` type. +/// The generic types are the following: +/// * `Call`: The type of the call being checked. pub trait DipCallOriginFilter { /// The error type for cases where the checks fail. type Error; @@ -54,35 +56,62 @@ pub trait DipCallOriginFilter { /// The success type for cases where the checks succeed. type Success; + /// Check whether the provided call can be dispatch with the given origin + /// information. fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } +/// A trait that provides context (e.g., runtime type definitions, storage keys) +/// about the relaychain that is relevant for cross-chain state proofs. pub trait RelayChainStorageInfo { + /// The type of relaychain block numbers. type BlockNumber; + /// The type of the relaychain hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// The type of the relaychain storage key. type Key; + /// The type of parachain IDs. type ParaId; + /// Return the storage key pointing to the head of the parachain + /// identified by the provided ID. fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key; } +/// A trait that provides state information about specific relaychain blocks. pub trait RelayChainStateInfo: RelayChainStorageInfo { + /// Return the relaychain state root at a given block height. fn state_root_for_block(block_height: &Self::BlockNumber) -> Option>; } -pub trait ProviderParachainStateInfo { +/// A trait that provides context (e.g., runtime type definitions, storage keys) +/// about the DIP provider parachain that is relevant for cross-chain state +/// proofs. +pub trait ProviderParachainStorageInfo { + /// The type of the provider chain's block numbers. type BlockNumber; + /// The type of the provider chain's identity commitments. type Commitment; + /// The type of the provider chain's storage keys. type Key; + /// The type of the provider chain's hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// The type of the provider chain's identity subject identifiers. type Identifier; + /// Return the storage key pointing to the identity commitment for the given + /// identifier and version. fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key; } +/// Implementation of the [`ProviderParachainStorageInfo`] trait that builds on +/// the definitions of a runtime that includes the DIP provider pallet (e.g., +/// KILT runtimes). +/// The generic types are the following: +/// * `T`: The runtime including the [`pallet_dip_provider::Pallet`] pallet. pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); -impl ProviderParachainStateInfo for ProviderParachainStateInfoViaProviderPallet +impl ProviderParachainStorageInfo for ProviderParachainStateInfoViaProviderPallet where T: pallet_dip_provider::Config, { @@ -99,18 +128,37 @@ where } } +/// A trait that provides the consumer parachain runtime additional context to +/// verify cross-chain DID signatures by subjects of the provider parachain. pub trait DidSignatureVerifierContext { + /// Max number of blocks a cross-chain DID signature can have to be + /// considered fresh. const SIGNATURE_VALIDITY: u16; + /// The type of consumer parachain's block numbers. type BlockNumber; + /// The type of consumer parachain's hashes. type Hash; + /// Additional information that must be included in the payload being + /// DID-signed by the subject. type SignedExtra; - fn block_number() -> Self::BlockNumber; + /// Returns the block number of the consumer's chain in which the DID + /// signature is being evaluated. + fn current_block_number() -> Self::BlockNumber; + /// Returns the genesis hash of the consumer's chain. fn genesis_hash() -> Self::Hash; + /// Returns any additional info that must be appended to the payload before + /// verifying a cross-chain DID signature. fn signed_extra() -> Self::SignedExtra; } +/// Implementation of the [`DidSignatureVerifierContext`] trait that draws +/// information dynamically from the consumer's runtime using its system pallet. +/// The generic types are the following: +/// * `T`: The runtime including the [`frame_system::Pallet`] pallet. +/// * `SIGNATURE_VALIDITY`: The max number of blocks DID signatures can have to +/// be considered valid. pub struct FrameSystemDidSignatureContext(PhantomData); impl DidSignatureVerifierContext @@ -124,7 +172,7 @@ where type Hash = T::Hash; type SignedExtra = (); - fn block_number() -> Self::BlockNumber { + fn current_block_number() -> Self::BlockNumber { frame_system::Pallet::::block_number() } @@ -135,10 +183,14 @@ where fn signed_extra() -> Self::SignedExtra {} } +/// A trait that provides access to information on historical blocks. pub trait HistoricalBlockRegistry { + /// The runtime definition of block numbers. type BlockNumber; + /// The runtime hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// Retrieve a block hash given its number. fn block_hash_for(block: &Self::BlockNumber) -> Option>; } diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs index 825826ce74..184ea12e8a 100644 --- a/crates/kilt-dip-support/src/utils.rs +++ b/crates/kilt-dip-support/src/utils.rs @@ -21,8 +21,11 @@ use scale_info::TypeInfo; use sp_core::RuntimeDebug; use sp_std::vec::Vec; +/// The output of a type implementing the [`sp_runtime::traits::Hash`] trait. pub type OutputOf = ::Output; +/// The vector of vectors that implements a statically-configured maximum length +/// without requiring const generics, used in benchmarking worst cases. #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] pub struct BoundedBlindedValue(Vec>); diff --git a/dip-template/nodes/dip-consumer/Cargo.toml b/dip-template/nodes/dip-consumer/Cargo.toml index 1f5435f436..76fcdebdca 100644 --- a/dip-template/nodes/dip-consumer/Cargo.toml +++ b/dip-template/nodes/dip-consumer/Cargo.toml @@ -70,6 +70,7 @@ cumulus-relay-chain-interface.workspace = true substrate-build-script-utils.workspace = true [features] +default = [] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "dip-consumer-runtime-template/runtime-benchmarks", diff --git a/dip-template/nodes/dip-consumer/src/command.rs b/dip-template/nodes/dip-consumer/src/command.rs index 36898a0190..f41f3c337a 100644 --- a/dip-template/nodes/dip-consumer/src/command.rs +++ b/dip-template/nodes/dip-consumer/src/command.rs @@ -35,15 +35,17 @@ use sc_telemetry::TelemetryEndpoints; use sp_runtime::traits::AccountIdConversion; use crate::{ - chain_spec::{development_config, Extensions}, + chain_spec::{development_config, ChainSpec as ConsumerChainSpec, Extensions}, cli::{Cli, RelayChainCli, Subcommand}, service::{new_partial, start_parachain_node}, }; fn load_spec(id: &str) -> std::result::Result, String> { match id { - "dev" => Ok(Box::new(development_config())), - _ => Err("Unrecognized spec ID.".into()), + "dev" | "" => Ok(Box::new(development_config())), + path => Ok(Box::new(ConsumerChainSpec::from_json_file(std::path::PathBuf::from( + path, + ))?)), } } diff --git a/dip-template/nodes/dip-provider/Cargo.toml b/dip-template/nodes/dip-provider/Cargo.toml index 584bca26ee..6ca76df480 100644 --- a/dip-template/nodes/dip-provider/Cargo.toml +++ b/dip-template/nodes/dip-provider/Cargo.toml @@ -70,6 +70,7 @@ cumulus-relay-chain-interface.workspace = true substrate-build-script-utils.workspace = true [features] +default = [] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "dip-provider-runtime-template/runtime-benchmarks" diff --git a/dip-template/nodes/dip-provider/src/command.rs b/dip-template/nodes/dip-provider/src/command.rs index 976c311898..3fb8b8322b 100644 --- a/dip-template/nodes/dip-provider/src/command.rs +++ b/dip-template/nodes/dip-provider/src/command.rs @@ -35,15 +35,17 @@ use sc_telemetry::TelemetryEndpoints; use sp_runtime::traits::AccountIdConversion; use crate::{ - chain_spec::{development_config, Extensions}, + chain_spec::{development_config, ChainSpec as ProviderChainSpec, Extensions}, cli::{Cli, RelayChainCli, Subcommand}, service::{new_partial, start_parachain_node}, }; fn load_spec(id: &str) -> std::result::Result, String> { match id { - "dev" => Ok(Box::new(development_config())), - _ => Err("Unrecognized spec ID.".into()), + "dev" | "" => Ok(Box::new(development_config())), + path => Ok(Box::new(ProviderChainSpec::from_json_file(std::path::PathBuf::from( + path, + ))?)), } } diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 03ef8efd18..823fcd37f3 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -30,6 +30,10 @@ use sp_runtime::traits::BlakeTwo256; use crate::{weights, AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; pub type MerkleProofVerifierOutput = >::VerificationResult; +/// The verifier logic assumes the provider is a sibling KILT parachain, and +/// that a KILT subject can provide DIP proof that reveal at most 10 DID keys +/// and 10 linked accounts. Calls that do not pass the [`DipCallFilter`] will be +/// discarded early on in the verification process. pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< ProviderRuntime, ConstU32<2_000>, @@ -43,8 +47,14 @@ pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; + // Any signed origin can submit a cross-chain DIP tx, since subject + // authentication (and optional binding to the tx submitter) is performed in the + // DIP proof verification step. type DispatchOriginCheck = EnsureSigned; type Identifier = DidIdentifier; + // Local identity info contains a simple `u128` representing a nonce. This means + // that two cross-chain operations targeting the same chain and with the same + // nonce cannot be both successfully evaluated. type LocalIdentityInfo = u128; type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; @@ -52,6 +62,8 @@ impl pallet_dip_consumer::Config for Runtime { type WeightInfo = weights::pallet_dip_consumer::WeightInfo; } +/// A preliminary DID call filter that only allows dispatching of extrinsics +/// from the [`pallet_postit::Pallet`] pallet. pub struct PreliminaryDipOriginFilter; impl Contains for PreliminaryDipOriginFilter { @@ -72,6 +84,9 @@ impl Contains for PreliminaryDipOriginFilter { } } +/// Calls to the [`pallet_postit::Pallet`] pallet or batches containing only +/// calls to the [`pallet_postit::Pallet`] pallet will go through if authorized +/// by a DID's authentication key. Everything else will fail. fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { RuntimeCall::PostIt { .. } => Some(DidVerificationKeyRelationship::Authentication), @@ -104,11 +119,18 @@ fn single_key_relationship<'a>( }) } +/// Errors generated by calls that do not pass the filter. pub enum DipCallFilterError { + /// The call cannot be dispatched with the provided origin. BadOrigin, + /// The call could be dispatched with the provided origin, but it has been + /// authorized with the wrong DID key. WrongVerificationRelationship, } +/// A call filter that requires calls to the [`pallet_postit::Pallet`] pallet to +/// be authorized with a DID signature generated with a key of a given +/// verification relationship. pub struct DipCallFilter; impl DipCallOriginFilter for DipCallFilter { @@ -129,6 +151,8 @@ impl DipCallOriginFilter for DipCallFilter { } impl pallet_relay_store::Config for Runtime { + // The pallet stores the last 100 relaychain state roots, making state proofs + // valid for at most 100 * 6 = 600 seconds. type MaxRelayBlocksStored = ConstU32<100>; type WeightInfo = weights::pallet_relay_store::WeightInfo; } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index daf21c15fa..06e031d2d5 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -16,6 +16,17 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Runtime template of a Decentralized Identity Provider (DIP) consumer, which +//! does not itself include any identity-related pallets, but only the +//! [`pallet_dip_consumer::Pallet`] pallet (configured to work with the +//! [`dip_provider_runtime_template::Runtime`] template runtime), the +//! [`pallet_relay_store::Pallet`] pallet to keep track of finalized relaychain +//! state roots, and the example [`pallet_postit::Pallet`], which allows any +//! entity that can be identified with a username (e.g., a web3name carried over +//! from the provider chain) to post a message on chain, reply to another +//! on-chain message (including another reply), or like a message and/or any of +//! its replies. + #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index ebde676e7a..5e0319f62e 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -24,6 +24,11 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; +/// An origin adapter which is used to make sure that a given [`DipOrigin`] +/// contains, among other things, a web3name. If a pallet extrinsic that +/// requires this origin is called with a DIP proof that does not revealed the +/// web3name linked to the subject, the extrinsic will fail with a `BadOrigin` +/// error. pub struct EnsureDipOriginAdapter; impl EnsureOrigin for EnsureDipOriginAdapter { @@ -39,6 +44,8 @@ impl EnsureOrigin for EnsureDipOriginAdapter { } } +/// A wrapper around a [`DipOrigin`] that makes sure the origin has a web3name, +/// or else the origin is invalid. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOriginAdapter(DipOrigin); diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 66f8ee88ec..f6cd7abb2e 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -39,12 +39,21 @@ const MAX_LINKED_ACCOUNTS: u32 = 20; pub mod runtime_api { use super::*; + /// Parameters for a DIP proof request. #[derive(Encode, Decode, TypeInfo)] pub struct DipProofRequest { + /// The subject identifier for which to generate the DIP proof. pub(crate) identifier: DidIdentifier, + /// The DIP version. pub(crate) version: IdentityCommitmentVersion, + /// The DID key IDs of the subject's DID Document to reveal in the DIP + /// proof. pub(crate) keys: Vec>, + /// The list of accounts linked to the subject's DID to reveal in the + /// DIP proof. pub(crate) accounts: Vec, + /// A flag indicating whether the web3name claimed by the DID subject + /// should revealed in the DIP proof. pub(crate) should_include_web3_name: bool, } @@ -73,6 +82,8 @@ pub mod deposit { DipProvider, } + /// The namespace to use in the [`pallet_deposit_storage::Pallet`] to store + /// all deposits related to DIP commitments. pub struct DipProviderDepositNamespace; impl Get for DipProviderDepositNamespace { @@ -81,8 +92,11 @@ pub mod deposit { } } + /// The amount of tokens locked for each identity commitment. pub const DEPOSIT_AMOUNT: Balance = 2 * UNIT; + /// The additional logic to execute whenever a deposit is removed by its + /// owner directly via the [`pallet_deposit_storage::Pallet`] pallet. pub type DepositCollectorHooks = FixedDepositCollectorViaDepositsPallet>; @@ -100,6 +114,11 @@ pub mod deposit { } } + /// The logic to execute whenever an identity commitment is generated and + /// stored in the [`pallet_dip_provider::Pallet`] pallet. + /// + /// Upon storing and removing identity commitments, this hook will reserve + /// or release deposits from the [`pallet_deposit_storage::Pallet`] pallet. pub struct DepositHooks; impl DepositStorageHooks for DepositHooks { @@ -172,7 +191,10 @@ pub mod deposit { impl pallet_deposit_storage::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type BenchmarkHooks = deposit::PalletDepositStorageBenchmarkHooks; + // Any signed origin can submit the tx, which will go through only if the + // deposit payer matches the signed origin. type CheckOrigin = EnsureSigned; + // The balances pallet is used to reserve/unreserve tokens. type Currency = Balances; type DepositHooks = DepositHooks; type MaxKeyLength = ConstU32<256>; @@ -183,10 +205,16 @@ impl pallet_deposit_storage::Config for Runtime { } impl pallet_dip_provider::Config for Runtime { + // Only DID origins can submit the commitment identity tx, which will go through + // only if the DID in the origin matches the identifier specified in the tx. type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; + // The identity commitment is defined as the Merkle root of the linked identity + // info, as specified by the [`LinkedDidInfoProvider`]. type IdentityCommitmentGenerator = DidMerkleRootGenerator; + // Identity info is defined as the collection of DID keys, linked accounts, and + // the optional web3name of a given DID subject. type IdentityProvider = LinkedDidInfoProvider; type ProviderHooks = deposit::DepositCollectorHooks; type RuntimeEvent = RuntimeEvent; diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 90e96c9a94..8a083b92b9 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -16,6 +16,12 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Runtime template of a Decentralized Identity Provider (DIP) provider, which +//! includes, beyond system pallets, [`did::Pallet`], +//! [`pallet_web3_names::Pallet`], and [`pallet_did_lookup::Pallet`] pallets, as +//! well as the [`pallet_dip_provider::Pallet`] pallet and the +//! [`pallet_deposit_storage::Pallet`] pallet. + #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index c01296e23e..36b902df5b 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -32,12 +32,18 @@ use sp_std::marker::PhantomData; use crate::{BalanceOf, Config, Error, HoldReason, Pallet}; +/// Details associated to an on-chain deposit. #[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)] pub struct DepositEntry { + /// The [`Deposit`] entry. pub(crate) deposit: Deposit, + /// The `Reason` for the deposit. pub(crate) reason: Reason, } +/// Type implementing the [`DipProviderHooks`] hooks trait by taking a deposit +/// whenever an identity commitment is stored, and releasing the deposit +/// whenever an identity commitment is removed. pub struct FixedDepositCollectorViaDepositsPallet( PhantomData<(DepositsNamespace, FixedDepositAmount)>, ); diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs index eeca637f53..0043f9eedf 100644 --- a/pallets/pallet-deposit-storage/src/lib.rs +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -16,8 +16,14 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Pallet to store namespaced deposits for the configured `Currency`. It allows +//! the original payer of a deposit to claim it back, triggering a hook to +//! optionally perform related actions somewhere else in the runtime. +//! Each deposit is identified by a namespace and a key. There cannot be two +//! equal keys under the same namespace, but the same key can be present under +//! different namespaces. + #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "256"] mod default_weights; mod deposit; @@ -67,20 +73,30 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The maximum length of keys. #[pallet::constant] type MaxKeyLength: Get; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHooks: crate::traits::BenchmarkHooks; + /// The origin check, returning an `AccountId` upon completion, for who + /// can reclaim a deposit. type CheckOrigin: EnsureOrigin; + /// The currency from which deposits are to be taken. type Currency: Mutate; + /// Additional logic to execute whenever a new deposit a created or a + /// deposit is released. type DepositHooks: DepositStorageHooks; + /// The type of a deposit namespace. type Namespace: Parameter + MaxEncodedLen; + /// The aggregated `Event` type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The aggregated `HoldReason` type. type RuntimeHoldReason: From + Clone + PartialEq + Debug + FullCodec + MaxEncodedLen + TypeInfo; type WeightInfo: WeightInfo; } + /// The hold reasons for deposits taken by the pallet. #[pallet::composite_enum] pub enum HoldReason { Deposit, @@ -88,28 +104,45 @@ pub mod pallet { #[pallet::error] pub enum Error { + /// The deposit with the provided key was not found within the specified + /// namespace. DepositNotFound, + /// A deposit with the provided key already exists within the specified + /// namespace. DepositExisting, + /// The origin was not authorized to perform the operation on the + /// specified deposit entry. Unauthorized, + /// The external hook failed. Hook(u16), } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// A new deposit has been reserved and stored. DepositAdded { + /// The deposit namespace. namespace: T::Namespace, + /// The deposit key. key: DepositKeyOf, + /// The deposit details. deposit_entry: DepositEntryOf, }, + /// A deposit has been released and deleted from storage. DepositReclaimed { + /// The deposit namespace. namespace: T::Namespace, + /// The deposit key. key: DepositKeyOf, + /// The deposit details. deposit_entry: DepositEntryOf, }, } - // Double map (namespace, key) -> deposit + /// Storage of all deposits. Its first key is a namespace, and the second + /// one the deposit key. Its value includes the details associated to a + /// deposit instance. #[pallet::storage] #[pallet::getter(fn deposits)] pub(crate) type Deposits = @@ -121,6 +154,10 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Reclaim a deposit that was previously taken. If there is no deposit + /// with the given key under the given namespace, it returns an error. + /// If a deposit exists, the deposit hooks are invoked after the deposit + /// has been removed from the pallet storage. #[pallet::call_index(0)] #[pallet::weight({ ::WeightInfo::reclaim_deposit() @@ -135,6 +172,10 @@ pub mod pallet { } impl Pallet { + /// Add a deposit identified by the given key under the given namespace. + /// If there is already a deposit entry for the same key under the same + /// namespace, it returns an error. It also returns an error if the + /// deposit cannot be reserved on the pallet's `Currency`. pub fn add_deposit(namespace: T::Namespace, key: DepositKeyOf, entry: DepositEntryOf) -> DispatchResult { Deposits::::try_mutate(&namespace, &key, |deposit_entry| match deposit_entry { Some(_) => Err(DispatchError::from(Error::::DepositExisting)), @@ -156,6 +197,11 @@ pub mod pallet { Ok(()) } + /// Remove and release a deposit identified by the given key under the + /// given namespace. If there is no deposit with under the provided + /// namespace with the provided key, it returns an error. It also + /// returns an error if the deposit cannot be released on the pallet's + /// `Currency`. pub fn remove_deposit( namespace: &T::Namespace, key: &DepositKeyOf, diff --git a/pallets/pallet-deposit-storage/src/traits.rs b/pallets/pallet-deposit-storage/src/traits.rs index b4e179937a..fb0270e764 100644 --- a/pallets/pallet-deposit-storage/src/traits.rs +++ b/pallets/pallet-deposit-storage/src/traits.rs @@ -18,12 +18,16 @@ use crate::{Config, DepositEntryOf, DepositKeyOf}; +/// A trait to configure additional custom logic whenever a deposit-related +/// operation takes place. pub trait DepositStorageHooks where Runtime: Config, { type Error: Into; + /// Called by the pallet whenever a deposit for a given namespace and key is + /// removed. fn on_deposit_reclaimed( namespace: &Runtime::Namespace, key: &DepositKeyOf, @@ -31,6 +35,7 @@ where ) -> Result<(), Self::Error>; } +/// Dummy implementation of the [`DepositStorageHooks`] trait that does a noop. pub struct NoopDepositStorageHooks; impl DepositStorageHooks for NoopDepositStorageHooks diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index 5217db0458..f19f17ab72 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-dip-consumer" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-consumer/README.md b/pallets/pallet-dip-consumer/README.md new file mode 100644 index 0000000000..e19a811d07 --- /dev/null +++ b/pallets/pallet-dip-consumer/README.md @@ -0,0 +1,60 @@ +# Decentralized Identity Provider (DIP) provider consumer pallet + +This pallet is a core component of the Decentralized Identity Provider protocol. +It enables entities with an identity on a connected Substrate-based chain (provider) to use those identities on the chain this pallet is deployed (consumers) without requiring those entities to set up a new identity locally. +A consumer chain is *connected* to a provider if there is a way for the consumer chain to verify state proofs about parts of the state of the provider chain. + +A cross-chain transaction with DIP assumes the entity submitting the transaction has already generated a cross-chain identity commitment on the provider chain, by interacting with the DIP provider pallet on the provider chain. +With a generated identity commitment, a cross-chain transaction flow for a generic entity `A` works as follows: + +1. `A` generates a state proof proving the state of the identity commitment on the provider chain. +2. `A` generates any additional information required for an identity proof to be successfully verified by the consumer runtime. +3. `A`, using their account `AccC` on the consumer chain, calls the `dispatch_as` extrinsic by providing its identifier on the provider chain, the generated proof, and the `Call` to be dispatched on the consumer chain. + 1. This pallet verifies if the proof is correct, if not it returns an error. + 2. This pallet dispatches the provided `Call` with a new origin created by this pallet, returning any errors the dispatch action returns. The origin contains the information revealed in the proof, the identifier of the acting subject and the account `AccC` dispatching the transaction. + +The pallet is agnostic over the chain-specific definition of *identity proof verifier* and *identifier*, although, when deployed, they must be configured to respect the definition of identity and identity commitment established by the provider this pallet is linked to. + +For instance, if the provider establishes that an identity commitment is a Merkle root of a set of public keys, an identity proof for the consumer will most likely be a Merkle proof revealing a subset of those keys. +Similarly, if the provider defines an identity commitment as some ZK-commitment, the respective identity proof on the consumer chain will be a ZK-proof verifying the validity of the commitment and therefore of the revealed information. + +For identifiers, if the provider establishes that an identifier is a public key, the same definition must be used in the consumer pallet. +Other definitions for an identifier, such as a simple integer or a [Decentralized Identifier (DID)](https://www.w3.org/TR/did-core/), must also be configured in the same way. + +The pallet allows the consumer runtime to define some `LocalIdentityInfo` associated with each identifier, which the pallet's proof verifier can access and optionally modify upon proof verification. +Any changes made to the `LocalIdentityInfo` will be persisted if the identity proof is verified correctly and the extrinsic executed successfully. + +If the consumer does not need to store anything in addition to the information an identity proof conveys, they can use an empty tuple `()` for the local identity info. +Another example could be the use of signatures, which requires a nonce to avoid replay protections. +In this case, a numeric type such as a `u64` or a `u128` could be used, and increased by the proof verifier when validating each new cross-chain transaction proof. + +## The `Config` trait + +Being chain-agnostic, most of the runtime configurations must be passed to the pallet's `Config` trait. +Nevertheless, most of the types provided must reflect the definition of identity and identity commitment that the identity provider chain has established. +The trait has the following components: + +* `type DipCallOriginFilter: Contains>`: A preliminary filter that checks whether a provided `Call` accepts a DIP origin or not. If a call such as a system call does not accept a DIP origin, there is no need to verify the identity proof, hence the execution can bail out early. This does not guarantee that the dispatch call will succeed, but rather than it will mostly not fail with a `BadOrigin` error. +* `type DispatchOriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::AccountId>`: The origin check on the `dispatch_as` extrinsic to verify that the caller is authorized to call the extrinsic. If successful, the check must return a `AccountId` as defined by the consumer runtime. +* `type Identifier: Parameter + MaxEncodedLen`: The type of a subject identifier. This must match the definition of `Identifier` the identity provider has defined in their deployment of the provider pallet. +* `type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen`: Any additional information that must be available only to the provider runtime that is required to provide additional context when verifying a cross-chain identity proof. +* `type ProofVerifier: IdentityProofVerifier`: The core component of this pallet. It takes care of validating an identity proof and optionally update any `LocalIdentityInfo`. It also defines, via its associated type, the structure of the identity proof that must be passed to the `dispatch_as` extrinsic. Although not directly, the proof structure depends on the information that goes into the identity commitment on the provider chain, as that defines what information can be revealed as part of the commitment proof. Additional info to satisfy requirements according to the `LocalIdentityInfo` (e.g., a signature) must also be provided in the proof. +* `type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>`: The aggregated `Call` type. +* `type RuntimeOrigin: From> + From<::RuntimeOrigin>`: The aggregated `Origin` type, which must include the origin exposed by this pallet. + +## Storage + +The pallet contains a single storage element, the `IdentityEntries` map. +It maps from a subject `Identifier` to an instance of `LocalIdentityInfo`. + +This information is updated by the proof verifier whenever a new cross-chain transaction and its proof is submitted. + +## Origin + +Because the pallet allows other `Call`s to be dispatched after an identity proof has been verified, it also exposes a `Origin` that can be used for those calls that require indeed a call to be DIP-authorized. + +The origin is created after the identity proof has been successfully verified by the proof verifier, and it includes the identifier of the subject, the address of the tx submitter, and the result returned by the proof verifier upon successful verification. + +## Calls (bullet numbers represent each call's encoded index) + +0. `pub fn dispatch_as(origin: OriginFor, identifier: T::Identifier, proof: IdentityProofOf, call: Box>) -> DispatchResult`: Try to dispatch a new local call only if it passes all the DIP requirements. Specifically, the call will be dispatched if it passes the preliminary `DipCallOriginFilter` and if the proof verifier returns an `Ok(verification_result)` value. The value is then added to the `DipOrigin` and passed down as the origin for the specified `Call`. If the whole execution terminates successfully, any changes applied to the `LocalIdentityInfo` by the proof verifier are persisted to the pallet storage. diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs deleted file mode 100644 index eed39f68d4..0000000000 --- a/pallets/pallet-dip-consumer/src/identity.rs +++ /dev/null @@ -1,43 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -/// The identity entry for any given user that uses the DIP protocol. -#[derive(Encode, Decode, MaxEncodedLen, Default, TypeInfo, RuntimeDebug)] -pub struct IdentityDetails { - /// The identity digest information, typically used to verify identity - /// proofs. - pub digest: Digest, - /// The details related to the user, stored in the pallet storage. - pub details: Details, -} - -impl From for IdentityDetails -where - Details: Default, -{ - fn from(value: Digest) -> Self { - Self { - digest: value, - details: Details::default(), - } - } -} diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index ccdd1e0883..148eb88b82 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -16,14 +16,13 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description - #![cfg_attr(not(feature = "std"), no_std)] +#![doc = include_str!("../README.md")] -mod default_weights; -pub mod identity; pub mod traits; +mod default_weights; + #[cfg(test)] pub mod mock; @@ -57,34 +56,58 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - #[pallet::storage] - #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityEntries = - StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; - #[pallet::config] pub trait Config: frame_system::Config { - /// Preliminary filter to filter out calls before doing any heavier - /// computations. + /// A preliminary filter that checks whether a provided `Call` accepts a + /// DIP origin or not. If a call such as a system call does not accept a + /// DIP origin, there is no need to verify the identity proof, hence the + /// execution can bail out early. This does not guarantee that the + /// dispatch call will succeed, but rather than it will mostly not fail + /// with a `BadOrigin` error. type DipCallOriginFilter: Contains>; - /// The origin check for the `dispatch_as` call. + /// The origin check on the `dispatch_as` extrinsic to verify that the + /// caller is authorized to call the extrinsic. If successful, the check + /// must return a `AccountId` as defined by the consumer runtime. type DispatchOriginCheck: EnsureOriginWithArg< ::RuntimeOrigin, Self::Identifier, Success = Self::AccountId, >; - /// The identifier of a subject, e.g., a DID. + /// The type of a subject identifier. This must match the definition of + /// `Identifier` the identity provider has defined in their deployment + /// of the provider pallet. type Identifier: Parameter + MaxEncodedLen; - /// The details stored in this pallet associated with any given subject. + /// Any additional information that must be available only to the + /// provider runtime that is required to provide additional context when + /// verifying a cross-chain identity proof. type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; - /// The logic of the proof verifier, called upon each execution of the - /// `dispatch_as` extrinsic. + /// The core component of this pallet. It takes care of validating an + /// identity proof and optionally update any `LocalIdentityInfo`. It + /// also defines, via its associated type, the structure of the identity + /// proof that must be passed to the `dispatch_as` extrinsic. Although + /// not directly, the proof structure depends on the information that + /// goes into the identity commitment on the provider chain, as that + /// defines what information can be revealed as part of the commitment + /// proof. Additional info to satisfy requirements according to the + /// `LocalIdentityInfo` (e.g., a signature) must also be provided in the + /// proof. type ProofVerifier: IdentityProofVerifier; + /// The aggregated `Call` type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin> + GetDispatchInfo; + /// The aggregated `Origin` type, which must include the origin exposed + /// by this pallet. type RuntimeOrigin: From> + From<::RuntimeOrigin>; type WeightInfo: WeightInfo; } + /// The pallet contains a single storage element, the `IdentityEntries` map. + /// It maps from a subject `Identifier` to an instance of + /// `LocalIdentityInfo`. + #[pallet::storage] + #[pallet::getter(fn identity_proofs)] + pub(crate) type IdentityEntries = + StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; + #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -97,15 +120,24 @@ pub mod pallet { Filtered, } - /// The origin this pallet creates after a user has provided a valid - /// identity proof to dispatch other calls. + /// The origin is created after the identity proof has been successfully + /// verified by the proof verifier, and it includes the identifier of the + /// subject, the address of the tx submitter, and the result returned by the + /// proof verifier upon successful verification. #[pallet::origin] pub type Origin = DipOrigin<::Identifier, ::AccountId, VerificationResultOf>; #[pallet::call] impl Pallet { - // TODO: Replace with a SignedExtra. + /// Try to dispatch a new local call only if it passes all the DIP + /// requirements. Specifically, the call will be dispatched if it passes + /// the preliminary `DipCallOriginFilter` and if the proof verifier + /// returns a `Ok(verification_result)` value. The value is then added + /// to the `DipOrigin` and passed down as the origin for the specified + /// `Call`. If the whole execution terminates successfully, any changes + /// applied to the `LocalIdentityInfo` by the proof verifier are + /// persisted to the pallet storage. #[pallet::call_index(0)] #[pallet::weight({ let extrinsic_weight = ::WeightInfo::dispatch_as(); diff --git a/pallets/pallet-dip-consumer/src/origin.rs b/pallets/pallet-dip-consumer/src/origin.rs index 415d055e0f..5281b25914 100644 --- a/pallets/pallet-dip-consumer/src/origin.rs +++ b/pallets/pallet-dip-consumer/src/origin.rs @@ -22,13 +22,21 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_std::marker::PhantomData; +/// An origin passed down to the to-be-dispatched `Call` upon successful DIP +/// proof verification. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOrigin { + /// The subject identifier which is performing the DIP operation. pub identifier: Identifier, + /// The local account address of the tx submitter. pub account_address: AccountId, + /// Details returned by the proof verifier upon successful proof + /// verification. pub details: Details, } +/// Implementation of the `EnsureOrigin` trait verifying that a given origin is +/// a `DipOrigin`. pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId, Details)>); #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 9213166288..bfc0bedee5 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -20,14 +20,25 @@ use frame_support::Parameter; use crate::{Config, RuntimeCallOf}; +/// A trait to verify a given DIP identity proof. The trait depends on the +/// runtime definition of the consumer pallet's `Identifier` and of the system +/// pallet's `AccountId`. The type of proof expected and the type returned upon +/// successful verification is defined as an associated type. pub trait IdentityProofVerifier where Runtime: Config, { + /// The error returned upon failed DIP proof verification. type Error: Into; + /// The accepted type for a DIP identity proof. type Proof: Parameter; + /// The type returned upon successful DIP proof verification. type VerificationResult; + /// Verify a given DIP proof given the calling context, including the call + /// being dispatched, the DIP subject dispatching it, the account submitting + /// the DIP tx, and the identity details of the DIP subject as stored in the + /// consumer pallet. fn verify_proof_for_call_against_details( call: &RuntimeCallOf, subject: &Runtime::Identifier, @@ -37,7 +48,8 @@ where ) -> Result; } -// Always returns success. +/// Dummy implementation of the [`IdentityProofVerifier`] trait which always +/// returns `Ok(())`. pub struct SuccessfulProofVerifier; impl IdentityProofVerifier for SuccessfulProofVerifier where diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 6c3c0942d3..6e284e0d1c 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-dip-provider" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-provider/README.md b/pallets/pallet-dip-provider/README.md new file mode 100644 index 0000000000..0b384009e0 --- /dev/null +++ b/pallets/pallet-dip-provider/README.md @@ -0,0 +1,50 @@ +# Decentralized Identity Provider (DIP) provider pallet + +This pallet is a core component of the Decentralized Identity Provider protocol. +It enables a Substrate-based chain (provider) to bridge the identities of its users to other connected chains (consumers) trustlessly. +A consumer chain is *connected* to a provider if there is a way for the consumer chain to verify state proofs about parts of the state of the provider chain. + +The pallet is agnostic over the chain-specific definition of *identity*, and delegates the definition of it to the provider chain's runtime. + +What the pallet stores are *identity commitments*, which are opaque byte blobs put in the pallet storage and on which the cross-chain identity bridging protocol can be built. +As for identities, the definition of an identity commitment must be provided by the runtime and is therefore provider-specific. +Naturally, this definition must be made available to consumers willing to integrate the identities living on the provider chain. + +Because providers and consumers evolve at different speeds, identity commitments are versioned. +This allows the provider chain to upgrade to a newer commitment scheme, while still giving its users the possibility to use the old version, if the chains on which they want to use their identity does not yet support the new scheme. + +Identity commitments can be replaced (e.g., if something in the identity info changes), or removed altogether by the identity subject. +After removal, the identity becomes unusable cross-chain, although it will still continue to exist on the provider chain and will be usable for local operations. + +## The `Config` trait + +Being chain-agnostic, most of the runtime configurations must be passed to the pallet's `Config` trait. Specifically: + +* `type CommitOriginCheck: EnsureOrigin`: The check ensuring a given runtime origin is allowed to generate and remove identity commitments. +* `type CommitOrigin: SubmitterInfo`: The resulting origin if `CommitOriginCheck` returns with errors. The origin is not required to be an `AccountId`, but must include information about the `AccountId` of the tx submitter. +* `type Identifier: Parameter + MaxEncodedLen`: The type of an identifier used to retrieve identity information about a subject. +* `type IdentityCommitmentGenerator: IdentityCommitmentGenerator`: The type responsible for generating identity commitments, given the identity information associated to a given `Identifier`. +* `type IdentityProvider: IdentityProvider`: The type responsible for retrieving the information associated to a subject given their identifier. The information can potentially be retrieved from any source, using a combination of on-chain and off-chain solutions. +* `type IdentityProvider: IdentityProvider`: Customizable external logic to handle events in which a new identity commitment is generated or removed. +* `type RuntimeEvent: From> + IsType<::RuntimeEvent>`: The aggregate `Event` type. + +## Storage + +The pallet contains a single storage element, the `IdentityCommitments` double map. +Its first key is the `Identifier` of subjects, while the second key is the commitment version. +The values are identity commitments. + +As mentioned above, a double map allows the same subject to have one commitment for each version supported by the provider, without forcing consumers to upgrade to a new version to support the latest commitment scheme. + +## Events + +The pallet generates two events: a `VersionedIdentityCommitted` and a `VersionedIdentityDeleted`. + +The `VersionedIdentityCommited` is called whenever a new commitment is stored, and contains information about the `Identifier` of the subject, the value of the commitment, and the commitment version. + +Similarly, the `VersionedIdentityDeleted`, is called whenever a commitment is deleted, and contains information about the `Identifier` of the subject and the version of the commitment deleted. + +## Calls (bullet numbers represent each call's encoded index) + +0. `pub fn commit_identity(origin: OriginFor, identifier: T::Identifier, version: Option ) -> DispatchResult`: Generate a new versioned commitment for the subject identified by the provided `Identifier`. If an old commitment for the same version is present, it is overridden. Hooks are called before the new commitment is stored, and optionally before the old one is replaced. +1. `pub fn delete_identity_commitment(origin: OriginFor, identifier: T::Identifier, version: Option) -> DispatchResult`: Delete an identity commitment of a specific version for a specific `Identifier`. If a commitment of the provided version does not exist for the given `Identifier`, an error is returned. Hooks are called after the commitment has been removed. diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index a3672eded9..e6eea5e5a8 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -16,9 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description - #![cfg_attr(not(feature = "std"), no_std)] +#![doc = include_str!("../README.md")] mod default_weights; pub mod traits; @@ -56,16 +55,36 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The check ensuring a given runtime origin is allowed to generate and + /// remove identity commitments. type CommitOriginCheck: EnsureOriginWithArg; + /// The resulting origin if `CommitOriginCheck` returns with errors. The + /// origin is not required to be an `AccountId`, but must include + /// information about the `AccountId` of the tx submitter. type CommitOrigin: SubmitterInfo; + /// The type of an identifier used to retrieve identity information + /// about a subject. type Identifier: Parameter + MaxEncodedLen; + /// The type responsible for generating identity commitments, given the + /// identity information associated to a given `Identifier`. type IdentityCommitmentGenerator: IdentityCommitmentGenerator; + /// The type responsible for retrieving the information associated to a + /// subject given their identifier. The information can potentially be + /// retrieved from any source, using a combination of on-chain and + /// off-chain solutions. type IdentityProvider: IdentityProvider; + /// Customizable external logic to handle events in which a new identity + /// commitment is generated or removed. type ProviderHooks: ProviderHooks; + /// The aggregate `Event` type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; type WeightInfo: WeightInfo; } + /// The pallet contains a single storage element, the `IdentityCommitments` + /// double map. Its first key is the `Identifier` of subjects, while the + /// second key is the commitment version. The values are identity + /// commitments. #[pallet::storage] #[pallet::getter(fn identity_commitments)] pub type IdentityCommitments = StorageDoubleMap< @@ -84,27 +103,42 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// A new commitment has been stored. VersionedIdentityCommitted { + /// The identifier of the identity committed. identifier: T::Identifier, + /// The value of the commitment. commitment: IdentityCommitmentOf, + /// The version of the commitment. version: IdentityCommitmentVersion, }, + /// A commitment has been deleted. VersionedIdentityDeleted { + /// The identifier of the identity committed. identifier: T::Identifier, + /// The version of the commitment. version: IdentityCommitmentVersion, }, } #[pallet::error] pub enum Error { + /// The specified commitment cannot be found. CommitmentNotFound, + /// Error when retrieving the identity details of the provided subject. IdentityProvider(u16), + /// Error when generating a commitment for the retrieved identity. IdentityCommitmentGenerator(u16), + /// Error inside the external hook logic. Hook(u16), } #[pallet::call] impl Pallet { + /// Generate a new versioned commitment for the subject identified by + /// the provided `Identifier`. If an old commitment for the same version + /// is present, it is overridden. Hooks are called before the new + /// commitment is stored, and optionally before the old one is replaced. #[pallet::call_index(0)] #[pallet::weight({ ::WeightInfo::commit_identity() @@ -151,6 +185,10 @@ pub mod pallet { Ok(()) } + /// Delete an identity commitment of a specific version for a specific + /// `Identifier`. If a commitment of the provided version does not exist + /// for the given `Identifier`, an error is returned. Hooks are called + /// after the commitment has been removed. #[pallet::call_index(1)] #[pallet::weight({ ::WeightInfo::delete_identity_commitment() diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index a475c0c9ed..760ad5cc46 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -27,6 +27,9 @@ pub mod identity_provision { use sp_std::marker::PhantomData; + /// A trait to retrieve identity information for a given identifier. The + /// information can come from a variety of different sources, as this pallet + /// does not impose any restrictions on that. pub trait IdentityProvider where Runtime: Config, @@ -34,10 +37,13 @@ pub mod identity_provision { type Error: Into; type Success; + /// Return the identity information for the identifier, if found. + /// Otherwise, return an error. fn retrieve(identifier: &Runtime::Identifier) -> Result; } - // Return the `Default` value if `Identity` and `Details` both implement it. + /// Return the `Default` value of the provided `Identity` type if it + /// implements the `Default` trait. pub struct DefaultIdentityProvider(PhantomData); impl IdentityProvider for DefaultIdentityProvider @@ -64,6 +70,8 @@ pub mod identity_generation { use scale_info::TypeInfo; use sp_std::{fmt::Debug, marker::PhantomData}; + /// A trait to generate an identity commitment of a given version for some + /// identity info retrieved by the [`IdentityProvider`]. pub trait IdentityCommitmentGenerator where Runtime: Config, @@ -72,6 +80,8 @@ pub mod identity_generation { type Error: Into; type Output: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; + /// Return the identity commitment for the given version and identity + /// information. fn generate_commitment( identifier: &Runtime::Identifier, identity: &IdentityOf, @@ -79,8 +89,8 @@ pub mod identity_generation { ) -> Result; } - // Implement the `IdentityCommitmentGenerator` by returning the `Default` value - // for the `Output` type. + /// Implement the [`IdentityCommitmentGenerator`] trait by returning the + /// `Default` value for the `Output` type. pub struct DefaultIdentityCommitmentGenerator(PhantomData); impl IdentityCommitmentGenerator for DefaultIdentityCommitmentGenerator @@ -101,6 +111,8 @@ pub mod identity_generation { } } +/// A trait for types that, among other things, contain information about the +/// submitter of a tx. pub trait SubmitterInfo { type Submitter; @@ -137,6 +149,8 @@ where } } +/// Hooks for additional customizable logic to be executed when new identity +/// commitments are stored or old ones are removed. pub trait ProviderHooks where Runtime: Config, @@ -158,6 +172,7 @@ where ) -> Result<(), Self::Error>; } +/// Implement the [`ProviderHooks`] trait with noops. pub struct NoopHooks; impl ProviderHooks for NoopHooks diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml index 96f1051b04..6cec553a96 100644 --- a/pallets/pallet-relay-store/Cargo.toml +++ b/pallets/pallet-relay-store/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-relay-store" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs index bebe071c46..084f88232a 100644 --- a/pallets/pallet-relay-store/src/lib.rs +++ b/pallets/pallet-relay-store/src/lib.rs @@ -16,7 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description +//! Pallet to store the last N (configurable) relay chain state roots to be used +//! for cross-chain state proof verification. The pallet relies on the +//! cumulus_parachain_system hook to populate the block `ValidationData` with +//! the latest relay chain state root. #![cfg_attr(not(feature = "std"), no_std)] @@ -43,18 +46,24 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + /// Maps from a relaychain block height to its related information, + /// including the state root. #[pallet::storage] #[pallet::getter(fn latest_relay_head_for_block)] pub(crate) type LatestRelayHeads = StorageMap<_, Twox64Concat, u32, RelayParentInfo>; // TODO: Replace this with a fixed-length array once support for const generics // is fully supported in Substrate. + /// Storage value complimentary to [`LatestRelayHeads`] implementing a FIFO + /// queue of the last N relay chain blocks info. #[pallet::storage] pub(crate) type LatestBlockHeights = StorageValue<_, BoundedVec, ValueQuery>; #[pallet::config] pub trait Config: frame_system::Config { + /// The maximum number of relaychain block details to store. When the + /// limit is reached, oldest blocks are overridden with new ones. #[pallet::constant] type MaxRelayBlocksStored: Get; type WeightInfo: WeightInfo; diff --git a/pallets/pallet-relay-store/src/relay.rs b/pallets/pallet-relay-store/src/relay.rs index 9dfaf69f95..1bca00ba53 100644 --- a/pallets/pallet-relay-store/src/relay.rs +++ b/pallets/pallet-relay-store/src/relay.rs @@ -20,7 +20,9 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; +/// Information associated to a relaychain block. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug, MaxEncodedLen)] pub struct RelayParentInfo { + /// The relaychain block storage root. pub relay_parent_storage_root: Hash, } diff --git a/runtime-api/dip-provider/src/lib.rs b/runtime-api/dip-provider/src/lib.rs index b910f734c1..1b7b8170da 100644 --- a/runtime-api/dip-provider/src/lib.rs +++ b/runtime-api/dip-provider/src/lib.rs @@ -21,11 +21,13 @@ use parity_scale_codec::Codec; sp_api::decl_runtime_apis! { + /// Runtime API to generate a DIP proof with the provided parameters. pub trait DipProvider where ProofRequest: Codec, Success: Codec, Error: Codec, { + /// Generate a DIP proof with the parameters specified in the request. fn generate_proof(request: ProofRequest) -> Result; } } diff --git a/runtimes/common/src/dip/README.md b/runtimes/common/src/dip/README.md new file mode 100644 index 0000000000..47664afb0d --- /dev/null +++ b/runtimes/common/src/dip/README.md @@ -0,0 +1,16 @@ +# KILT Decentralized Identity Provider (DIP) provider specification + +Specification of the format of a DIP identity commitment and the expected format of a DIP identity proof for cross-chain transactions using KILT identities. + +## V0 + +The V0 of the KILT DIP Provider specification defines the following components: + +* **Identity details**: What are the pieces of a KILT identity that can be used for cross-chain transactions. V0 defines them to include the following information: + * All `DidKey`s stored under the subject's DID Document. For more details about how these keys are defined, read the [KILT DID pallet](../../../../pallets/did). + * All the `LinkableAccountId`s the DID subject has linked to the DID via the KILT linking pallet. For more details about how on-chain linking works, read the [KILT lookup pallet](../../../../pallets/pallet-did-lookup/). + * (OPTIONAL) The web3name of the DID subject, if present. For more details about how web3names work, read the [KILT web3name pallet](../../../../pallets/pallet-web3-names/). +* **Identity commitment**: Defines how the identity details above are aggregated into a value which will be selectively shared on a consumer chain for a cross-chain transaction. V0 defines the identity commitment as a Merkle root of all the elements above that uses the shame hashing algorithm as the runtime. Using a Merkle root allows the DID subject to generate proof that can selectively disclose different pieces of identity for different operations on different chains providing, among other things, better scalability for cases in which the linked information becomes large. The leaves encoded in the commitment can be of the following type: + * DID key leaf: with leaf name being the key ID, and leaf value being the key details as defined in the `DidPublicKeyDetails` type. + * Linked account leaf: with leaf name being the linked account ID, and leaf value being an empty tuple `()`. + * Web3name leaf: with leaf name being the web3name, and leaf value being the KILT block number in which it was linked to the DID. diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 4b6ca4d0e3..6490684fec 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -53,15 +53,24 @@ impl From for u16 { pub type Web3OwnershipOf = RevealedWeb3Name<::Web3Name, BlockNumberFor>; +/// Identity information related to a KILT DID relevant for cross-chain +/// transactions via the DIP protocol. pub struct LinkedDidInfoOf where Runtime: did::Config + pallet_web3_names::Config, { + /// The DID Document of the subject. pub did_details: DidDetails, + /// The optional web3name details linked to the subject. pub web3_name_details: Option>, + /// The list of accounts the subject has previously linked via the linking + /// pallet. pub linked_accounts: BoundedVec>, } +/// Type implementing the [`IdentityProvider`] trait which is responsible for +/// collecting the DID information relevant for DIP cross-chain transactions by +/// interacting with the different pallets involved. pub struct LinkedDidInfoProvider; impl IdentityProvider for LinkedDidInfoProvider diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 5eb5829a8d..15e833a8f9 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -34,6 +34,8 @@ use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::dip::did::LinkedDidInfoOf; pub type BlindedValue = Vec; +/// Type of the Merkle proof revealing parts of the DIP identity of a given DID +/// subject. pub type DidMerkleProofOf = DidMerkleProof< Vec, RevealedDidMerkleProofLeaf< @@ -45,9 +47,13 @@ pub type DidMerkleProofOf = DidMerkleProof< >, >; +/// Type of a complete DIP Merkle proof. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { + /// The Merkle root. pub root: Root, + /// The Merkle proof revealing parts of the commitment that verify against + /// the provided root. pub proof: Proof, } @@ -75,6 +81,7 @@ impl From for u16 { pub mod v0 { use super::*; + /// Type of a Merkle leaf revealed as part of a DIP Merkle proof. type ProofLeafOf = RevealedDidMerkleProofLeaf< KeyIdOf, ::AccountId, @@ -83,6 +90,8 @@ pub mod v0 { LinkableAccountId, >; + /// Given the provided DID info, it calculates the Merkle commitment (root) + /// using the provided in-memory DB. pub(super) fn calculate_root_with_db( identity: &LinkedDidInfoOf, db: &mut MemoryDB, @@ -232,6 +241,11 @@ pub mod v0 { Ok(trie_builder.root().to_owned()) } + /// Given the provided DID info, and a set of DID key IDs, account IDs and a + /// web3name, generates a Merkle proof that reveals only the provided + /// identity components. The function fails if no key or account with the + /// specified ID can be found, or if a web3name is requested to be revealed + /// in the proof but is not present in the provided identity details. pub(super) fn generate_proof<'a, Runtime, K, A, const MAX_LINKED_ACCOUNT: u32>( identity: &LinkedDidInfoOf, key_ids: K, @@ -323,6 +337,7 @@ pub mod v0 { }) } + /// Given the provided DID info, generates a Merkle commitment (root). pub(super) fn generate_commitment( identity: &IdentityOf, ) -> Result @@ -335,6 +350,9 @@ pub mod v0 { } } +/// Type implementing the [`IdentityCommitmentGenerator`] and generating a +/// Merkle root of the provided identity details, according to the description +/// provided in the [README.md](./README.md), pub struct DidMerkleRootGenerator(PhantomData); impl IdentityCommitmentGenerator for DidMerkleRootGenerator diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index a3955297d6..9a578cec07 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -16,5 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +#![doc = include_str!("./README.md")] + +/// Logic for collecting information related to a KILT DID. pub mod did; +/// Logic for generating Merkle commitments of a KILT DID identity. pub mod merkle; From 150953875e55b96c2209e7ab689a92fc9c268bed Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 6 Dec 2023 13:06:43 +0100 Subject: [PATCH 20/28] Fix test error --- crates/kilt-dip-support/src/merkle.rs | 11 ++++------- dip-template/runtimes/dip-provider/src/dip.rs | 5 ++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index ebe2826c8d..3fecc5668e 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -18,14 +18,10 @@ //! Module to deal with cross-chain Merkle proof as generated by the KILT chain. -use did::{ - did_details::{DidPublicKeyDetails, DidVerificationKey}, - DidVerificationKeyRelationship, -}; +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::{traits::ConstU32, DefaultNoBound, RuntimeDebug}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_core::ed25519; use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; @@ -157,7 +153,8 @@ where Self::DidKey( (KeyId::default(), DidVerificationKeyRelationship::Authentication.into()).into(), DidPublicKeyDetails { - key: DidVerificationKey::Ed25519(ed25519::Public::from_raw([0u8; 32])).into(), + key: did::did_details::DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])) + .into(), block_number: BlockNumber::default(), } .into(), @@ -348,7 +345,7 @@ impl< pub(crate) fn verify_dip_merkle_proof( identity_commitment: &Hasher::Out, proof: DidMerkleProof< - Vec>, + crate::BoundedBlindedValue, RevealedDidMerkleProofLeaf, >, ) -> Result< diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index f6cd7abb2e..49764ce340 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -71,8 +71,7 @@ pub mod deposit { use frame_support::traits::Get; use pallet_deposit_storage::{ - traits::{BenchmarkHooks, DepositStorageHooks}, - DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, + traits::DepositStorageHooks, DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, }; use parity_scale_codec::MaxEncodedLen; use sp_core::{ConstU128, RuntimeDebug}; @@ -150,7 +149,7 @@ pub mod deposit { pub struct PalletDepositStorageBenchmarkHooks; #[cfg(feature = "runtime-benchmarks")] - impl BenchmarkHooks for PalletDepositStorageBenchmarkHooks { + impl pallet_deposit_storage::traits::BenchmarkHooks for PalletDepositStorageBenchmarkHooks { fn pre_reclaim_deposit() -> ( ::AccountId, ::Namespace, From 057de4838f3db42a2306104095775c2e28299e42 Mon Sep 17 00:00:00 2001 From: Albrecht Date: Thu, 30 Nov 2023 09:17:43 +0100 Subject: [PATCH 21/28] fix: check events (#586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## fixes https://github.com/KILTprotocol/ticket/issues/3023 ### changed * moved the `deposit_event(Event::AttestationRemoved)` to the `remove_attestation` so that the event shall never be forgotten to be deposited. * restructured Attestation event * added authorized_by to revoked and removed, added ctype * test attestation events * removed unused `DepositReclaimed` events * deposit `W3nReleased` event when governance bans an already used w3n ## Metadata Diff to Develop Branch

Peregrine Diff ``` !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! [≠] pallet 62: Attestation -> 4 change(s) - events changes: [≠] 0: AttestationCreated ( : AttesterOf, : ClaimHashOf, : CtypeHashOf, : Option>, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "attester"))]), Changed(1, [Name(StringChange("", "claim_hash"))]), Changed(2, [Name(StringChange("", "ctype_hash"))]), Changed(3, [Name(StringChange("", "authorization"))])] })] [≠] 1: AttestationRevoked ( : AttesterOf, : ClaimHashOf, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "authorized_by")), Ty(StringChange("AttesterOf", "AuthorizedByOf"))]), Changed(1, [Name(StringChange("", "attester")), Ty(StringChange("ClaimHashOf", "AttesterOf"))]), Added(2, ArgDesc { name: "claim_hash", ty: "ClaimHashOf" })] })] [≠] 2: AttestationRemoved ( : AttesterOf, : ClaimHashOf, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "authorized_by")), Ty(StringChange("AttesterOf", "AuthorizedByOf"))]), Changed(1, [Name(StringChange("", "attester")), Ty(StringChange("ClaimHashOf", "AttesterOf"))]), Added(2, ArgDesc { name: "claim_hash", ty: "ClaimHashOf" })] })] [-] "DepositReclaimed" [≠] pallet 63: Delegation -> 1 change(s) - events changes: [-] "DepositReclaimed" SUMMARY: - Compatible.......................: true - Require transaction_version bump.: false !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! ```
Spiritnet Diff ``` !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! [≠] pallet 62: Attestation -> 4 change(s) - events changes: [≠] 0: AttestationCreated ( : AttesterOf, : ClaimHashOf, : CtypeHashOf, : Option>, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "attester"))]), Changed(1, [Name(StringChange("", "claim_hash"))]), Changed(2, [Name(StringChange("", "ctype_hash"))]), Changed(3, [Name(StringChange("", "authorization"))])] })] [≠] 1: AttestationRevoked ( : AttesterOf, : ClaimHashOf, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "authorized_by")), Ty(StringChange("AttesterOf", "AuthorizedByOf"))]), Changed(1, [Name(StringChange("", "attester")), Ty(StringChange("ClaimHashOf", "AttesterOf"))]), Added(2, ArgDesc { name: "claim_hash", ty: "ClaimHashOf" })] })] [≠] 2: AttestationRemoved ( : AttesterOf, : ClaimHashOf, ) ) [Signature(SignatureChange { args: [Changed(0, [Name(StringChange("", "authorized_by")), Ty(StringChange("AttesterOf", "AuthorizedByOf"))]), Changed(1, [Name(StringChange("", "attester")), Ty(StringChange("ClaimHashOf", "AttesterOf"))]), Added(2, ArgDesc { name: "claim_hash", ty: "ClaimHashOf" })] })] [-] "DepositReclaimed" [≠] pallet 63: Delegation -> 1 change(s) - events changes: [-] "DepositReclaimed" SUMMARY: - Compatible.......................: true - Require transaction_version bump.: false !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! ```
## Checklist: - [x] I have verified that the code works - [ ] No panics! (checked arithmetic ops, no indexing `array[3]` use `get(3)`, ...) - [x] I have verified that the code is easy to understand - [ ] If not, I have left a well-balanced amount of inline comments - [x] I have [left the code in a better state](https://deviq.com/principles/boy-scout-rule) - [x] I have documented the changes (where applicable) * Either PR or Ticket to update [the Docs](https://github.com/KILTprotocol/docs) * Link the PR/Ticket here --- pallets/attestation/src/authorized_by.rs | 34 ++++++ pallets/attestation/src/lib.rs | 144 +++++++++++++---------- pallets/attestation/src/mock.rs | 28 ++++- pallets/attestation/src/tests/claim.rs | 32 +++-- pallets/attestation/src/tests/delete.rs | 74 +++++++++++- pallets/attestation/src/tests/deposit.rs | 83 ++++++++++++- pallets/attestation/src/tests/revoke.rs | 35 +++++- pallets/delegation/src/lib.rs | 5 +- pallets/pallet-web3-names/src/lib.rs | 25 ++-- 9 files changed, 358 insertions(+), 102 deletions(-) create mode 100644 pallets/attestation/src/authorized_by.rs diff --git a/pallets/attestation/src/authorized_by.rs b/pallets/attestation/src/authorized_by.rs new file mode 100644 index 0000000000..25a748641f --- /dev/null +++ b/pallets/attestation/src/authorized_by.rs @@ -0,0 +1,34 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +/// Describes who authorized the associated action. +/// +/// This can either be the attester that issued this attestation, another +/// attester who is authorized by the `authorization_id` or the deposit owner. +#[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub enum AuthorizedBy { + /// Authorized by the deposit owner. + DepositOwner(Account), + /// Authorized by who issued the attestation. + Attester(Attester), + /// Authorized by the authorization_id. + Authorization(Attester), +} diff --git a/pallets/attestation/src/lib.rs b/pallets/attestation/src/lib.rs index 990c93152a..7d57897f74 100644 --- a/pallets/attestation/src/lib.rs +++ b/pallets/attestation/src/lib.rs @@ -75,6 +75,7 @@ pub mod benchmarking; mod try_state; mod access_control; +pub mod authorized_by; #[cfg(test)] mod tests; @@ -86,6 +87,7 @@ pub use crate::{ pub mod pallet { use super::*; + use authorized_by::AuthorizedBy; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, pallet_prelude::*, @@ -124,6 +126,8 @@ pub mod pallet { pub(crate) type BalanceMigrationManagerOf = ::BalanceMigrationManager; + pub(crate) type AuthorizedByOf = authorized_by::AuthorizedBy, AttesterOf>; + pub type AttestationDetailsOf = AttestationDetails, AttesterOf, AuthorizationIdOf, AccountIdOf, BalanceOf>; @@ -198,22 +202,40 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// A new attestation has been created. - /// \[attester ID, claim hash, CType hash, (optional) delegation ID\] - AttestationCreated( - AttesterOf, - ClaimHashOf, - CtypeHashOf, - Option>, - ), + AttestationCreated { + /// The DID which issued this attestation. + attester: AttesterOf, + /// The claim hash of the attested credential. + claim_hash: ClaimHashOf, + /// The ctype of the attested credential. + ctype_hash: CtypeHashOf, + /// The authorization information. If this is available, it + /// authorizes a group of attesters to manage this attestation. + authorization: Option>, + }, /// An attestation has been revoked. - /// \[account id, claim hash\] - AttestationRevoked(AttesterOf, ClaimHashOf), + AttestationRevoked { + /// Who authorized the revocation of the attestation. + authorized_by: AuthorizedByOf, + /// The attester who initially created the attestation. + attester: AttesterOf, + /// The ctype of the attested credential. + ctype_hash: CtypeHashOf, + /// The claim hash of the credential that is revoked. + claim_hash: ClaimHashOf, + }, /// An attestation has been removed. - /// \[account id, claim hash\] - AttestationRemoved(AttesterOf, ClaimHashOf), - /// The deposit owner reclaimed a deposit by removing an attestation. - /// \[account id, claim hash\] - DepositReclaimed(AccountIdOf, ClaimHashOf), + AttestationRemoved { + /// Who authorized the deletion of the attestation. + authorized_by: AuthorizedByOf, + /// The attester who initially created the attestation. + attester: AttesterOf, + /// The ctype of the attested credential. + ctype_hash: CtypeHashOf, + /// The claim hash of the credential for which the attestation entry + /// was deleted. + claim_hash: ClaimHashOf, + }, } #[pallet::error] @@ -251,14 +273,6 @@ pub mod pallet { /// `DelegationEntityId`. /// /// Emits `AttestationCreated`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account], Ctype, Attestations - /// - Reads if delegation id is provided: Delegations, Roots, - /// DelegatedAttestations - /// - Writes: Attestations, (DelegatedAttestations) - /// # #[pallet::call_index(0)] #[pallet::weight( ::WeightInfo::add() @@ -312,7 +326,12 @@ pub mod pallet { ExternalAttestations::::insert(authorization_id, claim_hash, true); } - Self::deposit_event(Event::AttestationCreated(who, claim_hash, ctype_hash, authorization_id)); + Self::deposit_event(Event::AttestationCreated { + attester: who, + claim_hash, + ctype_hash, + authorization: authorization_id, + }); Ok(()) } @@ -325,15 +344,6 @@ pub mod pallet { /// an ancestor thereof. /// /// Emits `AttestationRevoked`. - /// - /// # - /// Weight: O(P) where P is the number of steps required to verify that - /// the dispatch Origin controls the delegation entitled to revoke the - /// attestation. It is bounded by `max_parent_checks`. - /// - Reads: [Origin Account], Attestations, delegation::Roots - /// - Reads per delegation step P: delegation::Delegations - /// - Writes: Attestations, DelegatedAttestations - /// # #[pallet::call_index(1)] #[pallet::weight( ::WeightInfo::revoke() @@ -348,10 +358,11 @@ pub mod pallet { let who = source.subject(); let attestation = Attestations::::get(claim_hash).ok_or(Error::::NotFound)?; + let attester = attestation.attester.clone(); ensure!(!attestation.revoked, Error::::AlreadyRevoked); - if attestation.attester != who { + let authorized_by = if attester != who { let attestation_auth_id = attestation.authorization_id.as_ref().ok_or(Error::::NotAuthorized)?; authorization.ok_or(Error::::NotAuthorized)?.can_revoke( &who, @@ -359,7 +370,11 @@ pub mod pallet { &claim_hash, attestation_auth_id, )?; - } + + AuthorizedBy::Authorization(who) + } else { + AuthorizedBy::Attester(who) + }; log::debug!("revoking Attestation"); Attestations::::insert( @@ -370,7 +385,12 @@ pub mod pallet { }, ); - Self::deposit_event(Event::AttestationRevoked(who, claim_hash)); + Self::deposit_event(Event::AttestationRevoked { + attester, + authorized_by, + ctype_hash: attestation.ctype_hash, + claim_hash, + }); Ok(Some(::WeightInfo::revoke()).into()) } @@ -382,16 +402,8 @@ pub mod pallet { /// i.e., it was either the delegator of the attester or an ancestor /// thereof. /// - /// Emits `AttestationRemoved`. - /// - /// # - /// Weight: O(P) where P is the number of steps required to verify that - /// the dispatch Origin controls the delegation entitled to revoke the - /// attestation. It is bounded by `max_parent_checks`. - /// - Reads: [Origin Account], Attestations, delegation::Roots - /// - Reads per delegation step P: delegation::Delegations - /// - Writes: Attestations, DelegatedAttestations - /// # + /// Always emits `AttestationRemoved` and emits `AttestationRevoked` + /// only if the attestation was not revoked yet. #[pallet::call_index(2)] #[pallet::weight( ::WeightInfo::remove() @@ -407,7 +419,7 @@ pub mod pallet { let attestation = Attestations::::get(claim_hash).ok_or(Error::::NotFound)?; - if attestation.attester != who { + let authorized_by = if attestation.attester != who { let attestation_auth_id = attestation.authorization_id.as_ref().ok_or(Error::::NotAuthorized)?; authorization.ok_or(Error::::NotAuthorized)?.can_remove( &who, @@ -415,25 +427,22 @@ pub mod pallet { &claim_hash, attestation_auth_id, )?; - } + AuthorizedBy::Authorization(who) + } else { + AuthorizedBy::Attester(who) + }; log::debug!("removing Attestation"); - Self::remove_attestation(attestation, claim_hash)?; - Self::deposit_event(Event::AttestationRemoved(who, claim_hash)); + Self::remove_attestation(authorized_by, attestation, claim_hash)?; Ok(Some(::WeightInfo::remove()).into()) } /// Reclaim a storage deposit by removing an attestation /// - /// Emits `DepositReclaimed`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account], Attestations, DelegatedAttestations - /// - Writes: Attestations, DelegatedAttestations - /// # + /// Always emits `AttestationRemoved` and emits `AttestationRevoked` + /// only if the attestation was not revoked yet. #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::reclaim_deposit())] pub fn reclaim_deposit(origin: OriginFor, claim_hash: ClaimHashOf) -> DispatchResult { @@ -444,8 +453,7 @@ pub mod pallet { log::debug!("removing Attestation"); - Self::remove_attestation(attestation, claim_hash)?; - Self::deposit_event(Event::DepositReclaimed(who, claim_hash)); + Self::remove_attestation(AuthorizedBy::DepositOwner(who), attestation, claim_hash)?; Ok(()) } @@ -493,7 +501,11 @@ pub mod pallet { } impl Pallet { - fn remove_attestation(attestation: AttestationDetailsOf, claim_hash: ClaimHashOf) -> DispatchResult { + fn remove_attestation( + authorized_by: AuthorizedByOf, + attestation: AttestationDetailsOf, + claim_hash: ClaimHashOf, + ) -> DispatchResult { let is_key_migrated = ::BalanceMigrationManager::is_key_migrated(&Attestations::::hashed_key_for(claim_hash)); if is_key_migrated { @@ -509,6 +521,20 @@ pub mod pallet { if let Some(authorization_id) = &attestation.authorization_id { ExternalAttestations::::remove(authorization_id, claim_hash); } + if !attestation.revoked { + Self::deposit_event(Event::AttestationRevoked { + attester: attestation.attester.clone(), + authorized_by: authorized_by.clone(), + claim_hash, + ctype_hash: attestation.ctype_hash, + }); + } + Self::deposit_event(Event::AttestationRemoved { + attester: attestation.attester, + authorized_by, + claim_hash, + ctype_hash: attestation.ctype_hash, + }); Ok(()) } } diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index 35f94d5b89..a6c65d56b8 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -192,7 +192,7 @@ pub(crate) mod runtime { use ctype::{CtypeCreatorOf, CtypeEntryOf}; use kilt_support::mock::{mock_origin, SubjectId}; - use crate::{self as attestation}; + use crate::{self as attestation, Event}; type Block = frame_system::mocking::MockBlock; @@ -205,6 +205,20 @@ pub(crate) mod runtime { pub const MILLI_UNIT: Balance = 10u128.pow(12); pub const ATTESTATION_DEPOSIT: Balance = 10 * MILLI_UNIT; + pub(crate) fn events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let RuntimeEvent::Attestation(e) = e { + Some(e) + } else { + None + } + }) + .collect::>() + } + frame_support::construct_runtime!( pub enum Test { @@ -230,7 +244,7 @@ pub(crate) mod runtime { type Lookup = IdentityLookup; type Block = Block; type Nonce = u64; - type RuntimeEvent = (); + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type DbWeight = RocksDbWeight; type Version = (); @@ -263,7 +277,7 @@ pub(crate) mod runtime { type MaxHolds = MaxHolds; type Balance = Balance; type DustRemoval = (); - type RuntimeEvent = (); + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); @@ -281,7 +295,7 @@ pub(crate) mod runtime { type EnsureOrigin = mock_origin::EnsureDoubleOrigin; type OriginSuccess = mock_origin::DoubleOrigin; type OverarchingOrigin = EnsureSigned; - type RuntimeEvent = (); + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type Currency = Balances; @@ -303,7 +317,7 @@ pub(crate) mod runtime { impl Config for Test { type EnsureOrigin = mock_origin::EnsureDoubleOrigin>; type OriginSuccess = mock_origin::DoubleOrigin>; - type RuntimeEvent = (); + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type RuntimeHoldReason = RuntimeHoldReason; type Currency = Balances; @@ -370,6 +384,10 @@ pub(crate) mod runtime { let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { + // ensure that we are not at the genesis block. Events are not registered for + // the genesis block. + System::set_block_number(System::block_number() + 1); + for ctype in self.ctypes { ctype::Ctypes::::insert( ctype.0, diff --git a/pallets/attestation/src/tests/claim.rs b/pallets/attestation/src/tests/claim.rs index b493f8f7e0..47c843e78e 100644 --- a/pallets/attestation/src/tests/claim.rs +++ b/pallets/attestation/src/tests/claim.rs @@ -21,35 +21,44 @@ use frame_support::{assert_noop, assert_ok}; use kilt_support::mock::mock_origin::DoubleOrigin; use sp_runtime::DispatchError; -use crate::{self as attestation, mock::*, AttestationAccessControl, AttesterOf, Config}; +use crate::{self as attestation, mock::*, AttestationAccessControl, AttesterOf, Config, Event}; #[test] fn test_attest_without_authorization() { let attester: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); - let ctype_hash = get_ctype_hash::(true); + let ctype = get_ctype_hash::(true); let authorization_info = None; ExtBuilder::default() - .with_ctypes(vec![(ctype_hash, attester.clone())]) + .with_ctypes(vec![(ctype, attester.clone())]) .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .build_and_execute_with_sanity_tests(|| { assert_ok!(Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), claim_hash, - ctype_hash, + ctype, authorization_info.clone() )); let stored_attestation = Attestation::attestations(claim_hash).expect("Attestation should be present on chain."); - assert_eq!(stored_attestation.ctype_hash, ctype_hash); + assert_eq!(stored_attestation.ctype_hash, ctype); assert_eq!(stored_attestation.attester, attester); assert_eq!( stored_attestation.authorization_id, authorization_info.map(|ac| ac.authorization_id()) ); assert!(!stored_attestation.revoked); + assert_eq!( + events(), + vec![Event::AttestationCreated { + attester: attester.clone(), + claim_hash, + ctype_hash: ctype, + authorization: None + }] + ); }); } @@ -81,6 +90,15 @@ fn test_attest_authorized() { authorization_info.map(|ac| ac.authorization_id()) ); assert!(!stored_attestation.revoked); + assert_eq!( + events(), + vec![Event::AttestationCreated { + attester: attester.clone(), + claim_hash, + ctype_hash: ctype, + authorization: Some(attester) + }] + ); }); } @@ -96,14 +114,14 @@ fn test_attest_unauthorized() { .with_ctypes(vec![(ctype, attester.clone())]) .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .build_and_execute_with_sanity_tests(|| { - assert_eq!( + assert_noop!( Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), claim_hash, ctype, authorization_info ), - Err(DispatchError::Other("Unauthorized")) + DispatchError::Other("Unauthorized") ); }); } diff --git a/pallets/attestation/src/tests/delete.rs b/pallets/attestation/src/tests/delete.rs index 99ae02e8a1..44618989b7 100644 --- a/pallets/attestation/src/tests/delete.rs +++ b/pallets/attestation/src/tests/delete.rs @@ -20,14 +20,14 @@ use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use kilt_support::mock::mock_origin::DoubleOrigin; use sp_runtime::traits::Zero; -use crate::{self as attestation, mock::*, AttesterOf, Config, HoldReason}; +use crate::{self as attestation, mock::*, AttesterOf, Config, Event, HoldReason}; #[test] fn test_remove() { let attester: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - let authorization_info = None; + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) @@ -37,9 +37,57 @@ fn test_remove() { assert_ok!(Attestation::remove( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), claim_hash, - authorization_info + None + )); + assert!(Attestation::attestations(claim_hash).is_none()); + assert_eq!( + events(), + vec![ + Event::AttestationRevoked { + attester: attester.clone(), + claim_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Attester(attester.clone()), + ctype_hash, + }, + Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Attester(attester.clone()), + ctype_hash, + } + ] + ); + }); +} + +#[test] +fn test_remove_revoked() { + let attester: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + attestation.revoked = true; + let ctype_hash = attestation.ctype_hash; + + ExtBuilder::default() + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) + .with_attestations(vec![(claim_hash, attestation)]) + .build_and_execute_with_sanity_tests(|| { + assert_ok!(Attestation::remove( + DoubleOrigin(ACCOUNT_00, attester.clone()).into(), + claim_hash, + None )); assert!(Attestation::attestations(claim_hash).is_none()); + assert_eq!( + events(), + vec![Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Attester(attester.clone()) + }] + ); }); } @@ -48,9 +96,10 @@ fn test_remove_authorized() { let attester: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); let revoker: AttesterOf = sr25519_did_from_public_key(&BOB_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); - let mut attestation = generate_base_attestation::(attester, ACCOUNT_00); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); attestation.authorization_id = Some(revoker.clone()); let authorization_info = Some(MockAccessControl(revoker.clone())); + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) @@ -64,6 +113,23 @@ fn test_remove_authorized() { )); assert!(Attestation::attestations(claim_hash).is_none()); assert!(!Attestation::external_attestations(revoker.clone(), claim_hash)); + assert_eq!( + events(), + vec![ + Event::AttestationRevoked { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Authorization(revoker.clone()) + }, + Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Authorization(revoker.clone()) + } + ] + ); }); } diff --git a/pallets/attestation/src/tests/deposit.rs b/pallets/attestation/src/tests/deposit.rs index 686be0a4c6..78620863f4 100644 --- a/pallets/attestation/src/tests/deposit.rs +++ b/pallets/attestation/src/tests/deposit.rs @@ -20,7 +20,7 @@ use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use kilt_support::{mock::mock_origin::DoubleOrigin, Deposit}; use sp_runtime::{traits::Zero, TokenError}; -use crate::{self as attestation, mock::*, AttesterOf, Config, Error, HoldReason}; +use crate::{self as attestation, mock::*, AttesterOf, Config, Error, Event, HoldReason}; #[test] fn test_reclaim_deposit_not_found() { @@ -210,16 +210,17 @@ fn test_update_deposit_unauthorized() { } #[test] -fn test_reclaim_deposit() { +fn test_reclaim_deposit_authorization() { let attester: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); let other_authorized: AttesterOf = sr25519_did_from_public_key(&BOB_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); attestation.authorization_id = Some(other_authorized.clone()); + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) .with_attestations(vec![(claim_hash, attestation)]) .build_and_execute_with_sanity_tests(|| { assert_eq!( @@ -236,18 +237,36 @@ fn test_reclaim_deposit() { )); assert!(Attestation::attestations(claim_hash).is_none()); assert!(Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00).is_zero()); + assert_eq!( + events(), + vec![ + Event::AttestationRevoked { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::DepositOwner(ACCOUNT_00) + }, + Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::DepositOwner(ACCOUNT_00) + } + ] + ); }); } #[test] -fn test_reclaim_deposit_authorization() { +fn test_reclaim_deposit() { let attester: AttesterOf = sr25519_did_from_public_key(&BOB_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) .with_attestations(vec![(claim_hash, attestation)]) .build_and_execute_with_sanity_tests(|| { assert_eq!( @@ -260,6 +279,60 @@ fn test_reclaim_deposit_authorization() { )); assert!(Attestation::attestations(claim_hash).is_none()); assert!(Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00).is_zero()); + + assert_eq!( + events(), + vec![ + Event::AttestationRevoked { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::DepositOwner(ACCOUNT_00) + }, + Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::DepositOwner(ACCOUNT_00) + } + ] + ); + }); +} + +#[test] +fn test_reclaim_deposit_revoked() { + let attester: AttesterOf = sr25519_did_from_public_key(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + attestation.revoked = true; + let ctype_hash = attestation.ctype_hash; + + ExtBuilder::default() + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) + .with_attestations(vec![(claim_hash, attestation)]) + .build_and_execute_with_sanity_tests(|| { + assert_eq!( + Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00), + ::Deposit::get() + ); + assert_ok!(Attestation::reclaim_deposit( + RuntimeOrigin::signed(ACCOUNT_00), + claim_hash + )); + assert!(Attestation::attestations(claim_hash).is_none()); + assert!(Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00).is_zero()); + + assert_eq!( + events(), + vec![Event::AttestationRemoved { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::DepositOwner(ACCOUNT_00) + }] + ); }); } diff --git a/pallets/attestation/src/tests/revoke.rs b/pallets/attestation/src/tests/revoke.rs index 483e30ea5f..34bf9fefe0 100644 --- a/pallets/attestation/src/tests/revoke.rs +++ b/pallets/attestation/src/tests/revoke.rs @@ -20,13 +20,14 @@ use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use kilt_support::mock::mock_origin::DoubleOrigin; use sp_runtime::{traits::Zero, DispatchError}; -use crate::{self as attestation, mock::*, AttesterOf, Config, HoldReason}; +use crate::{self as attestation, mock::*, AttesterOf, Config, Event, HoldReason}; #[test] fn test_revoke_remove() { let revoker: AttesterOf = sr25519_did_from_public_key(&ALICE_SEED); let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); let attestation = generate_base_attestation::(revoker.clone(), ACCOUNT_00); + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) @@ -46,6 +47,17 @@ fn test_revoke_remove() { Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00), ::Deposit::get() ); + assert_eq!( + events(), + vec![Event::AttestationRevoked { + attester: revoker.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Attester(revoker.clone()) + }] + ); + + System::reset_events(); assert_ok!(Attestation::remove( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), @@ -54,6 +66,15 @@ fn test_revoke_remove() { )); assert!(Attestation::attestations(claim_hash).is_none()); assert!(Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00).is_zero()); + assert_eq!( + events(), + vec![Event::AttestationRemoved { + attester: revoker.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Attester(revoker.clone()) + }] + ); }); } @@ -65,10 +86,11 @@ fn test_authorized_revoke() { let authorization_info = Some(MockAccessControl(revoker.clone())); let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); attestation.authorization_id = Some(revoker.clone()); + let ctype_hash = attestation.ctype_hash; ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) .with_attestations(vec![(claim_hash, attestation)]) .build_and_execute_with_sanity_tests(|| { assert_ok!(Attestation::revoke( @@ -85,6 +107,15 @@ fn test_authorized_revoke() { Balances::balance_on_hold(&HoldReason::Deposit.into(), &ACCOUNT_00), ::Deposit::get() ); + assert_eq!( + events(), + vec![Event::AttestationRevoked { + attester: attester.clone(), + claim_hash, + ctype_hash, + authorized_by: attestation::authorized_by::AuthorizedBy::Authorization(revoker.clone()) + }] + ); }); } diff --git a/pallets/delegation/src/lib.rs b/pallets/delegation/src/lib.rs index 501dd8d282..8b8e78cdf8 100644 --- a/pallets/delegation/src/lib.rs +++ b/pallets/delegation/src/lib.rs @@ -51,7 +51,7 @@ //! //! - **Delegation:**: An attestation which is not issued by the attester //! directly but via a (chain of) delegations which entitle the delegated -//! attester. This could be an employe of a company which is authorized to +//! attester. This could be an employee of a company which is authorized to //! sign documents for their superiors. //! //! ## Assumptions @@ -248,9 +248,6 @@ pub mod pallet { /// A delegation has been removed. /// \[remover ID, delegation node ID\] DelegationRemoved(AccountIdOf, DelegationNodeIdOf), - /// The deposit owner reclaimed a deposit by removing a delegation - /// subtree. \[revoker ID, delegation node ID\] - DepositReclaimed(AccountIdOf, DelegationNodeIdOf), } #[pallet::error] diff --git a/pallets/pallet-web3-names/src/lib.rs b/pallets/pallet-web3-names/src/lib.rs index 68b95304eb..9f609e3a51 100644 --- a/pallets/pallet-web3-names/src/lib.rs +++ b/pallets/pallet-web3-names/src/lib.rs @@ -227,11 +227,7 @@ pub mod pallet { let decoded_name = Self::check_claiming_preconditions(name, &owner, &payer)?; - Self::register_name(decoded_name.clone(), owner.clone(), payer)?; - Self::deposit_event(Event::::Web3NameClaimed { - owner, - name: decoded_name, - }); + Self::register_name(decoded_name, owner, payer)?; Ok(()) } @@ -257,10 +253,6 @@ pub mod pallet { let owned_name = Self::check_releasing_preconditions(&owner)?; Self::unregister_name(&owned_name)?; - Self::deposit_event(Event::::Web3NameReleased { - owner, - name: owned_name, - }); Ok(()) } @@ -284,11 +276,7 @@ pub mod pallet { let decoded_name = Self::check_reclaim_deposit_preconditions(name, &caller)?; - let Web3OwnershipOf:: { owner, .. } = Self::unregister_name(&decoded_name)?; - Self::deposit_event(Event::::Web3NameReleased { - owner, - name: decoded_name, - }); + Self::unregister_name(&decoded_name)?; Ok(()) } @@ -439,11 +427,13 @@ pub mod pallet { Owner::::insert( &name, Web3OwnershipOf:: { - owner, + owner: owner.clone(), claimed_at: block_number, deposit, }, ); + + Self::deposit_event(Event::::Web3NameClaimed { owner, name }); Ok(()) } @@ -493,7 +483,10 @@ pub mod pallet { ) } - // Should never fail since we checked in the preconditions + Self::deposit_event(Event::::Web3NameReleased { + owner: name_ownership.owner.clone(), + name: name.clone(), + }); Ok(name_ownership) } From f8a9c9ecb1d4edb0c0b9a2d2f9c67b66a2700ff8 Mon Sep 17 00:00:00 2001 From: Albrecht Date: Wed, 6 Dec 2023 09:59:33 +0100 Subject: [PATCH 22/28] chore: unrelated changes from the DIP PR (#589) Unrelated changes from the DIP PR. * fix formatting * sort alphabetically * implement Ord, Eq, Default for AsciiW3n ## Metadata Diff to Develop Branch
Peregrine Diff ``` !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! No change detected SUMMARY: - Compatible.......................: true - Require transaction_version bump.: false !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! ```
Spiritnet Diff ``` !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! No change detected SUMMARY: - Compatible.......................: true - Require transaction_version bump.: false !!! THE SUBWASM REDUCED DIFFER IS EXPERIMENTAL, DOUBLE CHECK THE RESULTS !!! ```
## Checklist: - [x] I have verified that the code works - [ ] No panics! (checked arithmetic ops, no indexing `array[3]` use `get(3)`, ...) - [x] I have verified that the code is easy to understand - [ ] If not, I have left a well-balanced amount of inline comments - [x] I have [left the code in a better state](https://deviq.com/principles/boy-scout-rule) - [x] I have documented the changes (where applicable) * Either PR or Ticket to update [the Docs](https://github.com/KILTprotocol/docs) * Link the PR/Ticket here --- pallets/pallet-web3-names/src/web3_name.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pallets/pallet-web3-names/src/web3_name.rs b/pallets/pallet-web3-names/src/web3_name.rs index dd05b1809c..2d67c2ffa4 100644 --- a/pallets/pallet-web3-names/src/web3_name.rs +++ b/pallets/pallet-web3-names/src/web3_name.rs @@ -78,42 +78,48 @@ fn is_valid_web3_name(input: &[u8]) -> bool { .all(|c| matches!(c, b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_')) } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement PartialEq. impl PartialEq for AsciiWeb3Name { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement Eq. impl Eq for AsciiWeb3Name { fn assert_receiver_is_total_eq(&self) { self.0.assert_receiver_is_total_eq() } } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement PartialOrd. impl PartialOrd for AsciiWeb3Name { fn partial_cmp(&self, other: &Self) -> Option { self.0.as_slice().partial_cmp(other.as_slice()) } } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement Ord. impl Ord for AsciiWeb3Name { fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { self.0.cmp(&other.0) } } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement Clone. impl Clone for AsciiWeb3Name { fn clone(&self) -> Self { Self(self.0.clone(), self.1) } } -// FIXME: did not find a way to automatically implement this. +// FIXME: did not find a way to automatically implement this. Runtime would need +// to implement Default. impl Default for AsciiWeb3Name { fn default() -> Self { Self(BoundedVec::default(), PhantomData) From cfa6d86fe168d5b63906cc610ced7c572df9444c Mon Sep 17 00:00:00 2001 From: Albrecht Date: Mon, 11 Dec 2023 12:55:59 +0100 Subject: [PATCH 23/28] refactor: dip (#591) Co-authored-by: Antonio Antonino --- Cargo.lock | 1 + crates/kilt-dip-support/Cargo.toml | 1 + crates/kilt-dip-support/src/did.rs | 241 ++++++----------- crates/kilt-dip-support/src/export/child.rs | 39 +-- crates/kilt-dip-support/src/export/sibling.rs | 38 +-- crates/kilt-dip-support/src/merkle.rs | 250 ++++++------------ crates/kilt-dip-support/src/state_proofs.rs | 21 +- runtimes/common/src/dip/did.rs | 98 ++++--- 8 files changed, 259 insertions(+), 430 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 741ea0e370..0d0eeaafa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4558,6 +4558,7 @@ dependencies = [ name = "kilt-dip-support" version = "1.12.0-dev" dependencies = [ + "cfg-if", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "did", diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index bbebdfa4f2..30262359b2 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -14,6 +14,7 @@ version.workspace = true # External dependencies hash-db.workspace = true log.workspace = true +cfg-if = "1.0" # Internal dependencies did.workspace = true diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index dfdce46dba..88ec834d0e 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -26,7 +26,7 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; use sp_runtime::traits::CheckedSub; -use sp_std::{marker::PhantomData, vec::Vec}; +use sp_std::vec::Vec; use crate::{ merkle::RevealedDidKey, @@ -86,7 +86,7 @@ impl From for u8 { } } -/// Proof verifier that tries to verify a DID signature over a given payload by +/// Function that tries to verify a DID signature over a given payload by /// using one of the DID keys revealed in the Merkle proof. This verifier is /// typically used in conjunction with a verifier that takes a user-provided /// input Merkle proof, verifies it, and transforms it into a struct that this @@ -110,7 +110,7 @@ impl From for u8 { /// * `RemoteBlockNumber`: Definition of a block number on the provider chain. /// * `CallVerifier`: A type specifying whether the provided `Call` can be /// dispatched with the information provided in the DIP proof. -pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< +pub(crate) fn verify_did_signature_for_call< Call, Submitter, DidLocalDetails, @@ -121,42 +121,15 @@ pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< RemoteBlockNumber, CallVerifier, >( - #[allow(clippy::type_complexity)] - PhantomData<( - Call, - Submitter, - DidLocalDetails, - MerkleProofEntries, - ContextProvider, - RemoteKeyId, - RemoteAccountId, - RemoteBlockNumber, - CallVerifier, - )>, -); - -impl< - Call, - Submitter, - DidLocalDetails, - MerkleProofEntries, - ContextProvider, - RemoteKeyId, - RemoteAccountId, - RemoteBlockNumber, - CallVerifier, - > - RevealedDidKeysSignatureAndCallVerifier< - Call, - Submitter, - DidLocalDetails, - MerkleProofEntries, - ContextProvider, - RemoteKeyId, - RemoteAccountId, - RemoteBlockNumber, - CallVerifier, - > where + call: &Call, + submitter: &Submitter, + local_details: &mut Option, + merkle_revealed_did_signature: RevealedDidKeysAndSignature, +) -> Result< + (DidVerificationKey, DidVerificationKeyRelationship), + RevealedDidKeysSignatureAndCallVerifierError, +> +where Call: Encode, Submitter: Encode, ContextProvider: DidSignatureVerifierContext, @@ -169,144 +142,82 @@ impl< CallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, { - #[cfg(not(feature = "runtime-benchmarks"))] - pub(crate) fn verify_did_signature_for_call( - call: &Call, - submitter: &Submitter, - local_details: &mut Option, - merkle_revealed_did_signature: RevealedDidKeysAndSignature, - ) -> Result< - (DidVerificationKey, DidVerificationKeyRelationship), - RevealedDidKeysSignatureAndCallVerifierError, - > { - use frame_support::ensure; - - let block_number = ContextProvider::current_block_number(); - let is_signature_fresh = if let Some(blocks_ago_from_now) = - block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) - { - // False if the signature is too old. - blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + {} } else { - // Signature generated at a future time, not possible to verify. - false - }; - ensure!( - is_signature_fresh, - RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh, - ); - let encoded_payload = ( - call, - &local_details, - submitter, - &merkle_revealed_did_signature.did_signature.block_number, - ContextProvider::genesis_hash(), - ContextProvider::signed_extra(), - ) - .encode(); - // Only consider verification keys from the set of revealed keys. - let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { + let block_number = ContextProvider::current_block_number(); + let is_signature_fresh = if let Some(blocks_ago_from_now) = + block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) + { + // False if the signature is too old. + blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + } else { + // Signature generated at a future time, not possible to verify. + false + }; + frame_support::ensure!( + is_signature_fresh, + RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh, + ); + } + } + let encoded_payload = ( + call, + &local_details, + submitter, + &merkle_revealed_did_signature.did_signature.block_number, + ContextProvider::genesis_hash(), + ContextProvider::signed_extra(), + ) + .encode(); + // Only consider verification keys from the set of revealed keys. + let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | { let DidPublicKey::PublicVerificationKey(key) = key else { return None }; if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) { // TODO: Fix this logic to avoid cloning Some(Ok((key.clone(), vr))) } else { - log::error!("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."); - Some(Err(RevealedDidKeysSignatureAndCallVerifierError::Internal)) + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + None + } else { + log::error!("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."); + Some(Err(RevealedDidKeysSignatureAndCallVerifierError::Internal)) + } + } } }).collect::>()?; - let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { - verification_key - .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) - .is_ok() - }); - let Some((key, relationship)) = valid_signing_key else { - return Err(RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable); - }; - if let Some(details) = local_details { - details.bump(); + let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { + verification_key + .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) + .is_ok() + }); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + let default = ( + DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])), + DidVerificationKeyRelationship::Authentication, + ); + let (key, relationship) = valid_signing_key.unwrap_or(&default); } else { - *local_details = Some(DidLocalDetails::default()); - }; - CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)) - .map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?; - Ok((key.clone(), *relationship)) + let (key, relationship) = valid_signing_key.ok_or(RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable)?; + } } - #[cfg(feature = "runtime-benchmarks")] - #[allow(clippy::result_unit_err)] - pub(crate) fn verify_did_signature_for_call( - call: &Call, - submitter: &Submitter, - local_details: &mut Option, - merkle_revealed_did_signature: RevealedDidKeysAndSignature, - ) -> Result< - (DidVerificationKey, DidVerificationKeyRelationship), - RevealedDidKeysSignatureAndCallVerifierError, - > { - use sp_core::ed25519; - - let block_number = ContextProvider::current_block_number(); - // Computed but ignored - if let Some(blocks_ago_from_now) = - block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) - { - // False if the signature is too old. - blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + if let Some(details) = local_details { + details.bump(); + } else { + *local_details = Some(DidLocalDetails::default()); + }; + let res = CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + drop(res); } else { - // Signature generated at a future time, not possible to verify. - false - }; - let encoded_payload = ( - call, - &local_details, - submitter, - &merkle_revealed_did_signature.did_signature.block_number, - ContextProvider::genesis_hash(), - ContextProvider::signed_extra(), - ) - .encode(); - // Only consider verification keys from the set of revealed keys. - let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = - merkle_revealed_did_signature - .merkle_leaves - .borrow() - .iter() - .filter_map( - |RevealedDidKey { - relationship, - details: DidPublicKeyDetails { key, .. }, - .. - }| { - let DidPublicKey::PublicVerificationKey(key) = key else { - return None; - }; - if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) { - Some(Ok((key.clone(), vr))) - } else { - None - } - }, - ) - .collect::>()?; - let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { - verification_key - .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) - .is_ok() - }); - let default = ( - DidVerificationKey::Ed25519(ed25519::Public::from_raw([0u8; 32])), - DidVerificationKeyRelationship::Authentication, - ); - let (key, relationship) = valid_signing_key.unwrap_or(&default); - if let Some(details) = local_details { - details.bump(); - } else { - *local_details = Some(DidLocalDetails::default()); - }; - // Ignore the result of this call - let _ = CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)); - Ok((key.clone(), *relationship)) + res.map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?; + } } + Ok((key.clone(), *relationship)) } diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index 00a897b538..58321c11bb 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -494,12 +494,11 @@ pub mod v0 { use crate::{ did::{ - RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, - RevealedDidKeysSignatureAndCallVerifierError, + verify_did_signature_for_call, RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifierError, }, export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, merkle::{ - DidMerkleProofVerifier, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, + verify_dip_merkle_proof, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves, }, state_proofs::{ @@ -776,7 +775,7 @@ pub mod v0 { .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; // 4. Verify DIP merkle proof. - let proof_leaves = DidMerkleProofVerifier::< + let proof_leaves = verify_dip_merkle_proof::< ProviderDipMerkleHasher, _, _, @@ -785,30 +784,20 @@ pub mod v0 { _, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + >(&subject_identity_commitment, proof.did.leaves) .map_err(DipChildProviderStateProofVerifierError::DipProof)?; // 5. Verify DID signature. - RevealedDidKeysSignatureAndCallVerifier::< - _, - _, - _, - _, - LocalContextProvider, - _, - _, - _, - LocalDidCallVerifier, - >::verify_did_signature_for_call( - call, - submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, - ) - .map_err(DipChildProviderStateProofVerifierError::DidSignature)?; + verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( + call, + submitter, + identity_details, + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + ) + .map_err(DipChildProviderStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index daf3759881..a67d9da56b 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -469,9 +469,9 @@ pub mod v0 { use sp_std::borrow::Borrow; use crate::{ - did::{RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier}, + did::{verify_did_signature_for_call, RevealedDidKeysAndSignature}, export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, - merkle::DidMerkleProofVerifier, + merkle::verify_dip_merkle_proof, state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, traits::ProviderParachainStorageInfo, }; @@ -741,7 +741,7 @@ pub mod v0 { ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - > = DidMerkleProofVerifier::< + > = verify_dip_merkle_proof::< ProviderDipMerkleHasher, _, _, @@ -750,30 +750,20 @@ pub mod v0 { _, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves) + >(&subject_identity_commitment, proof.did.leaves) .map_err(DipSiblingProviderStateProofVerifierError::DipProof)?; // 4. Verify DID signature. - RevealedDidKeysSignatureAndCallVerifier::< - _, - _, - _, - _, - LocalContextProvider, - _, - _, - _, - LocalDidCallVerifier, - >::verify_did_signature_for_call( - call, - submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, - ) - .map_err(DipSiblingProviderStateProofVerifierError::DidSignature)?; + verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( + call, + submitter, + identity_details, + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + ) + .map_err(DipSiblingProviderStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 3fecc5668e..6d744777f8 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -23,7 +23,7 @@ use frame_support::{traits::ConstU32, DefaultNoBound, RuntimeDebug}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{BoundedVec, SaturatedConversion}; -use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_std::{fmt::Debug, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; /// Type of a Merkle proof containing DID-related information. @@ -279,8 +279,8 @@ impl From for u8 { } } -/// A type that verifies a DIP Merkle proof revealing some leaves representing -/// parts of a KILT DID identity stored on the KILT chain. +/// A function that verifies a DIP Merkle proof revealing some leaves +/// representing parts of a KILT DID identity stored on the KILT chain. /// If cross-chain DID signatures are not required for the specific use case, /// this verifier can also be used on its own, without any DID signature /// verification. @@ -303,7 +303,7 @@ impl From for u8 { /// supported when verifying the Merkle proof. /// * `MAX_REVEALED_ACCOUNTS_COUNT`: The maximum number of linked accounts that /// are supported when verifying the Merkle proof. -pub(crate) struct DidMerkleProofVerifier< +pub(crate) fn verify_dip_merkle_proof< Hasher, KeyId, AccountId, @@ -312,20 +312,14 @@ pub(crate) struct DidMerkleProofVerifier< LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, ->(#[allow(clippy::type_complexity)] PhantomData<(Hasher, KeyId, AccountId, BlockNumber, Web3Name, LinkedAccountId)>); - -impl< - Hasher, - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > - DidMerkleProofVerifier< - Hasher, +>( + identity_commitment: &Hasher::Out, + proof: DidMerkleProof< + crate::BoundedBlindedValue, + RevealedDidMerkleProofLeaf, + >, +) -> Result< + RevealedDidMerkleProofLeaves< KeyId, AccountId, BlockNumber, @@ -333,7 +327,10 @@ impl< LinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, - > where + >, + DidMerkleProofVerifierError, +> +where BlockNumber: Encode + Clone, Hasher: sp_core::Hasher, KeyId: Encode + Clone, @@ -341,154 +338,79 @@ impl< LinkedAccountId: Encode + Clone, Web3Name: Encode + Clone, { - #[cfg(not(feature = "runtime-benchmarks"))] - pub(crate) fn verify_dip_merkle_proof( - identity_commitment: &Hasher::Out, - proof: DidMerkleProof< - crate::BoundedBlindedValue, - RevealedDidMerkleProofLeaf, - >, - ) -> Result< - RevealedDidMerkleProofLeaves< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >, - DidMerkleProofVerifierError, - > { - // TODO: more efficient by removing cloning and/or collecting. - // Did not find another way of mapping a Vec<(Vec, Vec)> to a - // Vec<(Vec, Option>)>. - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves) - .map_err(|_| DidMerkleProofVerifierError::InvalidMerkleProof)?; - - // At this point, we know the proof is valid. We just need to map the revealed - // leaves to something the consumer can easily operate on. - #[allow(clippy::type_complexity)] - let (did_keys, web3_name, linked_accounts): ( - BoundedVec, ConstU32>, - Option>, - BoundedVec>, - ) = proof.revealed.iter().try_fold( - ( - BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), - None, - BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), - ), - |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { - RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { - keys.try_push(RevealedDidKey { - // TODO: Avoid cloning if possible - id: key_id.0.clone(), - relationship: key_id.1, - details: key_value.0.clone(), - }) - .map_err(|_| DidMerkleProofVerifierError::TooManyRevealedKeys)?; - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) - } - // TODO: Avoid cloning if possible - RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( - keys, - Some(RevealedWeb3Name { - web3_name: revealed_web3_name.0.clone(), - claimed_at: details.0.clone(), - }), - linked_accounts, - )), - RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { - linked_accounts - .try_push(account_id.0.clone()) - .map_err(|_| DidMerkleProofVerifierError::TooManyRevealedAccounts)?; - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) - } - }, - )?; - - Ok(RevealedDidMerkleProofLeaves { - did_keys, - web3_name, - linked_accounts, - }) + // TODO: more efficient by removing cloning and/or collecting. + // Did not find another way of mapping a Vec<(Vec, Vec)> to a + // Vec<(Vec, Option>)>. + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + let res = verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + drop(res); + } else { + res.map_err(|_| DidMerkleProofVerifierError::InvalidMerkleProof)?; + } } - #[cfg(feature = "runtime-benchmarks")] + // At this point, we know the proof is valid. We just need to map the revealed + // leaves to something the consumer can easily operate on. #[allow(clippy::type_complexity)] - pub(crate) fn verify_dip_merkle_proof( - identity_commitment: &Hasher::Out, - proof: DidMerkleProof< - crate::BoundedBlindedValue, - RevealedDidMerkleProofLeaf, - >, - ) -> Result< - RevealedDidMerkleProofLeaves< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >, - DidMerkleProofVerifierError, - > { - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - // Ignore result - let _ = verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves); - - #[allow(clippy::type_complexity)] - let (did_keys, web3_name, linked_accounts): ( - BoundedVec, ConstU32>, - Option>, - BoundedVec>, - ) = proof.revealed.iter().try_fold( - ( - BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), - None, - BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), - ), - |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { - RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { - // Ignore error, just discard anything in excess. - let _ = keys.try_push(RevealedDidKey { - id: key_id.0.clone(), - relationship: key_id.1, - details: key_value.0.clone(), - }); - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + let (did_keys, web3_name, linked_accounts): ( + BoundedVec, ConstU32>, + Option>, + BoundedVec>, + ) = proof.revealed.into_iter().try_fold( + ( + BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), + None, + BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), + ), + |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { + RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { + let res = keys.try_push(RevealedDidKey { + id: key_id.0, + relationship: key_id.1, + details: key_value.0, + }); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + drop(res); + } else { + res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedKeys)?; + } } - RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( - keys, - Some(RevealedWeb3Name { - web3_name: revealed_web3_name.0.clone(), - claimed_at: details.0.clone(), - }), - linked_accounts, - )), - RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { - // Ignore error, just discard anything in excess. - let _ = linked_accounts.try_push(account_id.0.clone()); - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + } + RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( + keys, + Some(RevealedWeb3Name { + web3_name: revealed_web3_name.0, + claimed_at: details.0, + }), + linked_accounts, + )), + RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { + let res = linked_accounts.try_push(account_id.0); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + drop(res); + } else { + res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedAccounts)?; + } } - }, - )?; - - Ok(RevealedDidMerkleProofLeaves { - did_keys, - web3_name, - linked_accounts, - }) - } + + Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) + } + }, + )?; + + Ok(RevealedDidMerkleProofLeaves { + did_keys, + web3_name, + linked_accounts, + }) } diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 58619eb6bb..017470aa6e 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -187,17 +187,7 @@ pub(super) mod relay_chain { }); Ok(header) } - } - // Relies on the `RelayChainState::state_root_for_block` to retrieve the state - // root for the given block. - impl ParachainHeadProofVerifier - where - RelayChainState: RelayChainStateInfo, - OutputOf: Ord, - RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, - RelayChainState::Key: AsRef<[u8]>, - { /// Given a relaychain state root provided by the `RelayChainState` /// generic type, verify a state proof for the parachain with the /// provided ID. @@ -206,7 +196,10 @@ pub(super) mod relay_chain { para_id: &RelayChainState::ParaId, relay_height: &RelayChainState::BlockNumber, proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { + ) -> Result, ParachainHeadProofVerifierError> + where + RelayChainState: RelayChainStateInfo, + { let relay_state_root = RelayChainState::state_root_for_block(relay_height) .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) @@ -217,7 +210,10 @@ pub(super) mod relay_chain { para_id: &RelayChainState::ParaId, relay_height: &RelayChainState::BlockNumber, proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { + ) -> Result, ParachainHeadProofVerifierError> + where + RelayChainState: RelayChainStateInfo, + { let relay_state_root = RelayChainState::state_root_for_block(relay_height).unwrap_or_default(); Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) } @@ -370,7 +366,6 @@ pub(super) mod parachain { /// Given a parachain state root, verify a state proof for the /// commitment of a given subject identifier. #[cfg(not(feature = "runtime-benchmarks"))] - #[allow(clippy::result_unit_err)] pub fn verify_proof_for_identifier( identifier: &ParaInfo::Identifier, state_root: OutputOf, diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 6490684fec..fbbda7a3f1 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -84,48 +84,15 @@ where type Success = LinkedDidInfoOf; fn retrieve(identifier: &Runtime::Identifier) -> Result { - let did_details = match ( - did::Pallet::::get_did(identifier), - did::Pallet::::get_deleted_did(identifier), - ) { - (Some(details), _) => Ok(details), - (_, Some(_)) => Err(LinkedDidInfoProviderError::DidDeleted), - _ => Err(LinkedDidInfoProviderError::DidNotFound), - }?; - let web3_name_details = if let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) { - let Some(ownership) = pallet_web3_names::Pallet::::owner(&web3_name) else { - log::error!( - "Inconsistent reverse map pallet_web3_names::owner(web3_name). Cannot find owner for web3name {:#?}", - web3_name - ); - return Err(LinkedDidInfoProviderError::Internal); - }; - Ok(Some(Web3OwnershipOf:: { - web3_name, - claimed_at: ownership.claimed_at, - })) - } else { - Ok(None) - }?; - - // Check if the user has too many linked accounts. If they have more than - // [MAX_LINKED_ACCOUNTS], we throw an error. - let are_linked_accounts_within_limit = - pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) - .nth(MAX_LINKED_ACCOUNTS.saturated_into()) - .is_none(); - ensure!( - are_linked_accounts_within_limit, - LinkedDidInfoProviderError::TooManyLinkedAccounts + did::Pallet::::get_deleted_did(identifier).is_none(), + LinkedDidInfoProviderError::DidDeleted, ); + let did_details = did::Pallet::::get_did(identifier).ok_or(LinkedDidInfoProviderError::DidNotFound)?; - let linked_accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) - .take(MAX_LINKED_ACCOUNTS.saturated_into()) - .collect::>() - .try_into() - // Should never happen since we checked above. - .map_err(|_| LinkedDidInfoProviderError::TooManyLinkedAccounts)?; + let web3_name_details = retrieve_w3n::(identifier)?; + + let linked_accounts = retrieve_linked_accounts::(identifier)?; Ok(LinkedDidInfoOf { did_details, @@ -135,6 +102,59 @@ where } } +fn retrieve_w3n( + identifier: &Runtime::Identifier, +) -> Result>, LinkedDidInfoProviderError> +where + Runtime: did::Config::Identifier> + + pallet_web3_names::Config::Identifier> + + pallet_dip_provider::Config, +{ + let Some(web3_name) = pallet_web3_names::Pallet::::names(identifier) else { + return Ok(None); + }; + + let ownership = pallet_web3_names::Pallet::::owner(&web3_name).ok_or_else(|| { + log::error!( + "Inconsistent reverse map pallet_web3_names::owner(web3_name). Cannot find owner for web3name {:#?}", + web3_name + ); + LinkedDidInfoProviderError::Internal + })?; + + Ok(Some(Web3OwnershipOf:: { + web3_name, + claimed_at: ownership.claimed_at, + })) +} + +fn retrieve_linked_accounts( + identifier: &Runtime::Identifier, +) -> Result>, LinkedDidInfoProviderError> +where + Runtime: did::Config::Identifier> + + pallet_did_lookup::Config::Identifier> + + pallet_dip_provider::Config, +{ + // Check if the user has too many linked accounts. If they have more than + // [MAX_LINKED_ACCOUNTS], we throw an error. + let are_linked_accounts_within_limit = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) + .nth(MAX_LINKED_ACCOUNTS.saturated_into()) + .is_none(); + + ensure!( + are_linked_accounts_within_limit, + LinkedDidInfoProviderError::TooManyLinkedAccounts + ); + + pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(identifier) + .take(MAX_LINKED_ACCOUNTS.saturated_into()) + .collect::>() + .try_into() + // Should never happen since we checked above. + .map_err(|_| LinkedDidInfoProviderError::TooManyLinkedAccounts) +} + #[cfg(feature = "runtime-benchmarks")] impl GetWorstCase> for LinkedDidInfoOf From 84e6d22aed7d721fea3ff5f389878353b51009be Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 12 Dec 2023 10:54:54 +0000 Subject: [PATCH 24/28] chore: more improvements (#592) Addresses most of the outstanding comments in https://github.com/KILTprotocol/kilt-node/pull/494. Which ones have been addressed are specified in that PR. --- Cargo.lock | 9 +- Cargo.toml | 3 +- .../Cargo.toml | 6 +- .../src/did.rs | 6 +- .../src/export/common.rs | 0 .../src/export/mod.rs | 17 +- .../src/export/parachain.rs} | 119 +++-- .../src/export/relaychain.rs} | 113 ++-- .../src/lib.rs | 6 +- .../src/merkle.rs | 0 .../src/state_proofs/mod.rs | 81 +++ .../src/state_proofs/parachain.rs | 168 ++++++ .../src/state_proofs/relaychain.rs | 261 ++++++++++ .../src/traits.rs | 20 +- .../src/utils.rs | 0 crates/kilt-dip-support/src/state_proofs.rs | 483 ------------------ dip-template/pallets/pallet-postit/src/lib.rs | 4 +- .../pallets/pallet-postit/src/traits.rs | 2 +- dip-template/runtimes/dip-consumer/Cargo.toml | 6 +- dip-template/runtimes/dip-consumer/src/dip.rs | 14 +- .../dip-consumer/src/origin_adapter.rs | 4 +- dip-template/runtimes/dip-provider/Cargo.toml | 6 +- dip-template/runtimes/dip-provider/src/dip.rs | 4 +- pallets/pallet-deposit-storage/src/deposit.rs | 4 +- pallets/pallet-dip-consumer/Cargo.toml | 1 + pallets/pallet-dip-consumer/src/lib.rs | 44 +- pallets/pallet-dip-provider/src/lib.rs | 51 +- runtimes/common/Cargo.toml | 6 +- runtimes/common/src/dip/did.rs | 2 +- runtimes/common/src/dip/merkle.rs | 17 +- 30 files changed, 743 insertions(+), 714 deletions(-) rename crates/{kilt-dip-support => kilt-dip-primitives}/Cargo.toml (90%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/did.rs (98%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/export/common.rs (100%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/export/mod.rs (72%) rename crates/{kilt-dip-support/src/export/sibling.rs => kilt-dip-primitives/src/export/parachain.rs} (88%) rename crates/{kilt-dip-support/src/export/child.rs => kilt-dip-primitives/src/export/relaychain.rs} (88%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/lib.rs (84%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/merkle.rs (100%) create mode 100644 crates/kilt-dip-primitives/src/state_proofs/mod.rs create mode 100644 crates/kilt-dip-primitives/src/state_proofs/parachain.rs create mode 100644 crates/kilt-dip-primitives/src/state_proofs/relaychain.rs rename crates/{kilt-dip-support => kilt-dip-primitives}/src/traits.rs (93%) rename crates/{kilt-dip-support => kilt-dip-primitives}/src/utils.rs (100%) delete mode 100644 crates/kilt-dip-support/src/state_proofs.rs diff --git a/Cargo.lock b/Cargo.lock index 0d0eeaafa8..0c6b1e9f21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2445,7 +2445,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "hex-literal 0.3.4", - "kilt-dip-support", + "kilt-dip-primitives", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -2550,7 +2550,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "hex-literal 0.3.4", - "kilt-dip-support", + "kilt-dip-primitives", "kilt-runtime-api-did", "kilt-runtime-api-dip-provider", "kilt-support", @@ -4555,7 +4555,7 @@ dependencies = [ ] [[package]] -name = "kilt-dip-support" +name = "kilt-dip-primitives" version = "1.12.0-dev" dependencies = [ "cfg-if", @@ -6537,6 +6537,7 @@ dependencies = [ name = "pallet-dip-consumer" version = "1.12.0-dev" dependencies = [ + "cfg-if", "frame-benchmarking", "frame-support", "frame-system", @@ -9916,7 +9917,7 @@ dependencies = [ "frame-support", "frame-system", "kilt-asset-dids", - "kilt-dip-support", + "kilt-dip-primitives", "kilt-support", "log", "pallet-authorship", diff --git a/Cargo.toml b/Cargo.toml index afd7e6a757..402d2dea51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ substrate-wasm-builder = {git = "https://github.com/paritytech/substrate", branc # External (without extra features and with default disabled if necessary) base58 = {version = "0.2.0", default-features = false} bitflags = {version = "1.3.2", default-features = false} +cfg-if = "1.0" clap = "4.1.6" env_logger = "0.10.0" fluent-uri = { version = "0.1.4", default-features = false } @@ -68,7 +69,7 @@ pallet-migration = {path = "pallets/pallet-migration", default-features = false} # Internal support (with default disabled) kilt-asset-dids = {path = "crates/assets", default-features = false} -kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} +kilt-dip-primitives = {path = "crates/kilt-dip-primitives", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-primitives/Cargo.toml similarity index 90% rename from crates/kilt-dip-support/Cargo.toml rename to crates/kilt-dip-primitives/Cargo.toml index 30262359b2..2afec4987c 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-primitives/Cargo.toml @@ -1,11 +1,11 @@ [package] authors.workspace = true -description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." +description = "Primitive types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "kilt-dip-support" +name = "kilt-dip-primitives" readme.workspace = true repository.workspace = true version.workspace = true @@ -14,7 +14,7 @@ version.workspace = true # External dependencies hash-db.workspace = true log.workspace = true -cfg-if = "1.0" +cfg-if.workspace = true # Internal dependencies did.workspace = true diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-primitives/src/did.rs similarity index 98% rename from crates/kilt-dip-support/src/did.rs rename to crates/kilt-dip-primitives/src/did.rs index 88ec834d0e..683e952284 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-primitives/src/did.rs @@ -30,7 +30,7 @@ use sp_std::vec::Vec; use crate::{ merkle::RevealedDidKey, - traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, + traits::{DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, }; /// Type returned by the Merkle proof verifier component of the DIP consumer @@ -136,7 +136,7 @@ where ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, ContextProvider::Hash: Encode, ContextProvider::SignedExtra: Encode, - DidLocalDetails: Bump + Default + Encode, + DidLocalDetails: Incrementable + Default + Encode, RemoteAccountId: Clone, MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, CallVerifier: @@ -207,7 +207,7 @@ where } if let Some(details) = local_details { - details.bump(); + details.increment(); } else { *local_details = Some(DidLocalDetails::default()); }; diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-primitives/src/export/common.rs similarity index 100% rename from crates/kilt-dip-support/src/export/common.rs rename to crates/kilt-dip-primitives/src/export/common.rs diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-primitives/src/export/mod.rs similarity index 72% rename from crates/kilt-dip-support/src/export/mod.rs rename to crates/kilt-dip-primitives/src/export/mod.rs index 01fd5ad2a1..a48871a305 100644 --- a/crates/kilt-dip-support/src/export/mod.rs +++ b/crates/kilt-dip-primitives/src/export/mod.rs @@ -16,21 +16,20 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -/// Verification logic to integrate a child chain as a DIP provider. -pub mod child; /// Verification logic to integrate a sibling chain as a DIP provider. -pub mod sibling; +pub mod parachain; +/// Verification logic to integrate a child chain as a DIP provider. +pub mod relaychain; mod common; -pub use child::{ - DipChildProviderStateProofVerifierError, KiltVersionedChildProviderVerifier, VersionedChildParachainDipStateProof, +pub use parachain::{ + DipParachainStateProofVerifierError, KiltVersionedParachainVerifier, VersionedDipParachainStateProof, }; -pub use sibling::{ - DipSiblingProviderStateProofVerifierError, KiltVersionedSiblingProviderVerifier, - VersionedSiblingParachainDipStateProof, +pub use relaychain::{ + DipRelaychainStateProofVerifierError, KiltVersionedRelaychainVerifier, VersionedRelaychainStateProof, }; pub mod latest { - pub use super::{child::latest::*, common::latest::*, sibling::latest::*}; + pub use super::{common::latest::*, parachain::latest::*, relaychain::latest::*}; } diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-primitives/src/export/parachain.rs similarity index 88% rename from crates/kilt-dip-support/src/export/sibling.rs rename to crates/kilt-dip-primitives/src/export/parachain.rs index a67d9da56b..f0a6a63fc4 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-primitives/src/export/parachain.rs @@ -32,8 +32,8 @@ use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, - traits::{self, Bump, DidSignatureVerifierContext, DipCallOriginFilter}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, + traits::{self, DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, utils::OutputOf, BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; @@ -43,15 +43,14 @@ use crate::{ /// /// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -#[non_exhaustive] -pub enum VersionedSiblingParachainDipStateProof< +pub enum VersionedDipParachainStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, LocalBlockNumber, > { V0( - v0::SiblingParachainDipStateProof< + v0::ParachainDipStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, @@ -63,7 +62,7 @@ pub enum VersionedSiblingParachainDipStateProof< #[cfg(feature = "runtime-benchmarks")] impl kilt_support::traits::GetWorstCase - for VersionedSiblingParachainDipStateProof< + for VersionedDipParachainStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, @@ -76,11 +75,11 @@ impl Self { - Self::V0(v0::SiblingParachainDipStateProof::worst_case(context)) + Self::V0(v0::ParachainDipStateProof::worst_case(context)) } } -pub enum DipSiblingProviderStateProofVerifierError< +pub enum DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -100,7 +99,7 @@ impl< DidSignatureVerificationError, > From< - DipSiblingProviderStateProofVerifierError< + DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -114,7 +113,7 @@ where DidSignatureVerificationError: Into, { fn from( - value: DipSiblingProviderStateProofVerifierError< + value: DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -122,15 +121,15 @@ where >, ) -> Self { match value { - DipSiblingProviderStateProofVerifierError::UnsupportedVersion => 0, - DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + DipParachainStateProofVerifierError::UnsupportedVersion => 0, + DipParachainStateProofVerifierError::ParachainHeadMerkleProof(error) => { u8::MAX as u16 + error.into() as u16 } - DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { u8::MAX as u16 * 2 + error.into() as u16 } - DipSiblingProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipSiblingProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + DipParachainStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipParachainStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, } } } @@ -138,7 +137,7 @@ where /// Proof verifier configured given a specific KILT runtime implementation. /// /// It is a specialization of the -/// [`GenericVersionedDipSiblingProviderStateProofVerifier`] type, with +/// [`GenericVersionedParachainVerifier`] type, with /// configurations derived from the provided KILT runtime. /// /// The generic types @@ -151,14 +150,16 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept revealed as part of the DIP identity proof. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the -/// verifier will accept revealed as part of the DIP identity proof. -/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID -/// signature is considered fresh. +/// * `MAX_REVEALED_KEYS_COUNT`: [OPTIONAL] Max number of DID keys that the +/// verifier will accept revealed as part of the DIP identity proof. It +/// defaults to **10**. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: [OPTIONAL] Max number of linked accounts +/// that the verifier will accept revealed as part of the DIP identity proof. +/// It defaults to **10**. +/// * `MAX_DID_SIGNATURE_DURATION`: [OPTIONAL] Max number of blocks a +/// cross-chain DID signature is considered fresh. It defaults to **50**. /// -/// It specializes the [`GenericVersionedDipSiblingProviderStateProofVerifier`] +/// It specializes the [`GenericVersionedParachainVerifier`] /// type by using the following types for its generics: /// * `RelayChainInfo`: The provided `RelayChainInfo`. /// * `ChildProviderParachainId`: The provided `KiltParachainId`. @@ -177,15 +178,15 @@ where /// configured with the provided `KiltRuntime` and /// `MAX_DID_SIGNATURE_DURATION`. /// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. -pub struct KiltVersionedSiblingProviderVerifier< +pub struct KiltVersionedParachainVerifier< KiltRuntime, KiltParachainId, RelayChainStateInfo, KiltDipMerkleHasher, LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + const MAX_REVEALED_KEYS_COUNT: u32 = 10, + const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, + const MAX_DID_SIGNATURE_DURATION: u16 = 50, >( PhantomData<( KiltRuntime, @@ -207,7 +208,7 @@ impl< const MAX_REVEALED_ACCOUNTS_COUNT: u32, const MAX_DID_SIGNATURE_DURATION: u16, > IdentityProofVerifier - for KiltVersionedSiblingProviderVerifier< + for KiltVersionedParachainVerifier< KiltRuntime, KiltParachainId, RelayChainStateInfo, @@ -227,7 +228,7 @@ impl< KeyIdOf: Into, KiltDipMerkleHasher: sp_core::Hasher>, ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, RelayChainStateInfo::ParaId: From, RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, @@ -240,13 +241,13 @@ impl< ), >, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedSiblingParachainDipStateProof< + type Proof = VersionedDipParachainStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -275,7 +276,7 @@ impl< identity_details: &mut Option, proof: Self::Proof, ) -> Result { - , @@ -302,12 +303,12 @@ impl< /// versions coming from a sibling provider running one of the available KILT /// runtimes. /// -/// It expects the DIP proof to be a [`VersionedSiblingParachainDipStateProof`], +/// It expects the DIP proof to be a [`VersionedDipParachainStateProof`], /// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully /// verified. /// /// For more info, refer to the version-specific proof identifiers. -pub struct GenericVersionedDipSiblingProviderStateProofVerifier< +pub struct GenericVersionedParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -351,7 +352,7 @@ impl< LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for GenericVersionedDipSiblingProviderStateProofVerifier< + for GenericVersionedParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -366,7 +367,7 @@ impl< LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, @@ -398,13 +399,13 @@ impl< ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedSiblingParachainDipStateProof< + type Proof = VersionedDipParachainStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -434,7 +435,7 @@ impl< proof: Self::Proof, ) -> Result { match proof { - VersionedSiblingParachainDipStateProof::V0(v0_proof) => kilt_support::traits::GetWorstCase - for SiblingParachainDipStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, - > where + for ParachainDipStateProof + where DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, DipMerkleProofRevealedLeaf: Default + Clone, RelayBlockHeight: Default, @@ -532,7 +529,7 @@ pub mod v0 { /// different verifier or a wrapper around this verifier must be built. /// /// It expects the DIP proof to be a - /// [`VersionedSiblingParachainDipStateProof`], and returns + /// [`VersionedDipParachainStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream /// call dispatched. @@ -588,7 +585,7 @@ pub mod v0 { /// verified, to filter only the revealed keys that match the provided /// relationship. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] - pub struct DipSiblingProviderStateProofVerifier< + pub struct ParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -632,7 +629,7 @@ pub mod v0 { LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for DipSiblingProviderStateProofVerifier< + for ParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -647,7 +644,7 @@ pub mod v0 { LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, @@ -679,13 +676,13 @@ pub mod v0 { ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = SiblingParachainDipStateProof< + type Proof = ParachainDipStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -714,23 +711,23 @@ pub mod v0 { identity_details: &mut Option, proof: Self::Proof, ) -> Result { - // 1. Verify relay chain proof. + // 1. Verify parachain state is finalized by relay chain and fresh. let provider_parachain_header = ParachainHeadProofVerifier::::verify_proof_for_parachain( &SiblingProviderParachainId::get(), &proof.para_state_root.relay_block_height, proof.para_state_root.proof, ) - .map_err(DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + .map_err(DipParachainStateProofVerifierError::ParachainHeadMerkleProof)?; - // 2. Verify parachain state proof. + // 2. Verify commitment is included in provider parachain. let subject_identity_commitment = DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( subject, provider_parachain_header.state_root.into(), proof.dip_identity_commitment, ) - .map_err(DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + .map_err(DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof)?; // 3. Verify DIP merkle proof. let proof_leaves: RevealedDidMerkleProofLeaves< @@ -751,9 +748,9 @@ pub mod v0 { MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipSiblingProviderStateProofVerifierError::DipProof)?; + .map_err(DipParachainStateProofVerifierError::DipProof)?; - // 4. Verify DID signature. + // 4. Verify call is signed by one of the DID keys revealed at step 3. verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( call, submitter, @@ -763,7 +760,7 @@ pub mod v0 { did_signature: proof.did.signature, }, ) - .map_err(DipSiblingProviderStateProofVerifierError::DidSignature)?; + .map_err(DipParachainStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-primitives/src/export/relaychain.rs similarity index 88% rename from crates/kilt-dip-support/src/export/child.rs rename to crates/kilt-dip-primitives/src/export/relaychain.rs index 58321c11bb..ce75fc5cfa 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-primitives/src/export/relaychain.rs @@ -32,10 +32,10 @@ use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, traits::{ - Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStorageInfo, - RelayChainStorageInfo, + DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, Incrementable, + ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, @@ -46,15 +46,14 @@ use crate::{ /// /// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -#[non_exhaustive] -pub enum VersionedChildParachainDipStateProof< +pub enum VersionedRelaychainStateProof< ParentBlockHeight: Copy + Into + TryFrom, ParentBlockHasher: Hash, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, > { V0( - v0::ChildParachainDipStateProof< + v0::RelaychainDipStateProof< ParentBlockHeight, ParentBlockHasher, DipMerkleProofBlindedValues, @@ -63,7 +62,7 @@ pub enum VersionedChildParachainDipStateProof< ), } -pub enum DipChildProviderStateProofVerifierError< +pub enum DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -85,7 +84,7 @@ impl< DidSignatureVerificationError, > From< - DipChildProviderStateProofVerifierError< + DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -99,7 +98,7 @@ where DidSignatureVerificationError: Into, { fn from( - value: DipChildProviderStateProofVerifierError< + value: DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -107,17 +106,17 @@ where >, ) -> Self { match value { - DipChildProviderStateProofVerifierError::UnsupportedVersion => 0, - DipChildProviderStateProofVerifierError::InvalidBlockHeight => 1, - DipChildProviderStateProofVerifierError::InvalidBlockHash => 2, - DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + DipRelaychainStateProofVerifierError::UnsupportedVersion => 0, + DipRelaychainStateProofVerifierError::InvalidBlockHeight => 1, + DipRelaychainStateProofVerifierError::InvalidBlockHash => 2, + DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof(error) => { u8::MAX as u16 + error.into() as u16 } - DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { u8::MAX as u16 * 2 + error.into() as u16 } - DipChildProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipChildProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + DipRelaychainStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipRelaychainStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, } } } @@ -125,7 +124,7 @@ where /// Proof verifier configured given a specific KILT runtime implementation. /// /// A specialization of the -/// [`GenericVersionedDipChildProviderStateProofVerifier`] type, with +/// [`GenericVersionedRelaychainVerifier`] type, with /// configurations derived from the provided KILT runtime. /// /// The generic types are the following: @@ -138,14 +137,16 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept revealed as part of the DIP identity proof. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the -/// verifier will accept revealed as part of the DIP identity proof. -/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID -/// signature is considered fresh. +/// * `MAX_REVEALED_KEYS_COUNT`: [OPTIONAL] Max number of DID keys that the +/// verifier will accept revealed as part of the DIP identity proof. It +/// defaults to **10**. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: [OPTIONAL] Max number of linked accounts +/// that the verifier will accept revealed as part of the DIP identity proof. +/// It defaults to **10**. +/// * `MAX_DID_SIGNATURE_DURATION`: [OPTIONAL] Max number of blocks a +/// cross-chain DID signature is considered fresh. It defaults to **50**. /// -/// It specializes the [`GenericVersionedDipChildProviderStateProofVerifier`] +/// It specializes the [`GenericVersionedRelaychainVerifier`] /// type by using the following types for its generics: /// * `RelayChainInfo`: The provided `RelayChainInfo`. /// * `ChildProviderParachainId`: The provided `KiltParachainId`. @@ -164,15 +165,15 @@ where /// configured with the provided `KiltRuntime` and /// `MAX_DID_SIGNATURE_DURATION`. /// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. -pub struct KiltVersionedChildProviderVerifier< +pub struct KiltVersionedRelaychainVerifier< KiltRuntime, KiltParachainId, RelayChainInfo, KiltDipMerkleHasher, LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + const MAX_REVEALED_KEYS_COUNT: u32 = 10, + const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, + const MAX_DID_SIGNATURE_DURATION: u16 = 50, >( PhantomData<( KiltRuntime, @@ -194,7 +195,7 @@ impl< const MAX_REVEALED_ACCOUNTS_COUNT: u32, const MAX_DID_SIGNATURE_DURATION: u16, > IdentityProofVerifier - for KiltVersionedChildProviderVerifier< + for KiltVersionedRelaychainVerifier< KiltRuntime, KiltParachainId, RelayChainInfo, @@ -214,7 +215,7 @@ impl< KeyIdOf: Into, KiltDipMerkleHasher: sp_core::Hasher>, ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< BlockNumber = ::BlockNumber, @@ -244,13 +245,13 @@ impl< ), >, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedChildParachainDipStateProof< + type Proof = VersionedRelaychainStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -279,7 +280,7 @@ impl< identity_details: &mut Option, proof: Self::Proof, ) -> Result { - , @@ -306,12 +307,12 @@ impl< /// versions coming from a child provider running one of the available KILT /// runtimes. /// -/// It expects the DIP proof to be a [`VersionedChildParachainDipStateProof`], +/// It expects the DIP proof to be a [`VersionedRelaychainStateProof`], /// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully /// verified. /// /// For more info, refer to the version-specific proof identifiers. -pub struct GenericVersionedDipChildProviderStateProofVerifier< +pub struct GenericVersionedRelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -355,7 +356,7 @@ impl< LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for GenericVersionedDipChildProviderStateProofVerifier< + for GenericVersionedRelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -370,7 +371,7 @@ impl< LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< @@ -418,13 +419,13 @@ impl< ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedChildParachainDipStateProof< + type Proof = VersionedRelaychainStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -454,7 +455,7 @@ impl< proof: Self::Proof, ) -> Result { match proof { - VersionedChildParachainDipStateProof::V0(v0_proof) => + TryFrom, ParentBlockHasher: Hash, DipMerkleProofBlindedValues, @@ -545,7 +546,7 @@ pub mod v0 { /// different verifier or a wrapper around this verifier must be built. /// /// It expects the DIP proof to be a - /// [`VersionedChildParachainDipStateProof`], and returns + /// [`VersionedRelaychainStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream /// call dispatched. @@ -602,7 +603,7 @@ pub mod v0 { /// key relationship. This information is used once the Merkle proof is /// verified, to filter only the revealed keys that match the provided /// relationship. - pub struct DipChildProviderStateProofVerifier< + pub struct RelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -646,7 +647,7 @@ pub mod v0 { LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for DipChildProviderStateProofVerifier< + for RelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -661,7 +662,7 @@ pub mod v0 { LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< @@ -710,13 +711,13 @@ pub mod v0 { ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = ChildParachainDipStateProof< + type Proof = RelaychainDipStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -747,11 +748,11 @@ pub mod v0 { ) -> Result { // 1. Retrieve block hash from provider at the proof height let block_hash_at_height = RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height) - .ok_or(DipChildProviderStateProofVerifierError::InvalidBlockHeight)?; + .ok_or(DipRelaychainStateProofVerifierError::InvalidBlockHeight)?; // 1.1 Verify that the provided header hashes to the same block has retrieved if block_hash_at_height != proof.relay_header.hash() { - return Err(DipChildProviderStateProofVerifierError::InvalidBlockHash); + return Err(DipRelaychainStateProofVerifierError::InvalidBlockHash); } // 1.2 If so, extract the state root from the header let state_root_at_height = proof.relay_header.state_root; @@ -763,7 +764,7 @@ pub mod v0 { &state_root_at_height, proof.para_state_root.proof, ) - .map_err(DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + .map_err(DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof)?; // 3. Verify parachain state proof. let subject_identity_commitment = @@ -772,7 +773,7 @@ pub mod v0 { provider_parachain_header.state_root.into(), proof.dip_identity_commitment, ) - .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + .map_err(DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof)?; // 4. Verify DIP merkle proof. let proof_leaves = verify_dip_merkle_proof::< @@ -785,7 +786,7 @@ pub mod v0 { MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipChildProviderStateProofVerifierError::DipProof)?; + .map_err(DipRelaychainStateProofVerifierError::DipProof)?; // 5. Verify DID signature. verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( @@ -797,7 +798,7 @@ pub mod v0 { did_signature: proof.did.signature, }, ) - .map_err(DipChildProviderStateProofVerifierError::DidSignature)?; + .map_err(DipRelaychainStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-primitives/src/lib.rs similarity index 84% rename from crates/kilt-dip-support/src/lib.rs rename to crates/kilt-dip-primitives/src/lib.rs index 66ec155cdc..04c9f17a25 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-primitives/src/lib.rs @@ -21,8 +21,8 @@ //! protocol. //! //! Consumers of KILT identities should prefer directly using -//! [`KiltVersionedChildProviderVerifier`] for consumer relaychains and -//! [`KiltVersionedSiblingProviderVerifier`] for consumer sibling parachains. +//! [`KiltVersionedRelaychainVerifier`] for consumer relaychains and +//! [`KiltVersionedParachainVerifier`] for consumer sibling parachains. #![cfg_attr(not(feature = "std"), no_std)] @@ -37,7 +37,7 @@ mod export; pub use export::*; pub use state_proofs::{ parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, - relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, + relaychain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, }; pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; pub use utils::BoundedBlindedValue; diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-primitives/src/merkle.rs similarity index 100% rename from crates/kilt-dip-support/src/merkle.rs rename to crates/kilt-dip-primitives/src/merkle.rs diff --git a/crates/kilt-dip-primitives/src/state_proofs/mod.rs b/crates/kilt-dip-primitives/src/state_proofs/mod.rs new file mode 100644 index 0000000000..f21a27b147 --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/mod.rs @@ -0,0 +1,81 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Module to deal with cross-chain state proofs. + +/// Parachain-related state proof logic. +pub(crate) mod parachain; +/// Relaychain-related state proof logic. +pub(crate) mod relaychain; + +// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 +// Needs to be replaced with its runtime-friendly version when available, or be +// kept up-to-date with upstream. +mod substrate_no_std_port { + use hash_db::EMPTY_PREFIX; + use parity_scale_codec::Codec; + use sp_core::Hasher; + use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; + use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + use sp_trie::{HashDBT, MemoryDB, StorageProof}; + + pub(super) fn read_proof_check( + root: H::Out, + proof: StorageProof, + keys: I, + ) -> Result, Option>>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = BTreeMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) + } + + fn read_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + key: &[u8], + ) -> Result>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + { + proving_backend.storage(key).map_err(|_| ()) + } + + fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> + where + H: Hasher, + H::Out: Codec, + { + let db = proof.into_memory_db(); + + if db.contains(&root, EMPTY_PREFIX) { + Ok(TrieBackendBuilder::new(db, root).build()) + } else { + Err(()) + } + } +} diff --git a/crates/kilt-dip-primitives/src/state_proofs/parachain.rs b/crates/kilt-dip-primitives/src/state_proofs/parachain.rs new file mode 100644 index 0000000000..3ad51695f0 --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/parachain.rs @@ -0,0 +1,168 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use parity_scale_codec::Decode; +use sp_core::RuntimeDebug; +use sp_std::marker::PhantomData; +use sp_trie::StorageProof; + +use crate::{ + state_proofs::substrate_no_std_port::read_proof_check, traits::ProviderParachainStorageInfo, utils::OutputOf, +}; + +#[derive(RuntimeDebug)] +pub enum DipIdentityCommitmentProofVerifierError { + InvalidMerkleProof, + RequiredLeafNotRevealed, + CommitmentDecode, +} + +impl From for u8 { + fn from(value: DipIdentityCommitmentProofVerifierError) -> Self { + match value { + DipIdentityCommitmentProofVerifierError::InvalidMerkleProof => 0, + DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed => 1, + DipIdentityCommitmentProofVerifierError::CommitmentDecode => 2, + } + } +} + +/// Verifier of state proofs that reveal the value of the DIP commitment for +/// a given subject on the provider chain. The generic types indicate the +/// following: +/// * `ParaInfo`: defines the provider parachain runtime types relevant for +/// state proof verification, and returns the provider's runtime storage key +/// identifying the identity commitment for a subject with the given +/// identifier. +pub struct DipIdentityCommitmentProofVerifier(PhantomData); + +impl DipIdentityCommitmentProofVerifier +where + ParaInfo: ProviderParachainStorageInfo, + OutputOf: Ord, + ParaInfo::Commitment: Decode, + ParaInfo::Key: AsRef<[u8]>, +{ + /// Given a parachain state root, verify a state proof for the + /// commitment of a given subject identifier. + #[cfg(not(feature = "runtime-benchmarks"))] + pub fn verify_proof_for_identifier( + identifier: &ParaInfo::Identifier, + state_root: OutputOf, + proof: impl IntoIterator>, + ) -> Result { + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(state_root, storage_proof, [&dip_commitment_storage_key].iter()) + .map_err(|_| DipIdentityCommitmentProofVerifierError::InvalidMerkleProof)?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); + } + let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { + return Err(DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed); + }; + ParaInfo::Commitment::decode(&mut &encoded_commitment[..]) + .map_err(|_| DipIdentityCommitmentProofVerifierError::CommitmentDecode) + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_identifier( + identifier: &ParaInfo::Identifier, + state_root: OutputOf, + proof: impl IntoIterator>, + ) -> Result + where + ParaInfo::Commitment: Default, + { + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(state_root, storage_proof, [&dip_commitment_storage_key].iter()) + .unwrap_or_default(); + let encoded_commitment = + if let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) { + encoded_commitment.clone() + } else { + sp_std::vec::Vec::default() + }; + let commitment = ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).unwrap_or_default(); + Ok(commitment) + } +} + +#[cfg(test)] +mod spiritnet_test_event_count_value { + use super::*; + + use hex_literal::hex; + use pallet_dip_provider::IdentityCommitmentVersion; + use sp_core::{storage::StorageKey, H256}; + use sp_runtime::traits::BlakeTwo256; + + // Spiritnet block n: 4_184_668, + // hash 0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5 + struct StaticSpiritnetInfoProvider; + + // We use the `system::eventCount()` storage entry as a unit test here. + impl ProviderParachainStorageInfo for StaticSpiritnetInfoProvider { + type BlockNumber = u32; + // The type of the `eventCount()` storage entry. + type Commitment = u32; + type Hasher = BlakeTwo256; + // Irrelevant for this test here + type Identifier = (); + type Key = StorageKey; + + fn dip_subject_storage_key(_identifier: &Self::Identifier, _version: IdentityCommitmentVersion) -> Self::Key { + // system::eventCount() raw storage key + let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); + StorageKey(storage_key) + } + } + + #[test] + fn test_spiritnet_event_count() { + // As of RPC state_getReadProof(" + // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") + let spiritnet_event_count_proof_at_block = [ + hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), + hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), + hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), + hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), + ].to_vec(); + let spiritnet_state_root: H256 = + hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); + // As of query system::eventCount() at block + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which + // results in the key + // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" + let expected_event_count_at_block = 5; + let returned_event_count = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + &(), + spiritnet_state_root, + spiritnet_event_count_proof_at_block, + ) + .unwrap(); + assert!(returned_event_count == expected_event_count_at_block, "Spiritnet event count returned from the state proof verification should not be different than the pre-computed one."); + } +} diff --git a/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs b/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs new file mode 100644 index 0000000000..018b98481b --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs @@ -0,0 +1,261 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use parity_scale_codec::{Encode, HasCompact}; +use sp_core::{storage::StorageKey, RuntimeDebug, U256}; +use sp_runtime::{generic::Header, traits::BlakeTwo256}; +use sp_std::{marker::PhantomData, vec::Vec}; +use sp_trie::StorageProof; + +use crate::{ + state_proofs::substrate_no_std_port::read_proof_check, + traits::{RelayChainStateInfo, RelayChainStorageInfo}, + utils::OutputOf, +}; + +#[derive(RuntimeDebug)] +pub enum ParachainHeadProofVerifierError { + InvalidMerkleProof, + RequiredLeafNotRevealed, + HeaderDecode, + RelaychainStateRootNotFound, +} + +impl From for u8 { + fn from(value: ParachainHeadProofVerifierError) -> Self { + match value { + ParachainHeadProofVerifierError::InvalidMerkleProof => 0, + ParachainHeadProofVerifierError::RequiredLeafNotRevealed => 1, + ParachainHeadProofVerifierError::HeaderDecode => 2, + ParachainHeadProofVerifierError::RelaychainStateRootNotFound => 3, + } + } +} + +/// Verifier of state proofs that reveal the value of a parachain head at a +/// given relaychain block number. +/// The generic types are the following: +/// * `RelayChainState`: defines the relaychain runtime types relevant for state +/// proof verification, and returns the relaychain runtime's storage key +/// identifying a parachain with a given ID. +pub struct ParachainHeadProofVerifier(PhantomData); + +// Uses the provided `root` to verify the proof. +impl ParachainHeadProofVerifier +where + RelayChainState: RelayChainStorageInfo, + OutputOf: Ord, + RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainState::Key: AsRef<[u8]>, +{ + /// Given a relaychain state root, verify a state proof for the + /// parachain with the provided ID. + #[cfg(not(feature = "runtime-benchmarks"))] + pub fn verify_proof_for_parachain_with_root( + para_id: &RelayChainState::ParaId, + root: &OutputOf<::Hasher>, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> { + use parity_scale_codec::Decode; + + let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) + .map_err(|_| ParachainHeadProofVerifierError::InvalidMerkleProof)?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); + } + let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { + return Err(ParachainHeadProofVerifierError::RequiredLeafNotRevealed); + }; + // TODO: Figure out why RPC call returns 2 bytes in front which we don't need + let mut unwrapped_head = &encoded_head[2..]; + Header::decode(&mut unwrapped_head).map_err(|_| ParachainHeadProofVerifierError::HeaderDecode) + } + + // Ignores any errors returned by the `read_proof_check` function and returns a + // default Header in case of failure. + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_parachain_with_root( + para_id: &RelayChainState::ParaId, + root: &OutputOf<::Hasher>, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> { + use parity_scale_codec::Decode; + + let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) + .unwrap_or_default(); + let encoded_head = if let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) { + encoded_head.clone() + } else { + sp_std::vec![0u8; 3] + }; + let mut unwrapped_head = &encoded_head[2..]; + let header = Header::decode(&mut unwrapped_head).unwrap_or(Header { + number: U256::from(0u64) + .try_into() + .map_err(|_| "Block number should be created from a u64") + .unwrap(), + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }); + Ok(header) + } + + /// Given a relaychain state root provided by the `RelayChainState` + /// generic type, verify a state proof for the parachain with the + /// provided ID. + #[cfg(not(feature = "runtime-benchmarks"))] + pub fn verify_proof_for_parachain( + para_id: &RelayChainState::ParaId, + relay_height: &RelayChainState::BlockNumber, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> + where + RelayChainState: RelayChainStateInfo, + { + let relay_state_root = RelayChainState::state_root_for_block(relay_height) + .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; + Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn verify_proof_for_parachain( + para_id: &RelayChainState::ParaId, + relay_height: &RelayChainState::BlockNumber, + proof: impl IntoIterator>, + ) -> Result, ParachainHeadProofVerifierError> + where + RelayChainState: RelayChainStateInfo, + { + let relay_state_root = RelayChainState::state_root_for_block(relay_height).unwrap_or_default(); + Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) + } +} + +/// Implementor of the [`RelayChainStorageInfo`] trait that return the state +/// root of a relaychain block with a given number by retrieving it from the +/// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the +/// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the +/// ones used by Polkadot-based relaychains. This type cannot be used with +/// relaychains that adopt a different definition for any on those types. +pub struct RelayStateRootsViaRelayStorePallet(PhantomData); + +impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet +where + Runtime: pallet_relay_store::Config, +{ + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // TODO: Access the runtime definition from here, once and if exposed. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } +} + +impl RelayChainStateInfo for RelayStateRootsViaRelayStorePallet +where + Runtime: pallet_relay_store::Config, +{ + fn state_root_for_block(block_height: &Self::BlockNumber) -> Option> { + pallet_relay_store::Pallet::::latest_relay_head_for_block(block_height) + .map(|relay_header| relay_header.relay_parent_storage_root) + } +} + +#[cfg(test)] +mod polkadot_parachain_head_proof_verifier_tests { + use super::*; + + use hex_literal::hex; + use sp_runtime::traits::BlakeTwo256; + + // Polkadot block n: 16_363_919, + // hash 0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39 + struct StaticPolkadotInfoProvider; + + impl RelayChainStorageInfo for StaticPolkadotInfoProvider { + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // Adapted from https://github.com/polytope-labs/substrate-ismp/blob/7fb09da6c7b818a98c25c962fee0ddde8e737306/parachain/src/consensus.rs#L369 + // Used for testing. In production this would be generated from the relay + // runtime definition of the `paras` storage map. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } + } + + impl RelayChainStateInfo for StaticPolkadotInfoProvider { + fn state_root_for_block(_block_height: &Self::BlockNumber) -> Option> { + Some(hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into()) + } + } + + #[test] + fn test_spiritnet_head_proof() { + // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") + let spiritnet_head_proof_at_block = [ + hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), + hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), + hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), + hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), + hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), + ].to_vec(); + // As of query paras::heads(2_086) at block + // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" + // (16_363_919) which results in the key + // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" + // + let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); + let returned_head = ParachainHeadProofVerifier::::verify_proof_for_parachain( + &2_086, + &16_363_919, + spiritnet_head_proof_at_block, + ) + .expect("Parachain head proof verification should not fail."); + assert!(returned_head.encode() == expected_spiritnet_head_at_block, "Parachain head returned from the state proof verification should not be different than the pre-computed one."); + } +} diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-primitives/src/traits.rs similarity index 93% rename from crates/kilt-dip-support/src/traits.rs rename to crates/kilt-dip-primitives/src/traits.rs index 7d0967c797..b96ac7d33a 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-primitives/src/traits.rs @@ -26,25 +26,25 @@ use crate::utils::OutputOf; // TODO: Switch to the `Incrementable` trait once it's added to the root of // `frame_support`. -/// A trait for "bumpable" types, i.e., types that have some notion of order of -/// its members. -pub trait Bump { - /// Bump the type instance to its next value. Overflows are assumed to be - /// taken care of by the type internal logic. - fn bump(&mut self); +/// A trait for "incrementable" types, i.e., types that have some notion of +/// order of its members. +pub trait Incrementable { + /// Increment the type instance to its next value. Overflows are assumed to + /// be taken care of by the type internal logic. + fn increment(&mut self); } -impl Bump for T +impl Incrementable for T where T: CheckedAdd + Zero + One, { - fn bump(&mut self) { + fn increment(&mut self) { *self = self.checked_add(&Self::one()).unwrap_or_else(Self::zero); } } -/// A trait for types that implement some sort of access control logic on the -/// provided input `Call` type. +/// A trait for types that implement access control logic where the call is the +/// controlled resource and access is granted based on the provided info. /// The generic types are the following: /// * `Call`: The type of the call being checked. pub trait DipCallOriginFilter { diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-primitives/src/utils.rs similarity index 100% rename from crates/kilt-dip-support/src/utils.rs rename to crates/kilt-dip-primitives/src/utils.rs diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs deleted file mode 100644 index 017470aa6e..0000000000 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ /dev/null @@ -1,483 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -//! Module to deal with cross-chain state proofs. - -use parity_scale_codec::{Decode, Encode, HasCompact}; -use sp_core::{storage::StorageKey, RuntimeDebug, U256}; -use sp_runtime::generic::Header; -use sp_std::{marker::PhantomData, vec::Vec}; -use sp_trie::StorageProof; - -use crate::utils::OutputOf; - -use substrate_no_std_port::read_proof_check; - -// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 -// Needs to be replaced with its runtime-friendly version when available, or be -// kept up-to-date with upstream. -mod substrate_no_std_port { - use super::*; - - use hash_db::EMPTY_PREFIX; - use parity_scale_codec::Codec; - use sp_core::Hasher; - use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; - use sp_std::collections::btree_map::BTreeMap; - use sp_trie::{HashDBT, MemoryDB}; - - pub(super) fn read_proof_check( - root: H::Out, - proof: StorageProof, - keys: I, - ) -> Result, Option>>, ()> - where - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, - { - let proving_backend = create_proof_check_backend::(root, proof)?; - let mut result = BTreeMap::new(); - for key in keys.into_iter() { - let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; - result.insert(key.as_ref().to_vec(), value); - } - Ok(result) - } - - fn read_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, - key: &[u8], - ) -> Result>, ()> - where - H: Hasher, - H::Out: Ord + Codec, - { - proving_backend.storage(key).map_err(|_| ()) - } - - fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> - where - H: Hasher, - H::Out: Codec, - { - let db = proof.into_memory_db(); - - if db.contains(&root, EMPTY_PREFIX) { - Ok(TrieBackendBuilder::new(db, root).build()) - } else { - Err(()) - } - } -} - -/// Relaychain-related state proof logic. -pub(super) mod relay_chain { - use super::*; - - use sp_runtime::traits::BlakeTwo256; - - use crate::traits::{RelayChainStateInfo, RelayChainStorageInfo}; - - #[derive(RuntimeDebug)] - pub enum ParachainHeadProofVerifierError { - InvalidMerkleProof, - RequiredLeafNotRevealed, - HeaderDecode, - RelaychainStateRootNotFound, - } - - impl From for u8 { - fn from(value: ParachainHeadProofVerifierError) -> Self { - match value { - ParachainHeadProofVerifierError::InvalidMerkleProof => 0, - ParachainHeadProofVerifierError::RequiredLeafNotRevealed => 1, - ParachainHeadProofVerifierError::HeaderDecode => 2, - ParachainHeadProofVerifierError::RelaychainStateRootNotFound => 3, - } - } - } - - /// Verifier of state proofs that reveal the value of a parachain head at a - /// given relaychain block number. - /// The generic types are the following: - /// * `RelayChainState`: defines the relaychain runtime types relevant for - /// state proof verification, and returns the relaychain runtime's storage - /// key identifying a parachain with a given ID. - pub struct ParachainHeadProofVerifier(PhantomData); - - // Uses the provided `root` to verify the proof. - impl ParachainHeadProofVerifier - where - RelayChainState: RelayChainStorageInfo, - OutputOf: Ord, - RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, - RelayChainState::Key: AsRef<[u8]>, - { - /// Given a relaychain state root, verify a state proof for the - /// parachain with the provided ID. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_parachain_with_root( - para_id: &RelayChainState::ParaId, - root: &OutputOf<::Hasher>, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { - let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) - .map_err(|_| ParachainHeadProofVerifierError::InvalidMerkleProof)?; - // TODO: Remove at some point - { - debug_assert!(revealed_leaves.len() == 1usize); - debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); - } - let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { - return Err(ParachainHeadProofVerifierError::RequiredLeafNotRevealed); - }; - // TODO: Figure out why RPC call returns 2 bytes in front which we don't need - let mut unwrapped_head = &encoded_head[2..]; - Header::decode(&mut unwrapped_head).map_err(|_| ParachainHeadProofVerifierError::HeaderDecode) - } - - // Ignores any errors returned by the `read_proof_check` function and returns a - // default Header in case of failure. - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_parachain_with_root( - para_id: &RelayChainState::ParaId, - root: &OutputOf<::Hasher>, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { - let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) - .unwrap_or_default(); - let encoded_head = if let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) { - encoded_head.clone() - } else { - sp_std::vec![0u8; 3] - }; - let mut unwrapped_head = &encoded_head[2..]; - let header = Header::decode(&mut unwrapped_head).unwrap_or(Header { - number: U256::from(0u64) - .try_into() - .map_err(|_| "Block number should be created from a u64") - .unwrap(), - digest: Default::default(), - extrinsics_root: Default::default(), - parent_hash: Default::default(), - state_root: Default::default(), - }); - Ok(header) - } - - /// Given a relaychain state root provided by the `RelayChainState` - /// generic type, verify a state proof for the parachain with the - /// provided ID. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_parachain( - para_id: &RelayChainState::ParaId, - relay_height: &RelayChainState::BlockNumber, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> - where - RelayChainState: RelayChainStateInfo, - { - let relay_state_root = RelayChainState::state_root_for_block(relay_height) - .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; - Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_parachain( - para_id: &RelayChainState::ParaId, - relay_height: &RelayChainState::BlockNumber, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> - where - RelayChainState: RelayChainStateInfo, - { - let relay_state_root = RelayChainState::state_root_for_block(relay_height).unwrap_or_default(); - Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) - } - } - - /// Implementor of the [`RelayChainStorageInfo`] trait that return the state - /// root of a relaychain block with a given number by retrieving it from the - /// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the - /// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the - /// ones used by Polkadot-based relaychains. This type cannot be used with - /// relaychains that adopt a different definition for any on those types. - pub struct RelayStateRootsViaRelayStorePallet(PhantomData); - - impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet - where - Runtime: pallet_relay_store::Config, - { - type BlockNumber = u32; - type Hasher = BlakeTwo256; - type Key = StorageKey; - type ParaId = u32; - - fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { - // TODO: It's not possible to access the runtime definition from here. - let encoded_para_id = para_id.encode(); - let storage_key = [ - frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), - sp_io::hashing::twox_64(&encoded_para_id).as_slice(), - encoded_para_id.as_slice(), - ] - .concat(); - StorageKey(storage_key) - } - } - - impl RelayChainStateInfo for RelayStateRootsViaRelayStorePallet - where - Runtime: pallet_relay_store::Config, - { - fn state_root_for_block(block_height: &Self::BlockNumber) -> Option> { - pallet_relay_store::Pallet::::latest_relay_head_for_block(block_height) - .map(|relay_header| relay_header.relay_parent_storage_root) - } - } - - #[cfg(test)] - mod polkadot_parachain_head_proof_verifier_tests { - use super::*; - - use hex_literal::hex; - use sp_runtime::traits::BlakeTwo256; - - // Polkadot block n: 16_363_919, - // hash 0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39 - struct StaticPolkadotInfoProvider; - - impl RelayChainStorageInfo for StaticPolkadotInfoProvider { - type BlockNumber = u32; - type Hasher = BlakeTwo256; - type Key = StorageKey; - type ParaId = u32; - - fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { - // Adapted from https://github.com/polytope-labs/substrate-ismp/blob/7fb09da6c7b818a98c25c962fee0ddde8e737306/parachain/src/consensus.rs#L369 - // Used for testing. In production this would be generated from the relay - // runtime definition of the `paras` storage map. - let encoded_para_id = para_id.encode(); - let storage_key = [ - frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), - sp_io::hashing::twox_64(&encoded_para_id).as_slice(), - encoded_para_id.as_slice(), - ] - .concat(); - StorageKey(storage_key) - } - } - - impl RelayChainStateInfo for StaticPolkadotInfoProvider { - fn state_root_for_block(_block_height: &Self::BlockNumber) -> Option> { - Some(hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into()) - } - } - - #[test] - fn test_spiritnet_head_proof() { - // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") - let spiritnet_head_proof_at_block = [ - hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), - hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), - hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), - hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), - hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), - ].to_vec(); - // As of query paras::heads(2_086) at block - // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" - // (16_363_919) which results in the key - // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" - // - let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); - let returned_head = ParachainHeadProofVerifier::::verify_proof_for_parachain( - &2_086, - &16_363_919, - spiritnet_head_proof_at_block, - ) - .expect("Parachain head proof verification should not fail."); - assert!(returned_head.encode() == expected_spiritnet_head_at_block, "Parachain head returned from the state proof verification should not be different than the pre-computed one."); - } - } -} - -/// Parachain-related state proof logic. -pub(super) mod parachain { - use super::*; - - use crate::traits::ProviderParachainStorageInfo; - - #[derive(RuntimeDebug)] - pub enum DipIdentityCommitmentProofVerifierError { - InvalidMerkleProof, - RequiredLeafNotRevealed, - CommitmentDecode, - } - - impl From for u8 { - fn from(value: DipIdentityCommitmentProofVerifierError) -> Self { - match value { - DipIdentityCommitmentProofVerifierError::InvalidMerkleProof => 0, - DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed => 1, - DipIdentityCommitmentProofVerifierError::CommitmentDecode => 2, - } - } - } - - /// Verifier of state proofs that reveal the value of the DIP commitment for - /// a given subject on the provider chain. The generic types indicate the - /// following: - /// * `ParaInfo`: defines the provider parachain runtime types relevant for - /// state proof verification, and returns the provider's runtime storage - /// key identifying the identity commitment for a subject with the given - /// identifier. - pub struct DipIdentityCommitmentProofVerifier(PhantomData); - - impl DipIdentityCommitmentProofVerifier - where - ParaInfo: ProviderParachainStorageInfo, - OutputOf: Ord, - ParaInfo::Commitment: Decode, - ParaInfo::Key: AsRef<[u8]>, - { - /// Given a parachain state root, verify a state proof for the - /// commitment of a given subject identifier. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_identifier( - identifier: &ParaInfo::Identifier, - state_root: OutputOf, - proof: impl IntoIterator>, - ) -> Result { - let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = read_proof_check::( - state_root, - storage_proof, - [&dip_commitment_storage_key].iter(), - ) - .map_err(|_| DipIdentityCommitmentProofVerifierError::InvalidMerkleProof)?; - // TODO: Remove at some point - { - debug_assert!(revealed_leaves.len() == 1usize); - debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); - } - let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { - return Err(DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed); - }; - ParaInfo::Commitment::decode(&mut &encoded_commitment[..]) - .map_err(|_| DipIdentityCommitmentProofVerifierError::CommitmentDecode) - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_identifier( - identifier: &ParaInfo::Identifier, - state_root: OutputOf, - proof: impl IntoIterator>, - ) -> Result - where - ParaInfo::Commitment: Default, - { - let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = read_proof_check::( - state_root, - storage_proof, - [&dip_commitment_storage_key].iter(), - ) - .unwrap_or_default(); - let encoded_commitment = - if let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) { - encoded_commitment.clone() - } else { - Vec::default() - }; - let commitment = ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).unwrap_or_default(); - Ok(commitment) - } - } - - #[cfg(test)] - mod spiritnet_test_event_count_value { - use super::*; - - use hex_literal::hex; - use pallet_dip_provider::IdentityCommitmentVersion; - use sp_core::H256; - use sp_runtime::traits::BlakeTwo256; - - // Spiritnet block n: 4_184_668, - // hash 0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5 - struct StaticSpiritnetInfoProvider; - - // We use the `system::eventCount()` storage entry as a unit test here. - impl ProviderParachainStorageInfo for StaticSpiritnetInfoProvider { - type BlockNumber = u32; - // The type of the `eventCount()` storage entry. - type Commitment = u32; - type Hasher = BlakeTwo256; - // Irrelevant for this test here - type Identifier = (); - type Key = StorageKey; - - fn dip_subject_storage_key( - _identifier: &Self::Identifier, - _version: IdentityCommitmentVersion, - ) -> Self::Key { - // system::eventCount() raw storage key - let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); - StorageKey(storage_key) - } - } - - #[test] - fn test_spiritnet_event_count() { - // As of RPC state_getReadProof(" - // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", - // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") - let spiritnet_event_count_proof_at_block = [ - hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), - hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), - hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), - hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), - ].to_vec(); - let spiritnet_state_root: H256 = - hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); - // As of query system::eventCount() at block - // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which - // results in the key - // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" - let expected_event_count_at_block = 5; - let returned_event_count = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - &(), - spiritnet_state_root, - spiritnet_event_count_proof_at_block, - ) - .unwrap(); - assert!(returned_event_count == expected_event_count_at_block, "Spiritnet event count returned from the state proof verification should not be different than the pre-computed one."); - } - } -} diff --git a/dip-template/pallets/pallet-postit/src/lib.rs b/dip-template/pallets/pallet-postit/src/lib.rs index 170797f61e..f0697f78df 100644 --- a/dip-template/pallets/pallet-postit/src/lib.rs +++ b/dip-template/pallets/pallet-postit/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use crate::{ post::{Comment, Post}, - traits::Usernamable, + traits::GetUsername, }; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -52,7 +52,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type MaxTextLength: Get; type OriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; - type OriginSuccess: Usernamable; + type OriginSuccess: GetUsername; type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Username: Encode + Decode + TypeInfo + MaxEncodedLen + Clone + PartialEq + Debug + Default; } diff --git a/dip-template/pallets/pallet-postit/src/traits.rs b/dip-template/pallets/pallet-postit/src/traits.rs index 8a1d20f580..d3c1a2b9a6 100644 --- a/dip-template/pallets/pallet-postit/src/traits.rs +++ b/dip-template/pallets/pallet-postit/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -pub trait Usernamable { +pub trait GetUsername { type Username; fn username(&self) -> Result; diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index ca55d7dcad..f6b7d428d4 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -20,7 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP dip-provider-runtime-template.workspace = true did.workspace = true -kilt-dip-support.workspace = true +kilt-dip-primitives.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-postit.workspace = true @@ -79,7 +79,7 @@ std = [ "scale-info/std", "dip-provider-runtime-template/std", "did/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-postit/std", @@ -123,7 +123,7 @@ std = [ runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "pallet-relay-store/runtime-benchmarks", "runtime-common/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 823fcd37f3..00868b9028 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -20,8 +20,8 @@ use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as ProviderRuntime}; use frame_support::traits::Contains; use frame_system::EnsureSigned; -use kilt_dip_support::{ - traits::DipCallOriginFilter, KiltVersionedSiblingProviderVerifier, RelayStateRootsViaRelayStorePallet, +use kilt_dip_primitives::{ + traits::DipCallOriginFilter, KiltVersionedParachainVerifier, RelayStateRootsViaRelayStorePallet, }; use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_core::ConstU32; @@ -32,17 +32,15 @@ use crate::{weights, AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrig pub type MerkleProofVerifierOutput = >::VerificationResult; /// The verifier logic assumes the provider is a sibling KILT parachain, and /// that a KILT subject can provide DIP proof that reveal at most 10 DID keys -/// and 10 linked accounts. Calls that do not pass the [`DipCallFilter`] will be -/// discarded early on in the verification process. -pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< +/// and 10 linked accounts (defaults provided by the +/// `KiltVersionedParachainVerifier` type). Calls that do not pass the +/// [`DipCallFilter`] will be discarded early on in the verification process. +pub type ProofVerifier = KiltVersionedParachainVerifier< ProviderRuntime, ConstU32<2_000>, RelayStateRootsViaRelayStorePallet, BlakeTwo256, DipCallFilter, - 10, - 10, - 50, >; impl pallet_dip_consumer::Config for Runtime { diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index 5e0319f62e..18d720c1f0 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -19,7 +19,7 @@ use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutput, RuntimeOrigin, Web3Name}; use frame_support::traits::EnsureOrigin; use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; -use pallet_postit::traits::Usernamable; +use pallet_postit::traits::GetUsername; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; @@ -49,7 +49,7 @@ impl EnsureOrigin for EnsureDipOriginAdapter { #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOriginAdapter(DipOrigin); -impl Usernamable for DipOriginAdapter { +impl GetUsername for DipOriginAdapter { type Username = Web3Name; fn username(&self) -> Result { diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index d1f7c7bcde..63b4302ffe 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -21,7 +21,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true kilt-support.workspace = true -kilt-dip-support.workspace = true +kilt-dip-primitives.workspace = true kilt-runtime-api-did.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-deposit-storage.workspace = true @@ -82,7 +82,7 @@ std = [ "scale-info/std", "did/std", "kilt-support/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "kilt-runtime-api-did/std", "kilt-runtime-api-dip-provider/std", "pallet-deposit-storage/std", @@ -127,7 +127,7 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "pallet-deposit-storage/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 49764ce340..a36cbd3944 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -126,12 +126,14 @@ pub mod deposit { fn on_deposit_reclaimed( _namespace: &::Namespace, key: &DepositKeyOf, - _deposit: DepositEntryOf, + deposit: DepositEntryOf, ) -> Result<(), Self::Error> { let (identifier, commitment_version) = <(DidIdentifier, IdentityCommitmentVersion)>::decode(&mut &key[..]) .map_err(|_| CommitmentDepositRemovalHookError::DecodeKey)?; pallet_dip_provider::Pallet::::delete_identity_commitment_storage_entry( &identifier, + // Deposit owner is the only one authorized to remove the deposit. + &deposit.deposit.owner, commitment_version, ) .map_err(|_| { diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index 36b902df5b..ef48f32f41 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -36,9 +36,9 @@ use crate::{BalanceOf, Config, Error, HoldReason, Pallet}; #[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)] pub struct DepositEntry { /// The [`Deposit`] entry. - pub(crate) deposit: Deposit, + pub deposit: Deposit, /// The `Reason` for the deposit. - pub(crate) reason: Reason, + pub reason: Reason, } /// Type implementing the [`DipProviderHooks`] hooks trait by taking a deposit diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index f19f17ab72..69695cfbee 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -19,6 +19,7 @@ sp-keystore = {workspace = true, features = ["std"]} sp-runtime = {workspace = true, features = ["std"]} [dependencies] +cfg-if.workspace = true frame-support.workspace = true frame-system.workspace = true kilt-support.workspace = true diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 148eb88b82..ea6125d7b4 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -38,7 +38,7 @@ pub mod pallet { use super::*; use frame_support::{ - dispatch::{Dispatchable, GetDispatchInfo}, + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{Contains, EnsureOriginWithArg}, Twox64Concat, @@ -93,7 +93,9 @@ pub mod pallet { /// proof. type ProofVerifier: IdentityProofVerifier; /// The aggregated `Call` type. - type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin> + GetDispatchInfo; + type RuntimeCall: Parameter + + Dispatchable::RuntimeOrigin> + + GetDispatchInfo; /// The aggregated `Origin` type, which must include the origin exposed /// by this pallet. type RuntimeOrigin: From> + From<::RuntimeOrigin>; @@ -149,20 +151,24 @@ pub mod pallet { identifier: T::Identifier, proof: IdentityProofOf, call: Box>, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let submitter = T::DispatchOriginCheck::ensure_origin(origin, &identifier)?; ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Filtered); - let mut identity_entry = IdentityEntries::::get(&identifier); - let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( - &*call, - &identifier, - &submitter, - &mut identity_entry, - proof, - ) - .map_err(|e| Error::::InvalidProof(e.into()))?; - IdentityEntries::::mutate(&identifier, |entry| *entry = identity_entry); - let did_origin = DipOrigin { + let proof_verification_result = IdentityEntries::::try_mutate(&identifier, |identity_entry| { + T::ProofVerifier::verify_proof_for_call_against_details( + &*call, + &identifier, + &submitter, + identity_entry, + proof, + ) + .map_err(|e| Error::::InvalidProof(e.into())) + })?; + let did_origin: DipOrigin< + T::Identifier, + T::AccountId, + >::VerificationResult, + > = DipOrigin { identifier, account_address: submitter, details: proof_verification_result, @@ -171,9 +177,13 @@ pub mod pallet { // TODO: Maybe find a nicer way to exclude the call dispatched from the // benchmarks while making sure the call is actually dispatched and passes any // filters the consumer proof verifier has set. - #[cfg(not(feature = "runtime-benchmark"))] - let _ = call.dispatch(did_origin.into()).map_err(|e| e.error)?; - Ok(()) + cfg_if::cfg_if! { + if #[cfg(not(feature = "runtime-benchmark"))] { + call.dispatch(did_origin.into()) + } else { + ().into() + } + } } } } diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index e6eea5e5a8..aff9f53ef5 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -158,30 +158,22 @@ pub mod pallet { T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) .map_err(|error| Error::::IdentityCommitmentGenerator(error.into()))?; - IdentityCommitments::::try_mutate(&identifier, commitment_version, |commitment_entry| { - if let Some(old_commitment) = commitment_entry { - T::ProviderHooks::on_commitment_removed( - &identifier, - &dispatcher, - old_commitment, - commitment_version, - ) - .map_err(|e| Error::::Hook(e.into()))?; - Self::deposit_event(Event::::VersionedIdentityDeleted { - identifier: identifier.clone(), - version: commitment_version, - }); - } - T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) - .map_err(|e| Error::::Hook(e.into()))?; - *commitment_entry = Some(commitment.clone()); - Self::deposit_event(Event::::VersionedIdentityCommitted { - identifier: identifier.clone(), - commitment, - version: commitment_version, - }); - Ok::<_, Error>(()) - })?; + match Self::delete_identity_commitment_storage_entry(&identifier, &dispatcher, commitment_version) { + // Ignore if there was no previous commitment. + Ok(_) | Err(Error::::CommitmentNotFound) => (), + // If a different error is returned, bubble it up. + Err(e) => return Err(e.into()), + }; + + IdentityCommitments::::insert(&identifier, commitment_version, commitment.clone()); + // Call hooks for new commitment. + T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) + .map_err(|e| Error::::Hook(e.into()))?; + Self::deposit_event(Event::::VersionedIdentityCommitted { + identifier: identifier.clone(), + commitment, + version: commitment_version, + }); Ok(()) } @@ -200,11 +192,9 @@ pub mod pallet { ) -> DispatchResult { let dispatcher = T::CommitOriginCheck::ensure_origin(origin, &identifier) .map(|e: ::CommitOrigin| e.submitter())?; - let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); - let commitment = Self::delete_identity_commitment_storage_entry(&identifier, commitment_version)?; - T::ProviderHooks::on_commitment_removed(&identifier, &dispatcher, &commitment, commitment_version) - .map_err(|e| Error::::Hook(e.into()))?; + + Self::delete_identity_commitment_storage_entry(&identifier, &dispatcher, commitment_version)?; Ok(()) } } @@ -212,10 +202,13 @@ pub mod pallet { impl Pallet { pub fn delete_identity_commitment_storage_entry( identifier: &T::Identifier, + dispatcher: &T::AccountId, version: IdentityCommitmentVersion, - ) -> Result, DispatchError> { + ) -> Result, Error> { let commitment = IdentityCommitments::::take(identifier, version).ok_or(Error::::CommitmentNotFound)?; + T::ProviderHooks::on_commitment_removed(identifier, dispatcher, &commitment, version) + .map_err(|e| Error::::Hook(e.into()))?; Self::deposit_event(Event::::VersionedIdentityDeleted { identifier: identifier.clone(), version, diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index cfba19e88b..a330c99ada 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -24,7 +24,7 @@ smallvec.workspace = true attestation.workspace = true did.workspace = true kilt-support.workspace = true -kilt-dip-support.workspace = true +kilt-dip-primitives.workspace = true pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true pallet-web3-names.workspace = true @@ -72,7 +72,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "kilt-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-membership/runtime-benchmarks", @@ -98,7 +98,7 @@ std = [ "frame-system/std", "kilt-asset-dids/std", "kilt-support/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "pallet-did-lookup/std", "pallet-dip-provider/std", "pallet-web3-names/std", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index fbbda7a3f1..9056b7990d 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -19,7 +19,7 @@ use did::did_details::DidDetails; use frame_support::ensure; use frame_system::pallet_prelude::BlockNumberFor; -use kilt_dip_support::merkle::RevealedWeb3Name; +use kilt_dip_primitives::merkle::RevealedWeb3Name; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 15e833a8f9..2ab8cb5bec 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -18,7 +18,7 @@ use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; use frame_system::pallet_prelude::BlockNumberFor; -use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; +use kilt_dip_primitives::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::{ traits::{IdentityCommitmentGenerator, IdentityProvider}, @@ -29,7 +29,7 @@ use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; +use kilt_dip_primitives::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::dip::did::LinkedDidInfoOf; @@ -305,19 +305,18 @@ pub mod v0 { .collect::, _>>()?; match (should_include_web3_name, web3_name_details) { - // If web3name should be included and it exists... + // If web3name should be included and it exists, add to the leaves to be revealed... (true, Some(web3name_details)) => { leaves.push(RevealedDidMerkleProofLeaf::Web3Name( web3name_details.web3_name.clone().into(), web3name_details.claimed_at.into(), )); - Ok(()) } - // ...else if web3name should be included and it DOES NOT exist... - (true, None) => Err(DidMerkleProofError::Web3NameNotFound), - // ...else (if web3name should NOT be included). - (false, _) => Ok(()), - }?; + // ...else if web3name should be included and it DOES NOT exist, return an error... + (true, None) => return Err(DidMerkleProofError::Web3NameNotFound), + // ...else (if web3name should NOT be included), skip. + (false, _) => {} + }; let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); let proof = From d84e40f0a3b76f6c71a1d7ee9020dc64cf7c9f1b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 12 Dec 2023 12:41:48 +0100 Subject: [PATCH 25/28] Fix docs --- crates/kilt-dip-primitives/src/export/parachain.rs | 6 +++--- crates/kilt-dip-primitives/src/export/relaychain.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/kilt-dip-primitives/src/export/parachain.rs b/crates/kilt-dip-primitives/src/export/parachain.rs index f0a6a63fc4..17cb134b88 100644 --- a/crates/kilt-dip-primitives/src/export/parachain.rs +++ b/crates/kilt-dip-primitives/src/export/parachain.rs @@ -150,13 +150,13 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: [OPTIONAL] Max number of DID keys that the +/// * `MAX_REVEALED_KEYS_COUNT`: **OPTIONAL** Max number of DID keys that the /// verifier will accept revealed as part of the DIP identity proof. It /// defaults to **10**. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: [OPTIONAL] Max number of linked accounts +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: **OPTIONAL** Max number of linked accounts /// that the verifier will accept revealed as part of the DIP identity proof. /// It defaults to **10**. -/// * `MAX_DID_SIGNATURE_DURATION`: [OPTIONAL] Max number of blocks a +/// * `MAX_DID_SIGNATURE_DURATION`: **OPTIONAL** Max number of blocks a /// cross-chain DID signature is considered fresh. It defaults to **50**. /// /// It specializes the [`GenericVersionedParachainVerifier`] diff --git a/crates/kilt-dip-primitives/src/export/relaychain.rs b/crates/kilt-dip-primitives/src/export/relaychain.rs index ce75fc5cfa..1799f3129d 100644 --- a/crates/kilt-dip-primitives/src/export/relaychain.rs +++ b/crates/kilt-dip-primitives/src/export/relaychain.rs @@ -137,13 +137,13 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: [OPTIONAL] Max number of DID keys that the +/// * `MAX_REVEALED_KEYS_COUNT`: **OPTIONAL** Max number of DID keys that the /// verifier will accept revealed as part of the DIP identity proof. It /// defaults to **10**. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: [OPTIONAL] Max number of linked accounts +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: **OPTIONAL** Max number of linked accounts /// that the verifier will accept revealed as part of the DIP identity proof. /// It defaults to **10**. -/// * `MAX_DID_SIGNATURE_DURATION`: [OPTIONAL] Max number of blocks a +/// * `MAX_DID_SIGNATURE_DURATION`: **OPTIONAL** Max number of blocks a /// cross-chain DID signature is considered fresh. It defaults to **50**. /// /// It specializes the [`GenericVersionedRelaychainVerifier`] From c79ea0f36cbb9ad787db6ab8b0d7b747acde1884 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 12 Dec 2023 12:50:33 +0100 Subject: [PATCH 26/28] Rename export to verifier --- crates/kilt-dip-primitives/src/lib.rs | 10 +++------- .../src/{export => verifier}/common.rs | 0 .../src/{export => verifier}/mod.rs | 0 .../src/{export => verifier}/parachain.rs | 2 +- .../src/{export => verifier}/relaychain.rs | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) rename crates/kilt-dip-primitives/src/{export => verifier}/common.rs (100%) rename crates/kilt-dip-primitives/src/{export => verifier}/mod.rs (100%) rename crates/kilt-dip-primitives/src/{export => verifier}/parachain.rs (99%) rename crates/kilt-dip-primitives/src/{export => verifier}/relaychain.rs (99%) diff --git a/crates/kilt-dip-primitives/src/lib.rs b/crates/kilt-dip-primitives/src/lib.rs index 04c9f17a25..f8abdd6aad 100644 --- a/crates/kilt-dip-primitives/src/lib.rs +++ b/crates/kilt-dip-primitives/src/lib.rs @@ -31,13 +31,9 @@ pub mod merkle; pub mod state_proofs; pub mod traits; pub mod utils; +pub mod verifier; -mod export; - -pub use export::*; -pub use state_proofs::{ - parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, - relaychain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, -}; +pub use state_proofs::relaychain::RelayStateRootsViaRelayStorePallet; pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; pub use utils::BoundedBlindedValue; +pub use verifier::*; diff --git a/crates/kilt-dip-primitives/src/export/common.rs b/crates/kilt-dip-primitives/src/verifier/common.rs similarity index 100% rename from crates/kilt-dip-primitives/src/export/common.rs rename to crates/kilt-dip-primitives/src/verifier/common.rs diff --git a/crates/kilt-dip-primitives/src/export/mod.rs b/crates/kilt-dip-primitives/src/verifier/mod.rs similarity index 100% rename from crates/kilt-dip-primitives/src/export/mod.rs rename to crates/kilt-dip-primitives/src/verifier/mod.rs diff --git a/crates/kilt-dip-primitives/src/export/parachain.rs b/crates/kilt-dip-primitives/src/verifier/parachain.rs similarity index 99% rename from crates/kilt-dip-primitives/src/export/parachain.rs rename to crates/kilt-dip-primitives/src/verifier/parachain.rs index 17cb134b88..c8b7151729 100644 --- a/crates/kilt-dip-primitives/src/export/parachain.rs +++ b/crates/kilt-dip-primitives/src/verifier/parachain.rs @@ -471,10 +471,10 @@ pub mod v0 { use crate::{ did::{verify_did_signature_for_call, RevealedDidKeysAndSignature}, - export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, merkle::verify_dip_merkle_proof, state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relaychain::ParachainHeadProofVerifier}, traits::ProviderParachainStorageInfo, + verifier::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, }; /// The expected format of a cross-chain DIP identity proof when the diff --git a/crates/kilt-dip-primitives/src/export/relaychain.rs b/crates/kilt-dip-primitives/src/verifier/relaychain.rs similarity index 99% rename from crates/kilt-dip-primitives/src/export/relaychain.rs rename to crates/kilt-dip-primitives/src/verifier/relaychain.rs index 1799f3129d..a68dd497f2 100644 --- a/crates/kilt-dip-primitives/src/export/relaychain.rs +++ b/crates/kilt-dip-primitives/src/verifier/relaychain.rs @@ -497,7 +497,6 @@ pub mod v0 { did::{ verify_did_signature_for_call, RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifierError, }, - export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, merkle::{ verify_dip_merkle_proof, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves, @@ -511,6 +510,7 @@ pub mod v0 { ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, + verifier::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, }; /// The expected format of a cross-chain DIP identity proof when the From 7d538cfba1eb819914e82bbb39ce943058f5d1c1 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 12 Dec 2023 14:10:26 +0000 Subject: [PATCH 27/28] feat: add DIP to Peregrine (#594) Based on top of https://github.com/KILTprotocol/kilt-node/pull/494. Mirrors the behaviour in the provider template, with the following differences: 1. The deposit is calculated taking into account the actual length of an identity commitment. 2. The key that is stored in the pallet deposit storage is an enum, which allows more keys to be added in the future --- Cargo.lock | 3 + dip-template/runtimes/dip-provider/src/dip.rs | 7 +- pallets/pallet-deposit-storage/Cargo.toml | 2 +- pallets/pallet-deposit-storage/src/deposit.rs | 28 ++-- pallets/pallet-dip-consumer/Cargo.toml | 2 + pallets/pallet-relay-store/Cargo.toml | 1 + runtimes/common/src/constants.rs | 16 ++ runtimes/peregrine/Cargo.toml | 10 ++ runtimes/peregrine/src/dip/deposit.rs | 149 ++++++++++++++++++ runtimes/peregrine/src/dip/mod.rs | 69 ++++++++ runtimes/peregrine/src/dip/runtime_api.rs | 54 +++++++ runtimes/peregrine/src/lib.rs | 21 +++ runtimes/peregrine/src/tests.rs | 18 +++ 13 files changed, 365 insertions(+), 15 deletions(-) create mode 100644 runtimes/peregrine/src/dip/deposit.rs create mode 100644 runtimes/peregrine/src/dip/mod.rs create mode 100644 runtimes/peregrine/src/dip/runtime_api.rs diff --git a/Cargo.lock b/Cargo.lock index 0c6b1e9f21..d0dd8e8942 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7655,6 +7655,7 @@ dependencies = [ "frame-try-runtime", "hex-literal 0.3.4", "kilt-runtime-api-did", + "kilt-runtime-api-dip-provider", "kilt-runtime-api-public-credentials", "kilt-runtime-api-staking", "kilt-support", @@ -7665,7 +7666,9 @@ dependencies = [ "pallet-collective", "pallet-configuration", "pallet-democracy", + "pallet-deposit-storage", "pallet-did-lookup", + "pallet-dip-provider", "pallet-indices", "pallet-inflation", "pallet-membership", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index a36cbd3944..5564abc13b 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -96,8 +96,11 @@ pub mod deposit { /// The additional logic to execute whenever a deposit is removed by its /// owner directly via the [`pallet_deposit_storage::Pallet`] pallet. - pub type DepositCollectorHooks = - FixedDepositCollectorViaDepositsPallet>; + pub type DepositCollectorHooks = FixedDepositCollectorViaDepositsPallet< + DipProviderDepositNamespace, + ConstU128, + (DidIdentifier, IdentityCommitmentVersion), + >; pub enum CommitmentDepositRemovalHookError { DecodeKey, diff --git a/pallets/pallet-deposit-storage/Cargo.toml b/pallets/pallet-deposit-storage/Cargo.toml index 21f4ca1653..1865c4ea8f 100644 --- a/pallets/pallet-deposit-storage/Cargo.toml +++ b/pallets/pallet-deposit-storage/Cargo.toml @@ -49,7 +49,6 @@ std = [ "frame-benchmarking?/std", "pallet-balances?/std", ] - runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", @@ -59,3 +58,4 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "pallet-balances/runtime-benchmarks", ] +try-runtime = [] diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index ef48f32f41..927300aad0 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -44,8 +44,8 @@ pub struct DepositEntry { /// Type implementing the [`DipProviderHooks`] hooks trait by taking a deposit /// whenever an identity commitment is stored, and releasing the deposit /// whenever an identity commitment is removed. -pub struct FixedDepositCollectorViaDepositsPallet( - PhantomData<(DepositsNamespace, FixedDepositAmount)>, +pub struct FixedDepositCollectorViaDepositsPallet( + PhantomData<(DepositsNamespace, FixedDepositAmount, DepositKey)>, ); pub enum FixedDepositCollectorViaDepositsPalletError { @@ -68,12 +68,13 @@ impl From for u16 { } } -impl DipProviderHooks - for FixedDepositCollectorViaDepositsPallet +impl DipProviderHooks + for FixedDepositCollectorViaDepositsPallet where Runtime: pallet_dip_provider::Config + Config, DepositsNamespace: Get, FixedDepositAmount: Get>, + DepositKey: From<(Runtime::Identifier, IdentityCommitmentVersion)> + Encode, { type Error = u16; @@ -84,14 +85,17 @@ where version: IdentityCommitmentVersion, ) -> Result<(), Self::Error> { let namespace = DepositsNamespace::get(); - let key = (identifier, version).encode().try_into().map_err(|_| { - log::error!( - "Failed to convert tuple ({:#?}, {version}) to BoundedVec with max length {}", - identifier, - Runtime::MaxKeyLength::get() - ); - FixedDepositCollectorViaDepositsPalletError::Internal - })?; + let key = DepositKey::from((identifier.clone(), version)) + .encode() + .try_into() + .map_err(|_| { + log::error!( + "Failed to convert tuple ({:#?}, {version}) to BoundedVec with max length {}", + identifier, + Runtime::MaxKeyLength::get() + ); + FixedDepositCollectorViaDepositsPalletError::Internal + })?; let deposit_entry = DepositEntry { deposit: Deposit { amount: FixedDepositAmount::get(), diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index 69695cfbee..ac4758d3f1 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -53,3 +53,5 @@ std = [ "frame-benchmarking?/std", "sp-runtime?/std", ] + +try-runtime = [] diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml index 6cec553a96..724b327558 100644 --- a/pallets/pallet-relay-store/Cargo.toml +++ b/pallets/pallet-relay-store/Cargo.toml @@ -53,3 +53,4 @@ runtime-benchmarks = [ "frame-benchmarking", "sp-runtime/runtime-benchmarks", ] +try-runtime = [] diff --git a/runtimes/common/src/constants.rs b/runtimes/common/src/constants.rs index a37b989b58..0ad68f6c91 100644 --- a/runtimes/common/src/constants.rs +++ b/runtimes/common/src/constants.rs @@ -166,6 +166,22 @@ pub mod delegation { } } +pub mod deposit_storage { + // Keys is an enum with a single variant (DidIdentifier, + // IdentityCommitmentVersion) which is 32 + 2 = 34 bytes. Adding the + // discriminant byte, it totals to 35 bytes. + pub const MAX_DEPOSIT_PALLET_KEY_LENGTH: u32 = 35; +} + +pub mod dip_provider { + use super::*; + + pub const MAX_LINKED_ACCOUNTS: u32 = 10; + // Commitment are 32-byte hashes. + pub const MAX_COMMITMENT_BYTE_LENGTH: u32 = 32; + pub const COMMITMENT_DEPOSIT: Balance = deposit(1, MAX_COMMITMENT_BYTE_LENGTH); +} + pub mod staking { use super::*; diff --git a/runtimes/peregrine/Cargo.toml b/runtimes/peregrine/Cargo.toml index e4ee385029..288f22db56 100644 --- a/runtimes/peregrine/Cargo.toml +++ b/runtimes/peregrine/Cargo.toml @@ -36,9 +36,12 @@ attestation.workspace = true ctype.workspace = true delegation.workspace = true did.workspace = true +kilt-runtime-api-dip-provider.workspace = true kilt-support.workspace = true pallet-configuration.workspace = true +pallet-deposit-storage.workspace = true pallet-did-lookup.workspace = true +pallet-dip-provider.workspace = true pallet-inflation.workspace = true pallet-web3-names.workspace = true pallet-migration.workspace = true @@ -129,7 +132,9 @@ runtime-benchmarks = [ "pallet-collective/runtime-benchmarks", "pallet-configuration/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", + "pallet-deposit-storage/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", + "pallet-dip-provider/runtime-benchmarks", "pallet-indices/runtime-benchmarks", "pallet-inflation/runtime-benchmarks", "pallet-membership/runtime-benchmarks", @@ -175,6 +180,7 @@ std = [ "frame-system/std", "frame-try-runtime?/std", "kilt-runtime-api-did/std", + "kilt-runtime-api-dip-provider/std", "kilt-runtime-api-public-credentials/std", "kilt-runtime-api-staking/std", "kilt-support/std", @@ -186,7 +192,9 @@ std = [ "pallet-collective/std", "pallet-configuration/std", "pallet-democracy/std", + "pallet-deposit-storage/std", "pallet-did-lookup/std", + "pallet-dip-provider/std", "pallet-indices/std", "pallet-inflation/std", "pallet-membership/std", @@ -249,7 +257,9 @@ try-runtime = [ "pallet-collective/try-runtime", "pallet-configuration/try-runtime", "pallet-democracy/try-runtime", + "pallet-deposit-storage/try-runtime", "pallet-did-lookup/try-runtime", + "pallet-dip-provider/try-runtime", "pallet-indices/try-runtime", "pallet-inflation/try-runtime", "pallet-membership/try-runtime", diff --git a/runtimes/peregrine/src/dip/deposit.rs b/runtimes/peregrine/src/dip/deposit.rs new file mode 100644 index 0000000000..4276451390 --- /dev/null +++ b/runtimes/peregrine/src/dip/deposit.rs @@ -0,0 +1,149 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use crate::{DidIdentifier, Runtime}; +use frame_support::traits::Get; +use pallet_deposit_storage::{ + traits::DepositStorageHooks, DepositEntryOf, DepositKeyOf, FixedDepositCollectorViaDepositsPallet, +}; +use pallet_dip_provider::IdentityCommitmentVersion; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use runtime_common::constants::dip_provider::COMMITMENT_DEPOSIT; +use scale_info::TypeInfo; +use sp_core::{ConstU128, RuntimeDebug}; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum DepositNamespace { + DipProvider, +} + +/// The namespace to use in the [`pallet_deposit_storage::Pallet`] to store +/// all deposits related to DIP commitments. +pub struct DipProviderDepositNamespace; + +impl Get for DipProviderDepositNamespace { + fn get() -> DepositNamespace { + DepositNamespace::DipProvider + } +} + +/// The various different keys that can be stored in the storage-tracking +/// pallet. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum DepositKey { + DipProvider { + identifier: DidIdentifier, + version: IdentityCommitmentVersion, + }, +} + +impl From<(DidIdentifier, IdentityCommitmentVersion)> for DepositKey { + fn from((identifier, version): (DidIdentifier, IdentityCommitmentVersion)) -> Self { + Self::DipProvider { identifier, version } + } +} + +/// The additional logic to execute whenever a deposit is removed by its +/// owner directly via the [`pallet_deposit_storage::Pallet`] pallet. +pub type DepositCollectorHooks = + FixedDepositCollectorViaDepositsPallet, DepositKey>; + +pub enum CommitmentDepositRemovalHookError { + DecodeKey, + Internal, +} + +impl From for u16 { + fn from(value: CommitmentDepositRemovalHookError) -> Self { + match value { + CommitmentDepositRemovalHookError::DecodeKey => 0, + CommitmentDepositRemovalHookError::Internal => u16::MAX, + } + } +} + +/// The logic to execute whenever an identity commitment is generated and +/// stored in the [`pallet_dip_provider::Pallet`] pallet. +/// +/// Upon storing and removing identity commitments, this hook will reserve +/// or release deposits from the [`pallet_deposit_storage::Pallet`] pallet. +pub struct DepositHooks; + +impl DepositStorageHooks for DepositHooks { + type Error = CommitmentDepositRemovalHookError; + + fn on_deposit_reclaimed( + _namespace: &::Namespace, + key: &DepositKeyOf, + deposit: DepositEntryOf, + ) -> Result<(), Self::Error> { + let DepositKey::DipProvider { identifier, version } = + DepositKey::decode(&mut &key[..]).map_err(|_| CommitmentDepositRemovalHookError::DecodeKey)?; + pallet_dip_provider::Pallet::::delete_identity_commitment_storage_entry( + &identifier, + // Deposit owner is the only one authorized to remove the deposit. + &deposit.deposit.owner, + version, + ) + .map_err(|_| { + log::error!( + "Should not fail to remove commitment for identifier {:#?} and version {version}", + identifier + ); + CommitmentDepositRemovalHookError::Internal + })?; + Ok(()) + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct PalletDepositStorageBenchmarkHooks; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_deposit_storage::traits::BenchmarkHooks for PalletDepositStorageBenchmarkHooks { + fn pre_reclaim_deposit() -> ( + ::AccountId, + ::Namespace, + sp_runtime::BoundedVec::MaxKeyLength>, + ) { + let submitter = runtime_common::AccountId::from([100u8; 32]); + let namespace = DepositNamespace::DipProvider; + let did_identifier = DidIdentifier::from([200u8; 32]); + let commitment_version = 0u16; + let key: DepositKeyOf = (did_identifier.clone(), 0) + .encode() + .try_into() + .expect("Should not fail to create a key for a DIP commitment."); + + pallet_dip_provider::IdentityCommitments::::insert( + &did_identifier, + commitment_version, + ::Hash::default(), + ); + + assert!(pallet_dip_provider::IdentityCommitments::::get(did_identifier, commitment_version).is_some()); + + (submitter, namespace, key) + } + + fn post_reclaim_deposit() { + let did_identifier = DidIdentifier::from([200u8; 32]); + let commitment_version = 0u16; + assert!(pallet_dip_provider::IdentityCommitments::::get(did_identifier, commitment_version).is_none()); + } +} diff --git a/runtimes/peregrine/src/dip/mod.rs b/runtimes/peregrine/src/dip/mod.rs new file mode 100644 index 0000000000..522def8fda --- /dev/null +++ b/runtimes/peregrine/src/dip/mod.rs @@ -0,0 +1,69 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::{DidRawOrigin, EnsureDidOrigin}; +use frame_system::EnsureSigned; +use runtime_common::{ + constants::{deposit_storage::MAX_DEPOSIT_PALLET_KEY_LENGTH, dip_provider::MAX_LINKED_ACCOUNTS}, + dip::{did::LinkedDidInfoProvider, merkle::DidMerkleRootGenerator}, + AccountId, DidIdentifier, +}; +use sp_core::ConstU32; + +use crate::{ + dip::deposit::{DepositCollectorHooks, DepositHooks, DepositNamespace}, + Balances, Runtime, RuntimeEvent, RuntimeHoldReason, +}; + +pub(crate) mod deposit; +pub(crate) mod runtime_api; + +impl pallet_dip_provider::Config for Runtime { + // Only DID origins can submit the commitment identity tx, which will go through + // only if the DID in the origin matches the identifier specified in the tx. + type CommitOriginCheck = EnsureDidOrigin; + type CommitOrigin = DidRawOrigin; + type Identifier = DidIdentifier; + // The identity commitment is defined as the Merkle root of the linked identity + // info, as specified by the [`LinkedDidInfoProvider`]. + type IdentityCommitmentGenerator = DidMerkleRootGenerator; + // Identity info is defined as the collection of DID keys, linked accounts, and + // the optional web3name of a given DID subject. + type IdentityProvider = LinkedDidInfoProvider; + type ProviderHooks = DepositCollectorHooks; + type RuntimeEvent = RuntimeEvent; + // TODO: Change after benchmarks + type WeightInfo = (); +} + +impl pallet_deposit_storage::Config for Runtime { + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHooks = deposit::PalletDepositStorageBenchmarkHooks; + // Any signed origin can submit the tx, which will go through only if the + // deposit payer matches the signed origin. + type CheckOrigin = EnsureSigned; + // The balances pallet is used to reserve/unreserve tokens. + type Currency = Balances; + type DepositHooks = DepositHooks; + type MaxKeyLength = ConstU32; + type Namespace = DepositNamespace; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + // TODO: Change after benchmarks + type WeightInfo = (); +} diff --git a/runtimes/peregrine/src/dip/runtime_api.rs b/runtimes/peregrine/src/dip/runtime_api.rs new file mode 100644 index 0000000000..dc240b612b --- /dev/null +++ b/runtimes/peregrine/src/dip/runtime_api.rs @@ -0,0 +1,54 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use did::KeyIdOf; +use pallet_did_lookup::linkable_account::LinkableAccountId; +use pallet_dip_provider::IdentityCommitmentVersion; +use parity_scale_codec::{Decode, Encode}; +use runtime_common::{ + dip::{did::LinkedDidInfoProviderError, merkle::DidMerkleProofError}, + DidIdentifier, +}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +use crate::Runtime; + +/// Parameters for a DIP proof request. +#[derive(Encode, Decode, TypeInfo)] +pub struct DipProofRequest { + /// The subject identifier for which to generate the DIP proof. + pub(crate) identifier: DidIdentifier, + /// The DIP version. + pub(crate) version: IdentityCommitmentVersion, + /// The DID key IDs of the subject's DID Document to reveal in the DIP + /// proof. + pub(crate) keys: Vec>, + /// The list of accounts linked to the subject's DID to reveal in the + /// DIP proof. + pub(crate) accounts: Vec, + /// A flag indicating whether the web3name claimed by the DID subject + /// should revealed in the DIP proof. + pub(crate) should_include_web3_name: bool, +} + +#[derive(Encode, Decode, TypeInfo)] +pub enum DipProofError { + IdentityProvider(LinkedDidInfoProviderError), + MerkleProof(DidMerkleProofError), +} diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index f92550a3a1..fcf76aa168 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -59,6 +59,7 @@ use runtime_common::{ assets::{AssetDid, PublicCredentialsFilter}, authorization::{AuthorizationId, PalletAuthorize}, constants::{self, UnvestedFundsAllowedWithdrawReasons, EXISTENTIAL_DEPOSIT, KILT}, + dip::merkle::{CompleteMerkleProof, DidMerkleProofOf, DidMerkleRootGenerator}, errors::PublicCredentialsApiError, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, @@ -78,6 +79,7 @@ pub use sp_runtime::BuildStorage; #[cfg(test)] mod tests; +mod dip; mod weights; mod xcm_config; @@ -777,8 +779,10 @@ impl InstanceFilter for ProxyType { | RuntimeCall::Ctype(..) | RuntimeCall::Delegation(..) | RuntimeCall::Democracy(..) + | RuntimeCall::DepositStorage(..) | RuntimeCall::Did(..) | RuntimeCall::DidLookup(..) + | RuntimeCall::DipProvider(..) | RuntimeCall::Indices( // Excludes `force_transfer`, and `transfer` pallet_indices::Call::claim { .. } @@ -830,6 +834,7 @@ impl InstanceFilter for ProxyType { | delegation::Call::change_deposit_owner { .. } ) | RuntimeCall::Democracy(..) + // Excludes `DepositStorage` | RuntimeCall::Did( // Excludes `reclaim_deposit` did::Call::add_key_agreement_key { .. } @@ -856,6 +861,7 @@ impl InstanceFilter for ProxyType { | pallet_did_lookup::Call::update_deposit { .. } | pallet_did_lookup::Call::change_deposit_owner { .. } ) + | RuntimeCall::DipProvider(..) | RuntimeCall::Indices(..) | RuntimeCall::Multisig(..) | RuntimeCall::ParachainStaking(..) @@ -1008,6 +1014,8 @@ construct_runtime! { Web3Names: pallet_web3_names = 68, PublicCredentials: public_credentials = 69, Migration: pallet_migration = 70, + DipProvider: pallet_dip_provider = 71, + DepositStorage: pallet_deposit_storage = 72, // Parachains pallets. Start indices at 80 to leave room. @@ -1051,6 +1059,7 @@ impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall RuntimeCall::Attestation { .. } => Ok(did::DidVerificationKeyRelationship::AssertionMethod), RuntimeCall::Ctype { .. } => Ok(did::DidVerificationKeyRelationship::AssertionMethod), RuntimeCall::Delegation { .. } => Ok(did::DidVerificationKeyRelationship::CapabilityDelegation), + RuntimeCall::DipProvider { .. } => Ok(did::DidVerificationKeyRelationship::Authentication), // DID creation is not allowed through the DID proxy. RuntimeCall::Did(did::Call::create { .. }) => Err(did::RelationshipDeriveError::NotCallableByDid), RuntimeCall::Did { .. } => Ok(did::DidVerificationKeyRelationship::Authentication), @@ -1147,6 +1156,8 @@ mod benches { [public_credentials, PublicCredentials] [pallet_xcm, PolkadotXcm] [pallet_migration, Migration] + [pallet_dip_provider, DipProvider] + [pallet_deposit_storage, DepositStorage] [frame_benchmarking::baseline, Baseline::] ); } @@ -1413,6 +1424,16 @@ impl_runtime_apis! { } } + impl kilt_runtime_api_dip_provider::DipProvider>, dip::runtime_api::DipProofError> for Runtime { + fn generate_proof(request: dip::runtime_api::DipProofRequest) -> Result>, dip::runtime_api::DipProofError> { + use pallet_dip_provider::traits::IdentityProvider; + + let identity_details = pallet_dip_provider::IdentityProviderOf::::retrieve(&request.identifier).map_err(dip::runtime_api::DipProofError::IdentityProvider)?; + + DidMerkleRootGenerator::::generate_proof(&identity_details, request.version, request.keys.iter(), request.should_include_web3_name, request.accounts.iter()).map_err(dip::runtime_api::DipProofError::MerkleProof) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/runtimes/peregrine/src/tests.rs b/runtimes/peregrine/src/tests.rs index 5eab813d96..14670bf679 100644 --- a/runtimes/peregrine/src/tests.rs +++ b/runtimes/peregrine/src/tests.rs @@ -17,6 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use frame_support::{traits::Currency, BoundedVec}; +use pallet_dip_provider::IdentityCommitmentOf; use parity_scale_codec::MaxEncodedLen; use did::DeriveDidCallAuthorizationVerificationKeyRelationship; @@ -26,8 +27,10 @@ use pallet_web3_names::{Web3NameOf, Web3OwnershipOf}; use runtime_common::{ constants::{ attestation::MAX_ATTESTATION_BYTE_LENGTH, + deposit_storage::MAX_DEPOSIT_PALLET_KEY_LENGTH, did::{MAX_KEY_LENGTH, MAX_SERVICE_ENDPOINT_BYTE_LENGTH}, did_lookup::MAX_CONNECTION_BYTE_LENGTH, + dip_provider::MAX_COMMITMENT_BYTE_LENGTH, public_credentials::MAX_PUBLIC_CREDENTIAL_STORAGE_LENGTH, web3_names::MAX_NAME_BYTE_LENGTH, MAX_INDICES_BYTE_LENGTH, @@ -35,6 +38,8 @@ use runtime_common::{ AccountId, BlockNumber, }; +use crate::dip::deposit::DepositKey; + use super::{Runtime, RuntimeCall}; #[test] @@ -118,6 +123,19 @@ fn public_credentials_storage_sizes() { ) } +#[test] +fn pallet_deposit_storage_max_key_length() { + assert_eq!(DepositKey::max_encoded_len(), MAX_DEPOSIT_PALLET_KEY_LENGTH as usize) +} + +#[test] +fn pallet_dip_provider_commitment_max_length() { + assert_eq!( + IdentityCommitmentOf::::max_encoded_len(), + MAX_COMMITMENT_BYTE_LENGTH as usize + ) +} + #[test] fn test_derive_did_verification_relation_ctype() { let c1 = RuntimeCall::Ctype(ctype::Call::add { From 9365b82d7ec5f311090fb5c899dcad3bff4d261c Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 12 Dec 2023 15:31:07 +0000 Subject: [PATCH 28/28] chore: add DIP templates Docker images (#595) Partially fixes https://github.com/KILTprotocol/ticket/issues/3051. Needed for easier DIP-SDK integration tests. Binaries will have to be compiled and exported in a different PR. --- .gitlab-ci.yml | 2 ++ .maintain/build-image.sh | 16 ++++++++++++++++ .maintain/push-image.sh | 11 +++++++++++ 3 files changed, 29 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b474a0a3d5..e491cf2026 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,6 +42,8 @@ build: variables: DOCKER_HUB_PARACHAIN: "kiltprotocol/kilt-node" DOCKER_HUB_STANDALONE: "kiltprotocol/standalone-node" + DOCKER_HUB_DIP_PROVIDER_TEMPLATE: "kiltprotocol/dip-provider-node-template" + DOCKER_HUB_DIP_CONSUMER_TEMPLATE: "kiltprotocol/dip-consumer-node-template" before_script: - aws --version - docker --version diff --git a/.maintain/build-image.sh b/.maintain/build-image.sh index 2222482734..b179b7928f 100755 --- a/.maintain/build-image.sh +++ b/.maintain/build-image.sh @@ -22,3 +22,19 @@ docker build \ --build-arg NODE_TYPE=standalone-node \ -t local/standalone-node:$target_tag \ . + +# build DIP provider and consumer templates +PROVIDER_BIN_NAME="dip-provider-node-template" +docker build \ + --cache-from $AWS_REGISTRY/kilt-parachain/collator:builder \ + --cache-from $AWS_REGISTRY/$PROVIDER_BIN_NAME:$target_tag \ + --build-arg NODE_TYPE=$PROVIDER_BIN_NAME \ + -t local/$PROVIDER_BIN_NAME:$target_tag \ + . +CONSUMER_BIN_NAME="dip-consumer-node-template" +docker build \ + --cache-from $AWS_REGISTRY/kilt-parachain/collator:builder \ + --cache-from $AWS_REGISTRY/$CONSUMER_BIN_NAME:$target_tag \ + --build-arg NODE_TYPE=$CONSUMER_BIN_NAME \ + -t local/$CONSUMER_BIN_NAME:$target_tag \ + . diff --git a/.maintain/push-image.sh b/.maintain/push-image.sh index 4bb38e5e89..ba46da0ea1 100755 --- a/.maintain/push-image.sh +++ b/.maintain/push-image.sh @@ -3,16 +3,27 @@ source_tag=$1 target_tag=$2 +PROVIDER_BIN_NAME="dip-provider-node-template" +CONSUMER_BIN_NAME="dip-consumer-node-template" + # publish to docker hub docker tag local/standalone-node:$source_tag ${DOCKER_HUB_STANDALONE}:$target_tag docker tag local/kilt-node:$source_tag ${DOCKER_HUB_PARACHAIN}:$target_tag +docker tag local/$PROVIDER_BIN_NAME:$source_tag ${DOCKER_HUB_DIP_PROVIDER_TEMPLATE}:$target_tag +docker tag local/$CONSUMER_BIN_NAME:$source_tag ${DOCKER_HUB_DIP_CONSUMER_TEMPLATE}:$target_tag docker push ${DOCKER_HUB_STANDALONE}:$target_tag docker push ${DOCKER_HUB_PARACHAIN}:$target_tag +docker push ${DOCKER_HUB_DIP_PROVIDER_TEMPLATE}:$target_tag +docker push ${DOCKER_HUB_DIP_CONSUMER_TEMPLATE}:$target_tag # publish to AWS docker tag local/standalone-node:$source_tag $AWS_REGISTRY/kilt/prototype-chain:$target_tag docker tag local/kilt-node:$source_tag $AWS_REGISTRY/kilt-parachain/collator:$target_tag +docker tag local/$PROVIDER_BIN_NAME:$source_tag $AWS_REGISTRY/$PROVIDER_BIN_NAME:$target_tag +docker tag local/$CONSUMER_BIN_NAME:$source_tag $AWS_REGISTRY/$CONSUMER_BIN_NAME:$target_tag docker push $AWS_REGISTRY/kilt/prototype-chain:$target_tag docker push $AWS_REGISTRY/kilt-parachain/collator:$target_tag +docker push $AWS_REGISTRY/$PROVIDER_BIN_NAME:$target_tag +docker push $AWS_REGISTRY/$CONSUMER_BIN_NAME:$target_tag