diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 08191dd51..07675f22f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -30,17 +30,20 @@ Please select the branch type you are merging and fill in the relevant template. - [ ] Change has been tested locally. - [ ] Change adds / updates tests if applicable. - [ ] Changelog doc updated. +- [ ] `spec_version` has been incremented. +- [ ] `network-relayer`'s [events](https://github.com/Cerebellum-Network/network-relayer/blob/dev-cere/shared/substrate/events.go) have been updated according to the blockchain events if applicable. +- [ ] All CI checks have been passed successfully ## Checklist for Hotfix -- [ ] Change has been deployed to Testnet. -- [ ] Change has been tested in Testnet. - [ ] Changelog has been updated. - [ ] Crate version has been updated. -- [ ] Spec version has been updated. +- [ ] `spec_version` has been incremented. - [ ] Transaction version has been updated if required. - [ ] Pull Request to `dev` has been created. - [ ] Pull Request to `staging` has been created. +- [ ] `network-relayer`'s [events](https://github.com/Cerebellum-Network/network-relayer/blob/dev-cere/shared/substrate/events.go) have been updated according to the blockchain events if applicable. +- [ ] All CI checks have been passed successfully ## Checklist for Release @@ -54,3 +57,4 @@ Please select the branch type you are merging and fill in the relevant template. - [ ] Crate version has been updated. - [ ] Spec version has been updated. - [ ] Transaction version has been updated if required. +- [ ] All CI checks have been passed successfully diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 776ae796f..5bbec07e0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -49,9 +49,15 @@ jobs: target: wasm32-unknown-unknown - name: Rust Cache uses: Swatinem/rust-cache@v2 + - name: Install try-runtime + run: cargo install --git https://github.com/paritytech/try-runtime-cli --tag v0.7.0 --locked - name: Check Build run: | - cargo build --release + cargo build --release --features try-runtime + - name: Check Try-Runtime + run: | + try-runtime --runtime ./target/release/wbuild/cere-runtime/cere_runtime.compact.compressed.wasm \ + on-runtime-upgrade --disable-idempotency-checks live --uri wss://archive.mainnet.cere.network:443 - name: Run dev chain run: | timeout --preserve-status 30s ./target/release/cere --dev @@ -59,9 +65,6 @@ jobs: run: > pushd node && cargo check --features=runtime-benchmarks --release - - name: Check Build for Try-Runtime - run: | - cargo check --features=try-runtime --release clippy: name: Run Clippy diff --git a/.github/workflows/docker-image-berlin.yml b/.github/workflows/docker-image-berlin.yml new file mode 100644 index 000000000..4c8c9a716 --- /dev/null +++ b/.github/workflows/docker-image-berlin.yml @@ -0,0 +1,50 @@ +name: ECR + +on: + push: + branches: + - berlin-hackaton + +env: + PROFILE: release + +jobs: + build: + runs-on: ubuntu-latest + concurrency: dev + steps: + - uses: actions/checkout@v3 + + - name: Cache cargo registry + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Configure AWS credentials ORG + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.ORG_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.ORG_AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 + + - name: Login to Amazon ECR ORG + id: login-ecr-org + uses: aws-actions/amazon-ecr-login@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push docker image to ECR Docker + uses: docker/build-push-action@v4 + with: + context: . + file: Dockerfile.tests + push: true + build-args: | + "ECR_REGISTRY=${{ steps.login-ecr-org.outputs.registry }}-berlin" \ No newline at end of file diff --git a/.github/workflows/republish-dev-image-to-dockerhub.yaml b/.github/workflows/republish-dev-image-to-dockerhub.yaml new file mode 100644 index 000000000..d04921f67 --- /dev/null +++ b/.github/workflows/republish-dev-image-to-dockerhub.yaml @@ -0,0 +1,38 @@ +name: Republish dev-latest docker image to DockerHub +on: + workflow_dispatch: + +env: + ECR_REPOSITORY: pos-network-node + DOCKERHUB_REPOSITORY: cerebellumnetwork + PACKAGE_NAME: pos-node +jobs: + tag-and-push: + runs-on: [self-hosted, cere-network-xlarge] + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.DEV_NETWORK_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.DEV_NETWORK_AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + - name: Pull image from ECR and change name, push tagged version to ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + run: | + docker pull $ECR_REGISTRY/$ECR_REPOSITORY:dev-latest + docker image tag $ECR_REGISTRY/$ECR_REPOSITORY:dev-latest $DOCKERHUB_REPOSITORY/$PACKAGE_NAME:dev-latest + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Publish image to DockerHub + id: docker_publish + run: | + docker push $DOCKERHUB_REPOSITORY/$PACKAGE_NAME:dev-latest + - name: Image digest + run: echo ${{ steps.docker_publish.outputs.digest }} diff --git a/.github/workflows/stage.yaml b/.github/workflows/stage.yaml index cf2c208f1..b62aa6138 100644 --- a/.github/workflows/stage.yaml +++ b/.github/workflows/stage.yaml @@ -3,7 +3,7 @@ on: push: branches: - dev - - staging + - new-staging - master - 'release/**' - 'hotfix/**' diff --git a/CHANGELOG.md b/CHANGELOG.md index e6235cd26..62397ea70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [C] Changes is `Cere` Runtime - [D] Changes is `Cere Dev` Runtime +- +## [6.1.0] +### Changed + +- [C,D] Update Substrate from `v1.2` to `v1.4`. + +## [6.0.0] + +### Changed + +- [C] `pallet-ddc-verification`: Verification Pallet and validator OCW for DAC verification. +- [C] `pallet-ddc-clusters`: New `join_cluster` extrinsic. + +## [5.5.0] + +### Changed + +- [C,D] Update Substrate from `v1.1` to `v1.2`. + +## [5.4.1] + +### Changed + +- [D] `pallet-ddc-verification`: Introduction of the Verification pallet to ensure the secure posting and retrieval of verification keys to and from the blockchain. +- [D] `pallet-ddc-clusters`: New `join_cluster` extrinsic. + +## [5.4.0] + +### Changed + +- [C,D] Introduce new events to the DDC Payouts Pallet +- [C,D] `pallet-ddc-clusters-gov`: Introduction of the Cluster Governance pallet for managing clusters protocol parameters. + +## [5.3.1] + +### Changed + +- [C,D] `WhitelistOrigin` is set to the Technical Committee Collective Body +- [C,D] The _Support Curve_ in OpenGov Tracks is made more strict ## [5.3.0] @@ -31,6 +70,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.2.0] +- DAC ddc node mode + ### Added - [C,D] Missing storage migrations to Staking pallet @@ -115,7 +156,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [C,D] Updated Substrate to polkadot-v0.9.38 - [C] Added pallet-preimage to support democracy functionality. - Changes in `pallet-ddc-payouts::begin_billing_report` crate to accept start and end of the era. -- Changes in `pallet-ddc-payouts::begin_billing_report` crate to accept start and end of the era. - More explicit events in `pallet-ddc-payouts` and `pallet-ddc-customers` ## [4.8.6] diff --git a/Cargo.lock b/Cargo.lock index 746efb529..7f0c4a88e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,18 +23,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli 0.28.1", + "gimli 0.31.1", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -65,10 +65,10 @@ checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -77,7 +77,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -89,7 +89,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -104,6 +104,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -130,47 +136,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -178,9 +185,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "approx" @@ -198,13 +205,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" dependencies = [ "include_dir", - "itertools", + "itertools 0.10.5", "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bls12-377-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c7021f180a0cbea0380eba97c2af3c57074cdaffe0eef7e840e1c9f2841e55" +dependencies = [ + "ark-bls12-377", + "ark-ec", + "ark-models-ext", + "ark-std", +] + [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -217,6 +247,45 @@ dependencies = [ "ark-std", ] +[[package]] +name = "ark-bls12-381-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1dc4b3d08f19e8ec06e949712f95b8361e43f1391d94f65e4234df03480631c" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-models-ext", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bw6-761" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0605daf0cc5aa2034b78d008aaf159f56901d92a52ee4f6ecdfdac4f426700" +dependencies = [ + "ark-bls12-377", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bw6-761-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccee5fba47266f460067588ee1bf070a9c760bf2050c1c509982c5719aadb4f2" +dependencies = [ + "ark-bw6-761", + "ark-ec", + "ark-ff", + "ark-models-ext", + "ark-std", +] + [[package]] name = "ark-ec" version = "0.4.2" @@ -229,11 +298,37 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.13.2", - "itertools", + "itertools 0.10.5", "num-traits", + "rayon", "zeroize", ] +[[package]] +name = "ark-ed-on-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10d901b9ac4b38f9c32beacedfadcdd64e46f8d7f8e88c1ae1060022cf6f6c6" +dependencies = [ + "ark-bls12-377", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ed-on-bls12-377-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524a4fb7540df2e1a8c2e67a83ba1d1e6c3947f4f9342cc2359fc2e789ad731d" +dependencies = [ + "ark-ec", + "ark-ed-on-bls12-377", + "ark-ff", + "ark-models-ext", + "ark-std", +] + [[package]] name = "ark-ed-on-bls12-381-bandersnatch" version = "0.4.0" @@ -246,6 +341,19 @@ dependencies = [ "ark-std", ] +[[package]] +name = "ark-ed-on-bls12-381-bandersnatch-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15185f1acb49a07ff8cbe5f11a1adc5a93b19e211e325d826ae98e98e124346" +dependencies = [ + "ark-ec", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ff", + "ark-models-ext", + "ark-std", +] + [[package]] name = "ark-ff" version = "0.4.2" @@ -258,11 +366,11 @@ dependencies = [ "ark-std", "derivative", "digest 0.10.7", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits", "paste", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "zeroize", ] @@ -289,6 +397,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-models-ext" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9eab5d4b5ff2f228b763d38442adc9b084b0a465409b059fac5c2308835ec2" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", +] + [[package]] name = "ark-poly" version = "0.4.2" @@ -304,15 +425,16 @@ dependencies = [ [[package]] name = "ark-scale" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b08346a3e38e2be792ef53ee168623c9244d968ff00cd70fb9932f6fe36393" +checksum = "51bd73bb6ddb72630987d37fa963e99196896c0d0ea81b7c894567e74a2f83af" dependencies = [ "ark-ec", "ark-ff", "ark-serialize", "ark-std", "parity-scale-codec", + "scale-info", ] [[package]] @@ -332,13 +454,13 @@ dependencies = [ [[package]] name = "ark-secret-scalar" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=cbc342e#cbc342e95d3cbcd3c5ba8d45af7200eb58e63502" dependencies = [ "ark-ec", "ark-ff", "ark-serialize", "ark-std", - "ark-transcript", + "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=cbc342e)", "digest 0.10.7", "rand_core 0.6.4", "zeroize", @@ -375,12 +497,14 @@ checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", "rand 0.8.5", + "rayon", ] [[package]] name = "ark-transcript" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563084372d89271122bd743ef0a608179726f5fad0566008ba55bd0f756489b8" dependencies = [ "ark-ff", "ark-serialize", @@ -390,17 +514,36 @@ dependencies = [ "sha3", ] +[[package]] +name = "ark-transcript" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=cbc342e#cbc342e95d3cbcd3c5ba8d45af7200eb58e63502" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "digest 0.10.7", + "rand_core 0.6.4", + "sha3", +] + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + [[package]] name = "array-bytes" -version = "6.2.2" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f840fb7195bcfc5e17ea40c26e5ce6d5b9ce5d584466e17703209657e459ae0" +checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -410,9 +553,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "asn1-rs" @@ -472,21 +615,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 0.38.32", + "rustix 0.38.37", "slab", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -500,35 +643,24 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.3.1", "event-listener-strategy", "pin-project-lite 0.2.14", ] -[[package]] -name = "async-recursion" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.58", -] - [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -557,35 +689,34 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "addr2line 0.21.0", - "cc", + "addr2line 0.24.2", "cfg-if", "libc", "miniz_oxide", - "object 0.32.2", + "object 0.36.5", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] name = "bandersnatch_vrfs" -version = "0.0.1" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +version = "0.0.3" +source = "git+https://github.com/w3f/ring-vrf?rev=cbc342e#cbc342e95d3cbcd3c5ba8d45af7200eb58e63502" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ed-on-bls12-381-bandersnatch", "ark-ff", - "ark-scale 0.0.12", "ark-serialize", "ark-std", "dleq_vrf", @@ -595,6 +726,8 @@ dependencies = [ "rand_core 0.6.4", "ring 0.1.0", "sha2 0.10.8", + "sp-ark-bls12-381", + "sp-ark-ed-on-bls12-381-bandersnatch", "zeroize", ] @@ -658,13 +791,42 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.17", + "prettyplease 0.2.24", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.58", + "syn 2.0.82", +] + +[[package]] +name = "bip39" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", ] [[package]] @@ -675,9 +837,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -691,6 +853,18 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +dependencies = [ + "byte-tools", + "crypto-mac 0.7.0", + "digest 0.8.1", + "opaque-debug 0.2.3", +] + [[package]] name = "blake2" version = "0.10.6" @@ -707,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "constant_time_eq", ] @@ -718,18 +892,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "constant_time_eq", ] [[package]] name = "blake3" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "cc", "cfg-if", "constant_time_eq", @@ -794,18 +968,18 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "tinyvec", ] [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "serde", @@ -849,9 +1023,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -861,9 +1035,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "bzip2-sys" @@ -876,11 +1050,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher 0.2.5", + "ppv-lite86", +] + [[package]] name = "camino" -version = "1.1.6" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -902,7 +1086,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -910,17 +1094,18 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.92" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] name = "cere-cli" -version = "5.3.0" +version = "6.1.0" dependencies = [ "cere-client", "cere-service", @@ -937,7 +1122,7 @@ dependencies = [ [[package]] name = "cere-client" -version = "5.3.0" +version = "6.1.0" dependencies = [ "cere-dev-runtime", "cere-runtime", @@ -962,14 +1147,14 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-storage", + "sp-storage 13.0.0", "sp-timestamp", "sp-transaction-pool", ] [[package]] name = "cere-dev-runtime" -version = "5.3.0" +version = "6.1.0" dependencies = [ "cere-runtime-common", "ddc-primitives", @@ -981,7 +1166,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.4.1", "log", "node-primitives", "pallet-authority-discovery", @@ -997,10 +1182,12 @@ dependencies = [ "pallet-contracts-primitives", "pallet-conviction-voting", "pallet-ddc-clusters", + "pallet-ddc-clusters-gov", "pallet-ddc-customers", "pallet-ddc-nodes", "pallet-ddc-payouts", "pallet-ddc-staking", + "pallet-ddc-verification", "pallet-democracy", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", @@ -1016,8 +1203,11 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", + "pallet-offences-benchmarking", + "pallet-origins", "pallet-preimage", "pallet-proxy", "pallet-recovery", @@ -1050,8 +1240,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -1060,7 +1250,7 @@ dependencies = [ [[package]] name = "cere-rpc" -version = "5.3.0" +version = "6.1.0" dependencies = [ "jsonrpsee", "node-primitives", @@ -1089,7 +1279,7 @@ dependencies = [ [[package]] name = "cere-runtime" -version = "5.3.0" +version = "6.1.0" dependencies = [ "cere-runtime-common", "ddc-primitives", @@ -1101,7 +1291,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.3.4", "log", "node-primitives", "pallet-authority-discovery", @@ -1117,10 +1307,12 @@ dependencies = [ "pallet-contracts-primitives", "pallet-conviction-voting", "pallet-ddc-clusters", + "pallet-ddc-clusters-gov", "pallet-ddc-customers", "pallet-ddc-nodes", "pallet-ddc-payouts", "pallet-ddc-staking", + "pallet-ddc-verification", "pallet-democracy", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", @@ -1136,8 +1328,11 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", + "pallet-offences-benchmarking", + "pallet-origins", "pallet-preimage", "pallet-proxy", "pallet-recovery", @@ -1170,8 +1365,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -1180,32 +1375,35 @@ dependencies = [ [[package]] name = "cere-runtime-common" -version = "5.3.0" +version = "6.1.0" dependencies = [ "frame-support", "frame-system", "log", "node-primitives", "pallet-contracts", + "pallet-referenda", "pallet-session", "parity-scale-codec", "sp-api", + "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "cere-service" -version = "5.3.0" +version = "6.1.0" dependencies = [ "cere-client", "cere-dev-runtime", "cere-rpc", "cere-runtime", "cere-runtime-common", + "ddc-primitives", "futures", "jsonrpsee", "node-primitives", @@ -1254,9 +1452,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", ] @@ -1273,6 +1471,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" +dependencies = [ + "byteorder", + "keystream", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -1280,7 +1488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -1292,23 +1500,23 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", - "cipher", + "cipher 0.4.4", "poly1305", "zeroize", ] [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] @@ -1324,6 +1532,15 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1337,9 +1554,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -1348,9 +1565,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -1358,33 +1575,34 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "codespan-reporting" @@ -1398,25 +1616,25 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "comfy-table" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.3", + "strum_macros 0.26.4", "unicode-width", ] [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +source = "git+https://github.com/w3f/ring-proof#652286c32f96beb9ce7f5793f5e2c2c923f63b73" dependencies = [ "ark-ec", "ark-ff", @@ -1424,7 +1642,8 @@ dependencies = [ "ark-serialize", "ark-std", "fflonk", - "merlin 3.0.0", + "getrandom_or_panic", + "rand_core 0.6.4", ] [[package]] @@ -1435,9 +1654,9 @@ checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -1476,16 +1695,28 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "constcat" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" @@ -1499,9 +1730,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" @@ -1523,9 +1754,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1621,7 +1852,7 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "itertools", + "itertools 0.10.5", "log", "smallvec", "wasmparser", @@ -1630,9 +1861,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1658,9 +1889,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1676,7 +1907,7 @@ checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1691,6 +1922,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array 0.12.4", + "subtle 1.0.0", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -1698,7 +1939,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.7", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1708,7 +1949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ "generic-array 0.14.7", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1717,7 +1958,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1729,7 +1970,7 @@ dependencies = [ "byteorder", "digest 0.8.1", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1742,24 +1983,23 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", - "rustc_version 0.4.0", - "subtle", + "rustc_version 0.4.1", + "subtle 2.4.1", "zeroize", ] @@ -1771,14 +2011,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "cxx" -version = "1.0.121" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21db378d04296a84d8b7d047c36bb3954f0b46529db725d7e62fb02f9ba53ccc" +checksum = "cbdc8cca144dce1c4981b5c9ab748761619979e515c3d53b5df385c677d1d007" dependencies = [ "cc", "cxxbridge-flags", @@ -1788,9 +2028,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.121" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5262a7fa3f0bae2a55b767c223ba98032d7c328f5c13fa5cdc980b77fc0658" +checksum = "c5764c3142ab44fcf857101d12c0ddf09c34499900557c764f5ad0597159d1fc" dependencies = [ "cc", "codespan-reporting", @@ -1798,37 +2038,37 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "cxxbridge-flags" -version = "1.0.121" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8dcadd2e2fb4a501e1d9e93d6e88e6ea494306d8272069c92d5a9edf8855c0" +checksum = "d422aff542b4fa28c2ce8e5cc202d42dbf24702345c1fba3087b2d3f8a1b90ff" [[package]] name = "cxxbridge-macro" -version = "1.0.121" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad08a837629ad949b73d032c637653d069e909cffe4ee7870b02301939ce39cc" +checksum = "a1719100f31492cd6adeeab9a0f46cdbc846e615fdb66d7b398aa46ec7fdd06f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1836,9 +2076,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ "data-encoding", "syn 1.0.109", @@ -1846,16 +2086,19 @@ dependencies = [ [[package]] name = "ddc-primitives" -version = "5.3.0" +version = "6.1.0" dependencies = [ + "blake2 0.10.6", "frame-support", "frame-system", "parity-scale-codec", + "polkadot-ckb-merkle-mountain-range", "scale-info", "serde", + "sp-application-crypto", "sp-core", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -1913,15 +2156,48 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-syn-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 1.0.109", + "rustc_version 0.4.1", + "syn 2.0.82", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", ] [[package]] @@ -1957,14 +2233,14 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] name = "directories" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ "dirs-sys", ] @@ -1981,13 +2257,14 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2003,56 +2280,56 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "dleq_vrf" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=cbc342e#cbc342e95d3cbcd3c5ba8d45af7200eb58e63502" dependencies = [ "ark-ec", "ark-ff", - "ark-scale 0.0.10", + "ark-scale 0.0.11", "ark-secret-scalar", "ark-serialize", "ark-std", - "ark-transcript", - "arrayvec 0.7.4", + "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=cbc342e)", + "arrayvec 0.7.6", "rand_core 0.6.4", "zeroize", ] [[package]] name = "docify" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc4fd38aaa9fb98ac70794c82a00360d1e165a87fbf96a8a91f9dfc602aaee2" +checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" dependencies = [ "docify_macros", ] [[package]] name = "docify_macros" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fa215f3a0d40fb2a221b3aa90d8e1fbb8379785a990cb60d62ac71ebdc6460" +checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" dependencies = [ "common-path", - "derive-syn-parse", + "derive-syn-parse 0.2.0", "once_cell", "proc-macro2", "quote", "regex", - "syn 2.0.58", + "syn 2.0.82", "termcolor", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -2131,12 +2408,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519", "rand_core 0.6.4", "serde", "sha2 0.10.8", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -2156,9 +2433,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -2175,7 +2452,7 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -2199,22 +2476,22 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -2244,9 +2521,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2260,9 +2537,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -2271,11 +2548,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.3.1", "pin-project-lite 0.2.14", ] @@ -2290,16 +2567,17 @@ dependencies = [ [[package]] name = "expander" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e83c02035136f1592a47964ea60c05a50e4ed8b5892cfac197063850898d4d" +checksum = "e2c470c71d91ecbd179935b24170459e926382eaaa86b590b78814e180d8a8e2" dependencies = [ - "blake2", + "blake2 0.10.6", + "file-guard", "fs-err", - "prettier-please", + "prettyplease 0.2.24", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -2316,9 +2594,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdlimit" @@ -2336,7 +2614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2353,10 +2631,20 @@ dependencies = [ ] [[package]] -name = "fiat-crypto" -version = "0.2.7" +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "file-guard" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c" +dependencies = [ + "libc", + "winapi", +] [[package]] name = "file-per-thread-logger" @@ -2370,14 +2658,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -2392,7 +2680,7 @@ dependencies = [ "log", "num-traits", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "scale-info", ] @@ -2416,9 +2704,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "libz-sys", @@ -2443,7 +2731,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", ] @@ -2466,7 +2754,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-support-procedural", @@ -2482,19 +2770,19 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "static_assertions", ] [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "Inflector", - "array-bytes", + "array-bytes 6.2.3", "chrono", "clap", "comfy-table", @@ -2503,7 +2791,7 @@ dependencies = [ "frame-system", "gethostname", "handlebars", - "itertools", + "itertools 0.10.5", "lazy_static", "linked-hash-map", "log", @@ -2523,15 +2811,15 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-database", - "sp-externalities", + "sp-externalities 0.19.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "sp-trie", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "thiserror", "thousands", ] @@ -2539,18 +2827,18 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -2561,13 +2849,13 @@ dependencies = [ "sp-core", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -2578,8 +2866,8 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] @@ -2597,9 +2885,8 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "async-recursion", "futures", "indicatif", "jsonrpsee", @@ -2619,9 +2906,10 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "aquamarine", + "array-bytes 6.2.3", "bitflags 1.3.2", "docify", "environmental", @@ -2641,7 +2929,7 @@ dependencies = [ "sp-arithmetic", "sp-core", "sp-core-hashing-proc-macro", - "sp-debug-derive", + "sp-debug-derive 8.0.0", "sp-genesis-builder", "sp-inherents", "sp-io", @@ -2649,8 +2937,8 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-weights", "static_assertions", "tt-call", @@ -2659,47 +2947,48 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "Inflector", "cfg-expr", - "derive-syn-parse", + "derive-syn-parse 0.1.5", "expander", "frame-support-procedural-tools", - "itertools", + "itertools 0.10.5", "macro_magic", "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.58", + "sp-core-hashing", + "syn 2.0.82", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "cfg-if", "frame-support", @@ -2710,7 +2999,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-version", "sp-weights", ] @@ -2718,7 +3007,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -2727,13 +3016,13 @@ dependencies = [ "scale-info", "sp-core", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "sp-api", @@ -2742,13 +3031,13 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "parity-scale-codec", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -2778,9 +3067,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -2793,9 +3082,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -2803,15 +3092,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -2821,9 +3110,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -2837,13 +3126,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -2859,15 +3148,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -2877,9 +3166,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -2945,9 +3234,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -2960,6 +3249,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ + "rand 0.8.5", "rand_core 0.6.4", ] @@ -2986,9 +3276,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -2998,15 +3288,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -3017,7 +3307,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3032,7 +3322,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.6", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -3088,9 +3378,28 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "hashlink" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] name = "heck" @@ -3119,18 +3428,36 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + [[package]] name = "hex-literal" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hkdf" version = "0.12.4" @@ -3230,9 +3557,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -3248,9 +3575,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -3263,7 +3590,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite 0.2.14", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -3280,7 +3607,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-native-certs", "tokio", "tokio-rustls", @@ -3289,9 +3616,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3369,6 +3696,26 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-num-traits" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c" +dependencies = [ + "integer-sqrt", + "num-traits", + "uint 0.10.0", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -3378,6 +3725,15 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -3391,18 +3747,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -3421,12 +3777,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.15.0", ] [[package]] @@ -3459,9 +3815,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -3498,7 +3854,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.6", + "socket2 0.5.7", "widestring", "windows-sys 0.48.0", "winreg", @@ -3506,21 +3862,27 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -3530,6 +3892,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -3538,18 +3909,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -3597,7 +3968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803" dependencies = [ "anyhow", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "async-lock 2.8.0", "async-trait", "beef", @@ -3607,7 +3978,7 @@ dependencies = [ "globset", "hyper", "jsonrpsee-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "rustc-hash", "serde", @@ -3700,9 +4071,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -3720,6 +4091,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keystream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" + [[package]] name = "kvdb" version = "0.13.0" @@ -3736,7 +4113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" dependencies = [ "kvdb", - "parking_lot 0.12.1", + "parking_lot 0.12.3", ] [[package]] @@ -3747,7 +4124,7 @@ checksum = "b644c70b92285f66bfc2032922a79000ea30af7bc2ab31902992a5dcb9b434f6" dependencies = [ "kvdb", "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "regex", "rocksdb", "smallvec", @@ -3755,9 +4132,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -3767,18 +4144,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] @@ -3796,7 +4173,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.14", + "getrandom 0.2.15", "instant", "libp2p-allow-block-list", "libp2p-connection-limits", @@ -3861,7 +4238,7 @@ dependencies = [ "multihash", "multistream-select", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project", "quick-protobuf", "rand 0.8.5", @@ -3881,7 +4258,7 @@ dependencies = [ "futures", "libp2p-core", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "smallvec", "trust-dns-resolver", ] @@ -3932,7 +4309,7 @@ version = "0.43.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "asynchronous-codec", "bytes", "either", @@ -3949,7 +4326,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "thiserror", - "uint", + "uint 0.9.5", "unsigned-varint", "void", ] @@ -4043,7 +4420,7 @@ dependencies = [ "libp2p-identity", "libp2p-tls", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "quinn-proto", "rand 0.8.5", "rustls 0.20.9", @@ -4159,7 +4536,7 @@ dependencies = [ "futures-rustls", "libp2p-core", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "quicksink", "rw-stream-sink", "soketto", @@ -4186,8 +4563,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", + "redox_syscall 0.5.7", ] [[package]] @@ -4232,7 +4610,7 @@ checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", "digest 0.9.0", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -4255,9 +4633,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "pkg-config", @@ -4290,9 +4668,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" +checksum = "a9eda9dcf4f2a99787827661f312ac3219292549c2ee992bf9a6248ffb066bf7" dependencies = [ "nalgebra", ] @@ -4305,15 +4683,27 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lioness" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" +dependencies = [ + "arrayref", + "blake2 0.8.1", + "chacha", + "keystream", +] [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -4321,9 +4711,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -4345,19 +4735,18 @@ dependencies = [ [[package]] name = "lz4" -version = "1.24.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +checksum = "4d1febb2b4a79ddd1980eede06a8f7902197960aa0383ffcfdd62fe723036725" dependencies = [ - "libc", "lz4-sys", ] [[package]] name = "lz4-sys" -version = "1.9.4" +version = "1.11.1+lz4-1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" dependencies = [ "cc", "libc", @@ -4374,50 +4763,50 @@ dependencies = [ [[package]] name = "macro_magic" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aee866bfee30d2d7e83835a4574aad5b45adba4cc807f2a3bbba974e5d4383c9" +checksum = "cc33f9f0351468d26fbc53d9ce00a096c8522ecb42f19b50f34f2c422f76d21d" dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "macro_magic_core" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e766a20fd9c72bab3e1e64ed63f36bd08410e75803813df210d1ce297d7ad00" +checksum = "1687dc887e42f352865a393acae7cf79d98fab6351cde1f58e9e057da89bf150" dependencies = [ "const-random", - "derive-syn-parse", + "derive-syn-parse 0.2.0", "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "macro_magic_core_macros" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d710e1214dffbab3b5dacb21475dde7d6ed84c69ff722b3a47a782668d44fbac" +checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "macro_magic_macros" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" +checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -4441,6 +4830,15 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matches" version = "0.1.10" @@ -4449,9 +4847,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" dependencies = [ "autocfg", "rawpointer", @@ -4459,9 +4857,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memfd" @@ -4469,7 +4867,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.32", + "rustix 0.38.37", ] [[package]] @@ -4531,22 +4929,48 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "mixnet" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa3eb39495d8e2e2947a1d862852c90cc6a4a8845f8b41c8829cb9fcc047f4a" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "bitflags 1.3.2", + "blake2 0.10.6", + "c2-chacha", + "curve25519-dalek 4.1.3", + "either", + "hashlink", + "lioness", + "log", + "parking_lot 0.12.3", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_distr", + "subtle 2.4.1", + "thiserror", + "zeroize", ] [[package]] @@ -4659,13 +5083,12 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.5" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" +checksum = "3bf139e93ad757869338ad85239cb1d6c067b23b94e5846e637ca6328ee4be60" dependencies = [ "approx", "matrixmultiply", - "nalgebra-macros", "num-complex", "num-rational", "num-traits", @@ -4673,17 +5096,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "nalgebra-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "names" version = "0.13.0" @@ -4773,7 +5185,7 @@ dependencies = [ [[package]] name = "node-primitives" version = "2.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "sp-core", "sp-runtime", @@ -4801,22 +5213,31 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -4833,7 +5254,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "itoa", ] @@ -4848,11 +5269,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -4860,11 +5280,12 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -4897,9 +5318,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] @@ -4915,9 +5336,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -4937,10 +5358,22 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -4950,13 +5383,13 @@ dependencies = [ "sp-application-crypto", "sp-authority-discovery", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -4964,13 +5397,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -4988,13 +5421,13 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "aquamarine", "docify", @@ -5009,14 +5442,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5025,13 +5458,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5043,12 +5476,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-chainbridge" -version = "5.3.0" +version = "6.1.0" dependencies = [ "frame-support", "frame-system", @@ -5058,13 +5491,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5077,13 +5510,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5094,13 +5527,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "bitflags 1.3.2", "environmental", @@ -5122,7 +5555,9 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", + "staging-xcm", + "staging-xcm-builder", "wasm-instrument 0.4.0", "wasmi", ] @@ -5130,30 +5565,30 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5164,18 +5599,18 @@ dependencies = [ "serde", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-ddc-clusters" -version = "5.3.0" +version = "6.1.0" dependencies = [ "ddc-primitives", "frame-benchmarking", "frame-support", "frame-system", - "hex-literal", + "hex-literal 0.4.1", "log", "pallet-balances", "pallet-contracts", @@ -5189,14 +5624,49 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", + "substrate-test-utils", +] + +[[package]] +name = "pallet-ddc-clusters-gov" +version = "6.1.0" +dependencies = [ + "ddc-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal 0.4.1", + "lazy_static", + "log", + "pallet-balances", + "pallet-contracts", + "pallet-conviction-voting", + "pallet-ddc-clusters", + "pallet-ddc-nodes", + "pallet-ddc-staking", + "pallet-insecure-randomness-collective-flip", + "pallet-preimage", + "pallet-referenda", + "pallet-scheduler", + "pallet-timestamp", + "parity-scale-codec", + "parking_lot 0.12.3", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "substrate-test-utils", ] [[package]] name = "pallet-ddc-customers" -version = "5.3.0" +version = "6.1.0" dependencies = [ "ddc-primitives", "frame-benchmarking", @@ -5212,19 +5682,21 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "substrate-test-utils", ] [[package]] name = "pallet-ddc-nodes" -version = "5.3.0" +version = "6.1.0" dependencies = [ "ddc-primitives", "frame-benchmarking", "frame-support", "frame-system", + "hex", + "log", "pallet-balances", "pallet-timestamp", "parity-scale-codec", @@ -5233,14 +5705,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "substrate-test-utils", ] [[package]] name = "pallet-ddc-payouts" -version = "5.3.0" +version = "6.1.0" dependencies = [ "byte-unit", "chrono", @@ -5249,6 +5721,7 @@ dependencies = [ "frame-election-provider-support", "frame-support", "frame-system", + "hex", "log", "pallet-balances", "parity-scale-codec", @@ -5257,37 +5730,77 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "substrate-test-utils", ] [[package]] name = "pallet-ddc-staking" -version = "5.3.0" +version = "6.1.0" dependencies = [ "ddc-primitives", "frame-benchmarking", "frame-support", "frame-system", + "hex-literal 0.4.1", "lazy_static", + "log", "pallet-balances", + "pallet-contracts", + "pallet-ddc-clusters", + "pallet-ddc-nodes", + "pallet-insecure-randomness-collective-flip", "pallet-timestamp", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "scale-info", "sp-core", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "substrate-test-utils", ] +[[package]] +name = "pallet-ddc-verification" +version = "6.1.0" +dependencies = [ + "array-bytes 6.2.3", + "base64ct", + "ddc-primitives", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "hex", + "itertools 0.13.0", + "log", + "pallet-balances", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", + "parity-scale-codec", + "polkadot-ckb-merkle-mountain-range", + "rand 0.8.5", + "scale-info", + "serde", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std 8.0.0", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5299,13 +5812,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5321,14 +5834,14 @@ dependencies = [ "sp-io", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "strum 0.24.1", ] [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5336,13 +5849,13 @@ dependencies = [ "parity-scale-codec", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5355,12 +5868,12 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-erc20" -version = "5.3.0" +version = "6.1.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5374,12 +5887,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-erc721" -version = "5.3.0" +version = "6.1.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5391,13 +5904,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "docify", "frame-benchmarking", @@ -5410,13 +5923,13 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5433,13 +5946,13 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5449,13 +5962,13 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5469,13 +5982,13 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5486,13 +5999,13 @@ dependencies = [ "sp-io", "sp-keyring", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-insecure-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -5500,13 +6013,13 @@ dependencies = [ "safe-mix", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5517,59 +6030,79 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 8.0.0", +] + +[[package]] +name = "pallet-nomination-pools" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-balances", "parity-scale-codec", "scale-info", + "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-staking", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] -name = "pallet-nomination-pools" +name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", - "log", - "pallet-balances", + "pallet-bags-list", + "pallet-nomination-pools", + "pallet-staking", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", "sp-runtime", + "sp-runtime-interface 17.0.0", "sp-staking", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", ] [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", "sp-api", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -5580,13 +6113,53 @@ dependencies = [ "serde", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", +] + +[[package]] +name = "pallet-offences-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-babe", + "pallet-balances", + "pallet-grandpa", + "pallet-im-online", + "pallet-offences", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std 8.0.0", +] + +[[package]] +name = "pallet-origins" +version = "6.1.0" +dependencies = [ + "cere-runtime-common", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std 8.0.0", ] [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5597,13 +6170,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5612,13 +6185,13 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5627,13 +6200,13 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5646,13 +6219,13 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "docify", "frame-benchmarking", @@ -5663,14 +6236,14 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -5685,14 +6258,14 @@ dependencies = [ "sp-session", "sp-staking", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", ] [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5703,13 +6276,13 @@ dependencies = [ "rand 0.8.5", "sp-runtime", "sp-session", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5726,25 +6299,26 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "docify", "frame-benchmarking", "frame-support", "frame-system", @@ -5752,14 +6326,15 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "docify", "frame-benchmarking", "frame-support", "frame-system", @@ -5769,15 +6344,15 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-timestamp", ] [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5790,13 +6365,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-support", "frame-system", @@ -5806,13 +6381,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -5828,7 +6403,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -5840,8 +6415,9 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "docify", "frame-benchmarking", "frame-support", "frame-system", @@ -5850,14 +6426,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", + "sp-core", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5867,13 +6444,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5882,13 +6459,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-benchmarking", "frame-support", @@ -5897,7 +6474,7 @@ dependencies = [ "scale-info", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -5906,7 +6483,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "592a28a24b09c9dc20ac8afaa6839abc417c720afe42c12e1e4a9d6aa2508d2e" dependencies = [ - "blake2", + "blake2 0.10.6", "crc32fast", "fs2", "hex", @@ -5914,7 +6491,7 @@ dependencies = [ "log", "lz4", "memmap2", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "siphasher", "snap", @@ -5923,11 +6500,11 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bitvec", "byte-slice-cast", "bytes", @@ -5938,11 +6515,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -5962,9 +6539,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -5979,12 +6556,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -6003,15 +6580,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.7", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -6022,9 +6599,9 @@ checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -6035,15 +6612,6 @@ dependencies = [ "crypto-mac 0.11.1", ] -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "peeking_take_while" version = "0.1.2" @@ -6067,9 +6635,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.7" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -6078,9 +6646,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.7" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -6088,22 +6656,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.7" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "pest_meta" -version = "2.7.7" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -6112,32 +6680,32 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.6.0", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -6170,29 +6738,99 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] -name = "platforms" -version = "3.4.0" +name = "polkadot-ckb-merkle-mountain-range" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b44320e5f7ce2c18227537a3032ae5b2c476a7e8eddba45333e1011fc31b92" +dependencies = [ + "cfg-if", + "itertools 0.10.5", +] + +[[package]] +name = "polkadot-core-primitives" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 8.0.0", +] + +[[package]] +name = "polkadot-parachain-primitives" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "bounded-collections", + "derive_more 0.99.18", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std 8.0.0", + "sp-weights", +] + +[[package]] +name = "polkavm-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9428a5cfcc85c5d7b9fc4b6a18c4b802d0173d768182a51cc7751640f08b92" + +[[package]] +name = "polkavm-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" +dependencies = [ + "polkavm-derive-impl-macro", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" +dependencies = [ + "polkavm-common", + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" +dependencies = [ + "polkavm-derive-impl", + "syn 2.0.82", +] [[package]] name = "polling" -version = "3.6.0" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "pin-project-lite 0.2.14", - "rustix 0.38.32", + "rustix 0.38.37", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6220,9 +6858,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -6232,9 +6870,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "predicates" @@ -6244,7 +6885,7 @@ checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", - "itertools", + "itertools 0.10.5", "normalize-line-endings", "predicates-core", "regex", @@ -6252,35 +6893,25 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", ] -[[package]] -name = "prettier-please" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22020dfcf177fcc7bf5deaf7440af371400c67c0de14c399938d8ed4fb4645d3" -dependencies = [ - "proc-macro2", - "syn 2.0.58", -] - [[package]] name = "prettyplease" -version = "0.1.11" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28f53e8b192565862cf99343194579a022eb9c7dd3a8d03134734803c7b3125" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", "syn 1.0.109", @@ -6288,12 +6919,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.17" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" dependencies = [ "proc-macro2", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -6303,10 +6934,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec", - "impl-serde", + "impl-codec 0.6.0", + "impl-serde 0.4.0", "scale-info", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec 0.7.0", + "impl-num-traits", + "uint 0.10.0", ] [[package]] @@ -6321,12 +6964,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] @@ -6355,35 +6997,35 @@ dependencies = [ [[package]] name = "proc-macro-warning" -version = "0.4.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" +checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "thiserror", ] @@ -6395,7 +7037,7 @@ checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e" dependencies = [ "dtoa", "itoa", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus-client-derive-encode", ] @@ -6407,7 +7049,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -6428,12 +7070,12 @@ checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck 0.4.1", - "itertools", + "itertools 0.10.5", "lazy_static", "log", "multimap", "petgraph", - "prettyplease 0.1.11", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -6449,7 +7091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -6466,9 +7108,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] @@ -6532,9 +7174,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -6604,7 +7246,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", ] [[package]] @@ -6674,42 +7326,42 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "libredox", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -6726,14 +7378,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -6747,13 +7399,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.5", ] [[package]] @@ -6764,9 +7416,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "resolv-conf" @@ -6785,22 +7437,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle", + "subtle 2.4.1", ] [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +source = "git+https://github.com/w3f/ring-proof#652286c32f96beb9ce7f5793f5e2c2c923f63b73" dependencies = [ "ark-ec", "ark-ff", "ark-poly", "ark-serialize", "ark-std", + "ark-transcript 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.7.6", + "blake2 0.10.6", "common", "fflonk", - "merlin 3.0.0", ] [[package]] @@ -6826,7 +7480,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -6881,9 +7535,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -6908,11 +7562,11 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.22", + "semver 1.0.23", ] [[package]] @@ -6940,14 +7594,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -6965,9 +7619,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", @@ -7008,9 +7662,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rw-stream-sink" @@ -7025,9 +7679,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "safe-mix" @@ -7040,9 +7694,9 @@ dependencies = [ [[package]] name = "safe_arch" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" dependencies = [ "bytemuck", ] @@ -7059,18 +7713,18 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "log", "sp-core", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "thiserror", ] [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -7098,14 +7752,13 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "futures", "futures-timer", "log", "parity-scale-codec", "sc-block-builder", - "sc-client-api", "sc-proposer-metrics", "sc-telemetry", "sc-transaction-pool-api", @@ -7121,10 +7774,9 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", - "sc-client-api", "sp-api", "sp-block-builder", "sp-blockchain", @@ -7136,9 +7788,13 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "array-bytes 6.2.3", + "docify", + "log", "memmap2", + "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", "sc-executor", @@ -7148,6 +7804,8 @@ dependencies = [ "serde_json", "sp-blockchain", "sp-core", + "sp-genesis-builder", + "sp-io", "sp-runtime", "sp-state-machine", ] @@ -7155,24 +7813,26 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", + "bip39", "chrono", "clap", "fdlimit", "futures", + "itertools 0.10.5", "libp2p-identity", "log", "names", @@ -7183,6 +7843,7 @@ dependencies = [ "sc-client-api", "sc-client-db", "sc-keystore", + "sc-mixnet", "sc-network", "sc-service", "sc-telemetry", @@ -7198,20 +7859,19 @@ dependencies = [ "sp-runtime", "sp-version", "thiserror", - "tiny-bip39", "tokio", ] [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "fnv", "futures", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-executor", "sc-transaction-pool-api", "sc-utils", @@ -7220,18 +7880,19 @@ dependencies = [ "sp-consensus", "sp-core", "sp-database", - "sp-externalities", + "sp-externalities 0.19.0", "sp-runtime", "sp-state-machine", "sp-statement-store", - "sp-storage", + "sp-storage 13.0.0", + "sp-trie", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "hash-db", "kvdb", @@ -7241,7 +7902,7 @@ dependencies = [ "log", "parity-db", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-client-api", "sc-state-db", "schnellru", @@ -7257,7 +7918,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -7265,7 +7926,7 @@ dependencies = [ "libp2p-identity", "log", "mockall", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-client-api", "sc-utils", "serde", @@ -7282,7 +7943,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "fork-tree", @@ -7292,14 +7953,13 @@ dependencies = [ "num-rational", "num-traits", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-client-api", "sc-consensus", "sc-consensus-epochs", "sc-consensus-slots", "sc-telemetry", "sc-transaction-pool-api", - "scale-info", "sp-api", "sp-application-crypto", "sp-block-builder", @@ -7318,7 +7978,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "futures", "jsonrpsee", @@ -7340,7 +8000,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "fork-tree", "parity-scale-codec", @@ -7353,10 +8013,10 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ahash 0.8.11", - "array-bytes", + "array-bytes 6.2.3", "async-trait", "dyn-clone", "finality-grandpa", @@ -7365,7 +8025,7 @@ dependencies = [ "futures-timer", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "sc-block-builder", "sc-chain-spec", @@ -7374,6 +8034,7 @@ dependencies = [ "sc-network", "sc-network-common", "sc-network-gossip", + "sc-network-sync", "sc-telemetry", "sc-transaction-pool-api", "sc-utils", @@ -7394,7 +8055,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "finality-grandpa", "futures", @@ -7414,7 +8075,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -7437,33 +8098,33 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-executor-common", "sc-executor-wasmtime", "schnellru", "sp-api", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-io", "sp-panic-handler", - "sp-runtime-interface", + "sp-runtime-interface 17.0.0", "sp-trie", "sp-version", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "tracing", ] [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "thiserror", "wasm-instrument 0.3.0", ] @@ -7471,24 +8132,25 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "anyhow", "cfg-if", "libc", "log", + "parking_lot 0.12.3", "rustix 0.36.17", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", + "sp-runtime-interface 17.0.0", + "sp-wasm-interface 14.0.0", "wasmtime", ] [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ansi_term", "futures", @@ -7497,6 +8159,7 @@ dependencies = [ "sc-client-api", "sc-network", "sc-network-common", + "sc-network-sync", "sp-blockchain", "sp-runtime", ] @@ -7504,10 +8167,10 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", - "parking_lot 0.12.1", + "array-bytes 6.2.3", + "parking_lot 0.12.3", "serde_json", "sp-application-crypto", "sp-core", @@ -7515,12 +8178,40 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-mixnet" +version = "0.1.0-dev" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "array-bytes 4.2.0", + "arrayvec 0.7.6", + "blake2 0.10.6", + "futures", + "futures-timer", + "libp2p-identity", + "log", + "mixnet", + "multiaddr", + "parity-scale-codec", + "parking_lot 0.12.3", + "sc-client-api", + "sc-network", + "sc-transaction-pool-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-keystore", + "sp-mixnet", + "sp-runtime", + "thiserror", +] + [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "async-channel", "async-trait", "asynchronous-codec", @@ -7535,7 +8226,7 @@ dependencies = [ "log", "mockall", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "partial_sort", "pin-project", "rand 0.8.5", @@ -7559,7 +8250,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-channel", "cid", @@ -7579,7 +8270,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "bitflags 1.3.2", @@ -7596,7 +8287,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ahash 0.8.11", "futures", @@ -7605,6 +8296,7 @@ dependencies = [ "log", "sc-network", "sc-network-common", + "sc-network-sync", "schnellru", "sp-runtime", "substrate-prometheus-endpoint", @@ -7614,9 +8306,9 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "async-channel", "futures", "libp2p-identity", @@ -7635,9 +8327,9 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "async-channel", "async-trait", "fork-tree", @@ -7664,20 +8356,23 @@ dependencies = [ "sp-runtime", "substrate-prometheus-endpoint", "thiserror", + "tokio", + "tokio-stream", ] [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "futures", "libp2p", "log", "parity-scale-codec", "sc-network", "sc-network-common", + "sc-network-sync", "sc-utils", "sp-consensus", "sp-runtime", @@ -7687,9 +8382,9 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "bytes", "fnv", "futures", @@ -7701,7 +8396,7 @@ dependencies = [ "num_cpus", "once_cell", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "sc-client-api", "sc-network", @@ -7710,7 +8405,7 @@ dependencies = [ "sc-utils", "sp-api", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-keystore", "sp-offchain", "sp-runtime", @@ -7721,7 +8416,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -7730,16 +8425,17 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "futures", "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-block-builder", "sc-chain-spec", "sc-client-api", + "sc-mixnet", "sc-rpc-api", "sc-tracing", "sc-transaction-pool-api", @@ -7761,11 +8457,12 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "jsonrpsee", "parity-scale-codec", "sc-chain-spec", + "sc-mixnet", "sc-transaction-pool-api", "scale-info", "serde", @@ -7780,7 +8477,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "http", "jsonrpsee", @@ -7795,16 +8492,16 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", + "array-bytes 6.2.3", "futures", "futures-util", "hex", "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-chain-spec", "sc-client-api", "sc-transaction-pool-api", @@ -7813,6 +8510,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-core", + "sp-rpc", "sp-runtime", "sp-version", "thiserror", @@ -7823,7 +8521,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "directories", @@ -7833,10 +8531,9 @@ dependencies = [ "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project", "rand 0.8.5", - "sc-block-builder", "sc-chain-spec", "sc-client-api", "sc-client-db", @@ -7865,12 +8562,12 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-keystore", "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", @@ -7887,18 +8584,18 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sp-core", ] [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -7917,8 +8614,9 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ + "derive_more 0.99.18", "futures", "libc", "log", @@ -7930,19 +8628,19 @@ dependencies = [ "serde_json", "sp-core", "sp-io", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "chrono", "futures", "libp2p", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project", "rand 0.8.5", "sc-utils", @@ -7955,7 +8653,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ansi_term", "atty", @@ -7963,7 +8661,7 @@ dependencies = [ "lazy_static", "libc", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "regex", "rustc-hash", "sc-client-api", @@ -7974,28 +8672,28 @@ dependencies = [ "sp-core", "sp-rpc", "sp-runtime", - "sp-tracing", + "sp-tracing 10.0.0", "thiserror", "tracing", - "tracing-log", - "tracing-subscriber", + "tracing-log 0.1.4", + "tracing-subscriber 0.2.25", ] [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -8003,7 +8701,7 @@ dependencies = [ "linked-hash-map", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sc-client-api", "sc-transaction-pool-api", "sc-utils", @@ -8012,7 +8710,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "sp-tracing", + "sp-tracing 10.0.0", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -8021,7 +8719,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -8037,27 +8735,27 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-channel", "futures", "futures-timer", "lazy_static", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "sp-arithmetic", ] [[package]] name = "scale-info" -version = "2.11.2" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +checksum = "22760a375f81a31817aeaf6f5081e9ccb7ffd7f2da1809a6e3fc82b6656f10d5" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -8065,11 +8763,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.2" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +checksum = "abc61ebe25a5c410c0e245028fc9934bf8fa4817199ef5a24a68092edfd34614" dependencies = [ - "proc-macro-crate 1.1.3", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -8077,18 +8775,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "schnellru" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ "ahash 0.8.11", "cfg-if", @@ -8109,7 +8807,7 @@ dependencies = [ "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -8120,13 +8818,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ "arrayref", - "arrayvec 0.7.4", - "curve25519-dalek 4.1.2", + "arrayvec 0.7.6", + "curve25519-dalek 4.1.3", "getrandom_or_panic", "merlin 3.0.0", "rand_core 0.6.4", "sha2 0.10.8", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -8162,7 +8860,7 @@ dependencies = [ "der", "generic-array 0.14.7", "pkcs8", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -8195,11 +8893,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -8208,9 +8906,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -8236,9 +8934,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -8251,40 +8949,41 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -8365,9 +9064,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -8384,9 +9083,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" dependencies = [ "approx", "num-complex", @@ -8435,14 +9134,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ "aes-gcm", - "blake2", + "blake2 0.10.6", "chacha20poly1305", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "ring 0.17.8", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "sha2 0.10.8", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -8457,9 +9156,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -8485,7 +9184,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "hash-db", "log", @@ -8493,11 +9192,11 @@ dependencies = [ "scale-info", "sp-api-proc-macro", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-metadata-ir", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", "sp-version", "thiserror", @@ -8506,77 +9205,95 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "Inflector", - "blake2", + "blake2 0.10.6", "expander", "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "sp-application-crypto" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", "serde", "sp-core", "sp-io", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-arithmetic" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 8.0.0", "static_assertions", ] +[[package]] +name = "sp-ark-bls12-381" +version = "0.4.2" +source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" +dependencies = [ + "ark-bls12-381-ext", + "sp-crypto-ec-utils", +] + +[[package]] +name = "sp-ark-ed-on-bls12-381-bandersnatch" +version = "0.4.2" +source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" +dependencies = [ + "ark-ed-on-bls12-381-bandersnatch-ext", + "sp-crypto-ec-utils", +] + [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", "sp-application-crypto", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "sp-api", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "futures", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "schnellru", "sp-api", "sp-consensus", @@ -8589,7 +9306,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "futures", @@ -8604,7 +9321,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "parity-scale-codec", @@ -8614,14 +9331,14 @@ dependencies = [ "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "parity-scale-codec", @@ -8633,14 +9350,14 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "finality-grandpa", "log", @@ -8652,47 +9369,48 @@ dependencies = [ "sp-core", "sp-keystore", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] [[package]] name = "sp-core" version = "21.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "array-bytes", - "arrayvec 0.7.4", + "array-bytes 6.2.3", "bandersnatch_vrfs", + "bip39", "bitflags 1.3.2", - "blake2", + "blake2 0.10.6", "bounded-collections", - "bs58 0.5.0", + "bs58 0.5.1", "dyn-clonable", "ed25519-zebra", "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", + "itertools 0.10.5", "lazy_static", "libsecp256k1", "log", "merlin 2.0.1", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "paste", - "primitive-types", + "primitive-types 0.12.2", "rand 0.8.5", "regex", "scale-info", @@ -8701,23 +9419,23 @@ dependencies = [ "secrecy", "serde", "sp-core-hashing", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "ss58-registry", "substrate-bip39", "thiserror", - "tiny-bip39", "tracing", + "w3f-bls", "zeroize", ] [[package]] name = "sp-core-hashing" version = "9.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "blake2b_simd", "byteorder", @@ -8730,72 +9448,112 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "9.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "quote", "sp-core-hashing", - "syn 2.0.58", + "syn 2.0.82", +] + +[[package]] +name = "sp-crypto-ec-utils" +version = "0.10.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" +dependencies = [ + "ark-bls12-377", + "ark-bls12-377-ext", + "ark-bls12-381", + "ark-bls12-381-ext", + "ark-bw6-761", + "ark-bw6-761-ext", + "ark-ec", + "ark-ed-on-bls12-377", + "ark-ed-on-bls12-377-ext", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ed-on-bls12-381-bandersnatch-ext", + "ark-scale 0.0.12", + "sp-runtime-interface 24.0.0", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "kvdb", - "parking_lot 0.12.1", + "parking_lot 0.12.3", ] [[package]] name = "sp-debug-derive" version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "sp-debug-derive" +version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", +] + +[[package]] +name = "sp-externalities" +version = "0.19.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 8.0.0", + "sp-storage 13.0.0", ] [[package]] name = "sp-externalities" -version = "0.19.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +version = "0.25.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" dependencies = [ "environmental", "parity-scale-codec", - "sp-std", - "sp-storage", + "sp-storage 19.0.0", ] [[package]] name = "sp-genesis-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "serde_json", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "thiserror", ] [[package]] name = "sp-io" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "bytes", "ed25519-dalek", @@ -8805,12 +9563,12 @@ dependencies = [ "rustversion", "secp256k1", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-keystore", - "sp-runtime-interface", + "sp-runtime-interface 17.0.0", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-trie", "tracing", "tracing-core", @@ -8819,7 +9577,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "lazy_static", "sp-core", @@ -8830,19 +9588,19 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.27.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "thiserror", ] [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "thiserror", "zstd 0.12.4", @@ -8851,18 +9609,30 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std", + "sp-std 8.0.0", +] + +[[package]] +name = "sp-mixnet" +version = "0.1.0-dev" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-std 8.0.0", ] [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", @@ -8870,13 +9640,13 @@ dependencies = [ "sp-arithmetic", "sp-core", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "sp-api", "sp-core", @@ -8886,7 +9656,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "backtrace", "lazy_static", @@ -8896,7 +9666,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "rustc-hash", "serde", @@ -8906,7 +9676,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "either", "hash256-std-hasher", @@ -8921,44 +9691,76 @@ dependencies = [ "sp-arithmetic", "sp-core", "sp-io", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] [[package]] name = "sp-runtime-interface" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types 0.12.2", + "sp-externalities 0.19.0", + "sp-runtime-interface-proc-macro 11.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", + "sp-tracing 10.0.0", + "sp-wasm-interface 14.0.0", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface" +version = "24.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", + "polkavm-derive", + "primitive-types 0.13.1", + "sp-externalities 0.25.0", + "sp-runtime-interface-proc-macro 17.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", + "sp-wasm-interface 20.0.0", "static_assertions", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "Inflector", "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "17.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" +dependencies = [ + "Inflector", + "expander", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.82", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", @@ -8967,13 +9769,13 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -8981,24 +9783,24 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] name = "sp-state-machine" version = "0.28.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "hash-db", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "smallvec", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-panic-handler", - "sp-std", + "sp-std 8.0.0", "sp-trie", "thiserror", "tracing", @@ -9008,10 +9810,10 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "aes-gcm", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519-dalek", "hkdf", "parity-scale-codec", @@ -9021,10 +9823,10 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-core", - "sp-externalities", + "sp-externalities 0.19.0", "sp-runtime", - "sp-runtime-interface", - "sp-std", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", "thiserror", "x25519-dalek 2.0.1", ] @@ -9032,50 +9834,78 @@ dependencies = [ [[package]] name = "sp-std" version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" + +[[package]] +name = "sp-std" +version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" [[package]] name = "sp-storage" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "impl-serde 0.4.0", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", +] + +[[package]] +name = "sp-storage" +version = "19.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" dependencies = [ - "impl-serde", + "impl-serde 0.5.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 14.0.0", ] [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "thiserror", ] [[package]] name = "sp-tracing" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "parity-scale-codec", + "sp-std 8.0.0", + "tracing", + "tracing-core", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "sp-tracing" +version = "16.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" dependencies = [ "parity-scale-codec", - "sp-std", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.18", ] [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "sp-api", "sp-runtime", @@ -9084,7 +9914,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "parity-scale-codec", @@ -9092,14 +9922,14 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-trie", ] [[package]] name = "sp-trie" version = "22.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ahash 0.8.11", "hash-db", @@ -9108,11 +9938,12 @@ dependencies = [ "memory-db", "nohash-hasher", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot 0.12.3", + "rand 0.8.5", "scale-info", "schnellru", "sp-core", - "sp-std", + "sp-std 8.0.0", "thiserror", "tracing", "trie-db", @@ -9122,16 +9953,16 @@ dependencies = [ [[package]] name = "sp-version" version = "22.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "scale-info", "serde", "sp-core-hashing-proc-macro", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-version-proc-macro", "thiserror", ] @@ -9139,31 +9970,42 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "sp-wasm-interface" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std", + "sp-std 8.0.0", "wasmtime", ] +[[package]] +name = "sp-wasm-interface" +version = "20.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#b4732add46910370443d092a3f479986060f6df5" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", +] + [[package]] name = "sp-weights" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "parity-scale-codec", "scale-info", @@ -9171,8 +10013,8 @@ dependencies = [ "smallvec", "sp-arithmetic", "sp-core", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", ] [[package]] @@ -9210,9 +10052,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.47.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" +checksum = "19409f13998e55816d1c728395af0b52ec066206341d939e22e7766df9b494b8" dependencies = [ "Inflector", "num-format", @@ -9229,6 +10071,66 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "staging-xcm" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "bounded-collections", + "derivative", + "environmental", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-weights", + "xcm-procedural", +] + +[[package]] +name = "staging-xcm-builder" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std 8.0.0", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "staging-xcm-executor" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 8.0.0", + "sp-weights", + "staging-xcm", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -9280,9 +10182,9 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" @@ -9299,15 +10201,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -9317,7 +10219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a7590dc041b9bc2825e52ce5af8416c73dbe9d0654402bfd4b4941938b94d8f" dependencies = [ "hmac 0.11.0", - "pbkdf2 0.8.0", + "pbkdf2", "schnorrkel 0.11.4", "sha2 0.9.9", "zeroize", @@ -9326,12 +10228,12 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -9350,7 +10252,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "hyper", "log", @@ -9362,7 +10264,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "jsonrpsee", @@ -9375,7 +10277,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -9392,7 +10294,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "futures", "tokio", @@ -9401,7 +10303,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "ansi_term", "build-helper", @@ -9416,6 +10318,12 @@ dependencies = [ "wasm-opt", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "subtle" version = "2.4.1" @@ -9435,9 +10343,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2", "quote", @@ -9485,20 +10393,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", - "rustix 0.38.32", - "windows-sys 0.52.0", + "once_cell", + "rustix 0.38.37", + "windows-sys 0.59.0", ] [[package]] @@ -9510,6 +10419,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix 0.38.37", + "windows-sys 0.59.0", +] + [[package]] name = "termtree" version = "0.4.1" @@ -9518,22 +10437,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -9573,9 +10492,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -9594,33 +10513,14 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] -[[package]] -name = "tiny-bip39" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" -dependencies = [ - "anyhow", - "hmac 0.12.1", - "once_cell", - "pbkdf2 0.11.0", - "rand 0.8.5", - "rustc-hash", - "sha2 0.10.8", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -9632,9 +10532,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -9647,32 +10547,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project-lite 0.2.14", "signal-hook-registry", - "socket2 0.5.6", + "socket2 0.5.7", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -9692,15 +10591,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.10", + "rustls 0.21.12", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite 0.2.14", @@ -9710,9 +10609,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -9720,7 +10619,6 @@ dependencies = [ "futures-sink", "pin-project-lite 0.2.14", "tokio", - "tracing", ] [[package]] @@ -9746,21 +10644,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -9771,24 +10669,24 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -9808,7 +10706,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bytes", "futures-core", "futures-util", @@ -9822,15 +10720,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -9852,7 +10750,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -9886,6 +10784,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -9905,7 +10814,7 @@ dependencies = [ "ansi_term", "chrono", "lazy_static", - "matchers", + "matchers 0.0.1", "parking_lot 0.11.2", "regex", "serde", @@ -9915,15 +10824,34 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.4", "tracing-serde", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers 0.1.0", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "time", + "tracing", + "tracing-core", + "tracing-log 0.2.0", +] + [[package]] name = "trie-db" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +checksum = "ff28e0f815c2fea41ebddf148e008b077d2faddb026c9555b29696114d602642" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -9978,7 +10906,7 @@ dependencies = [ "ipconfig", "lazy_static", "lru-cache", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "resolv-conf", "smallvec", "thiserror", @@ -9996,7 +10924,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" dependencies = [ "async-trait", "clap", @@ -10013,8 +10941,8 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-babe", "sp-core", - "sp-debug-derive", - "sp-externalities", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", "sp-inherents", "sp-io", "sp-keystore", @@ -10055,9 +10983,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -10071,38 +10999,50 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -10111,7 +11051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -10140,9 +11080,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -10157,9 +11097,9 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -10175,9 +11115,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "void" @@ -10185,6 +11125,30 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "w3f-bls" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "arrayref", + "constcat", + "digest 0.10.7", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "sha2 0.10.8", + "sha3", + "thiserror", + "zeroize", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -10218,34 +11182,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -10255,9 +11220,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10265,22 +11230,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-instrument" @@ -10302,9 +11267,9 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.114.2" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effbef3bd1dde18acb401f73e740a6f3d4a1bc651e9773bddc512fe4d8d68f67" +checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" dependencies = [ "anyhow", "libc", @@ -10318,9 +11283,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.114.2" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c09e24eb283919ace2ed5733bda4842a59ce4c8de110ef5c6d98859513d17047" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" dependencies = [ "anyhow", "cxx", @@ -10330,9 +11295,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.114.2" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f2f817bed2e8d65eb779fa37317e74de15585751f903c9118342d1970703a4" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" dependencies = [ "anyhow", "cc", @@ -10398,9 +11363,9 @@ dependencies = [ [[package]] name = "wasmparser-nostd" -version = "0.100.1" +version = "0.100.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" dependencies = [ "indexmap-nostd", ] @@ -10602,9 +11567,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -10644,14 +11609,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.37", ] [[package]] name = "wide" -version = "0.7.15" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" +checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" dependencies = [ "bytemuck", "safe_arch", @@ -10681,11 +11646,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -10719,7 +11684,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] @@ -10746,7 +11711,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -10781,17 +11755,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -10808,9 +11783,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -10826,9 +11801,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -10844,9 +11819,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -10862,9 +11843,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -10880,9 +11861,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -10898,9 +11879,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -10916,9 +11897,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -10929,6 +11910,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -10965,7 +11955,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "serde", "zeroize", @@ -10989,6 +11979,17 @@ dependencies = [ "time", ] +[[package]] +name = "xcm-procedural" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-v1.4.0#fcfdb98ab5c5fce91cce339c8d3e4f5f5844bbe1" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "yamux" version = "0.10.2" @@ -10998,7 +11999,7 @@ dependencies = [ "futures", "log", "nohash-hasher", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "static_assertions", ] @@ -11014,29 +12015,30 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -11049,7 +12051,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.82", ] [[package]] @@ -11092,9 +12094,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 4bfe6aa5d..30117c058 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "5.3.0" +version = "6.1.0" authors = ["Cerebellum-Network"] edition = "2021" homepage = "https://cere.network/" @@ -19,6 +19,9 @@ members = [ "pallets/ddc-staking", "pallets/erc20", "pallets/erc721", + "pallets/ddc-clusters-gov", + "pallets/origins", + "pallets/ddc-verification", "primitives", "runtime/cere", "runtime/cere-dev", @@ -27,143 +30,151 @@ resolver = "2" [workspace.dependencies] # 3rd-party dependencies +base64ct = { version = "1.6.0" } +blake2 = { version = "0.10.6", default-features = false } byte-unit = { version = "4.0.19", default-features = false, features = ["u128"] } chrono = { version = "0.4.31", default-features = false } -clap = { version = "4.2.5", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -futures = { version = "0.3.21" } -hex-literal = { version = "^0.3.1", default-features = false } -jsonrpsee = { version = "0.16.2", default-features = false, features = ["server"] } +clap = { version = "4.4.6", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.6.4", default-features = false, features = ["derive", "max-encoded-len"] } +futures = { version = "0.3.28" } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex-literal = { version = "^0.4.1", default-features = false } +jsonrpsee = { version = "0.16.3", default-features = false, features = ["server"] } lazy_static = { version = "1.4.0", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { version = "0.4.20", default-features = false } parking_lot = { version = "0.12.1", default-features = false } +polkadot-ckb-merkle-mountain-range = { version = "0.7.0", default-features = false } rand = { version = "0.8", default-features = false } -rand_chacha = { version = "0.2", default-features = false } -scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } -serde = { version = "1.0.136", default-features = false, features = ["derive"] } +rand_chacha = { version = "0.2.2", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.188", default-features = false, features = ["derive"] } +serde_json = { version = "1.0.107", default-features = false } static_assertions = { version = "1.1.0" } url = { version = "2.4.1" } +array-bytes = { version = "6.1" } +itertools = { version = "0.13.0", default-features = false, features = ["use_alloc"] } # Substrate Dependencies # Please keey format such that: # dependency-name = { git = "X", tag = "Y", default-features = false } -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -frame-election-provider-support = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -frame-try-runtime = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -node-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-bags-list = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-child-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-collective = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-contracts = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-contracts-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-democracy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-election-provider-multi-phase = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-elections-phragmen = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-fast-unstake = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-identity = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-im-online = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-indices = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-insecure-randomness-collective-flip = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-membership = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-nomination-pools = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-offences = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-offences-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-preimage = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-proxy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-recovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-scheduler = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false, features = ["historical"] } -pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-staking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-staking-reward-curve = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-tips = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-treasury = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-vesting = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-conviction-voting = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-babe-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-epochs = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-consensus-slots = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-executor = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-network = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-network-common = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-service = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-sync-state = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-sync-state-rpc = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-sysinfo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus-babe-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus-epochs = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-consensus-grandpa-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-keystore = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-staking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-storage = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-transaction-storage-proof = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -sp-version = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.1.0", default-features = false } -substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -substrate-test-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } -try-runtime-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +frame-election-provider-support = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false, features = ["tuples-96"] } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +frame-try-runtime = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +node-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-bags-list = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-child-bounties = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-collective = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-contracts = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-contracts-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-democracy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-election-provider-multi-phase = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-elections-phragmen = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-fast-unstake = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-identity = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-im-online = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-indices = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-insecure-randomness-collective-flip = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-membership = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-nomination-pools = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-offences = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-offences-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-preimage = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-proxy = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-recovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-scheduler = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false, features = ["historical"] } +pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-staking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-staking-reward-curve = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-tips = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-treasury = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-vesting = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-conviction-voting = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-babe-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-epochs = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-consensus-slots = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-executor = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-network = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-network-common = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-service = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-sync-state = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-sync-state-rpc = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-sysinfo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-authority-discovery = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus-babe-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus-epochs = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-consensus-grandpa-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false, features = ["serde"] } +sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-keystore = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-staking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-storage = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-transaction-storage-proof = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-trie = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +sp-version = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.4.0", default-features = false } +substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +substrate-test-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } +try-runtime-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.4.0", default-features = false } # Cere Dependenies cere-client = { path = "node/client" } @@ -175,12 +186,15 @@ cere-service = { path = "node/service" } ddc-primitives = { path = "primitives", default-features = false } pallet-chainbridge = { path = "pallets/chainbridge", default-features = false } pallet-ddc-clusters = { path = "pallets/ddc-clusters", default-features = false } +pallet-ddc-clusters-gov = { path = "pallets/ddc-clusters-gov", default-features = false } pallet-ddc-customers = { path = "pallets/ddc-customers", default-features = false } pallet-ddc-nodes = { path = "pallets/ddc-nodes", default-features = false } pallet-ddc-payouts = { path = "pallets/ddc-payouts", default-features = false } pallet-ddc-staking = { path = "pallets/ddc-staking", default-features = false } +pallet-ddc-verification = { path = "pallets/ddc-verification", default-features = false } pallet-erc20 = { path = "pallets/erc20", default-features = false } pallet-erc721 = { path = "pallets/erc721", default-features = false } +pallet-origins = { path = "pallets/origins", default-features = false } [profile.release] panic = "unwind" diff --git a/Dockerfile.tests b/Dockerfile.tests index b591c5cb8..4d4059618 100644 --- a/Dockerfile.tests +++ b/Dockerfile.tests @@ -25,3 +25,4 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && \ export PATH=$PATH:$HOME/.cargo/bin && \ scripts/init.sh && \ TRYBUILD=overwrite cargo test --workspace --locked --release --verbose --features runtime-benchmarks --manifest-path node/cli/Cargo.toml + diff --git a/node/client/src/lib.rs b/node/client/src/lib.rs index ef9e2c372..c7dc2ac14 100644 --- a/node/client/src/lib.rs +++ b/node/client/src/lib.rs @@ -3,7 +3,8 @@ use std::sync::Arc; use node_primitives::Nonce; pub use node_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Header, Signature}; use sc_client_api::{ - AuxStore, Backend as BackendT, BlockchainEvents, KeysIter, PairsIter, UsageProvider, + AuxStore, Backend as BackendT, BlockchainEvents, KeysIter, MerkleValue, PairsIter, + UsageProvider, }; pub use sc_executor::NativeElseWasmExecutor; use sp_api::{CallApiAt, NumberFor, ProvideRuntimeApi}; @@ -373,6 +374,37 @@ impl sc_client_api::StorageProvider for Client { } } } + + /// Given a block's `Hash` and a key, return the closest merkle value. + fn closest_merkle_value( + &self, + hash: ::Hash, + key: &StorageKey, + ) -> sp_blockchain::Result::Hash>>> { + with_client! { + self, + client, + { + client.closest_merkle_value(hash, key) + } + } + } + + /// Given a block's `Hash`, a key and a child storage key, return the closest merkle value. + fn child_closest_merkle_value( + &self, + hash: ::Hash, + child_info: &ChildInfo, + key: &StorageKey, + ) -> sp_blockchain::Result::Hash>>> { + with_client! { + self, + client, + { + client.child_closest_merkle_value(hash, child_info, key) + } + } + } } impl sp_blockchain::HeaderBackend for Client { diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index 5b26c016c..e2e3f30c4 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -55,6 +55,7 @@ cere-runtime-common = { workspace = true, optional = true } cere-dev-runtime = { workspace = true, optional = true } cere-runtime = { workspace = true, optional = true } +ddc-primitives = { workspace = true } [features] default = ["cere-native"] diff --git a/node/service/chain-specs/example.json b/node/service/chain-specs/example.json index 5ad984508..008ca543f 100644 --- a/node/service/chain-specs/example.json +++ b/node/service/chain-specs/example.json @@ -95,40 +95,10 @@ ] ] }, - "democracy": { - "phantom": null - }, - "council": { - "phantom": null, - "members": [] - }, - "technicalCommittee": { - "phantom": null, - "members": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" - ] - }, - "elections": { - "members": [ - [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - 10000000000 - ], - [ - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - 10000000000 - ] - ] - }, - "technicalMembership": { - "members": [], - "phantom": null - }, "grandpa": { "authorities": [] }, - "treasury": null, + "treasury": {}, "sudo": { "key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }, @@ -138,14 +108,6 @@ "authorityDiscovery": { "keys": [] }, - "society": { - "pot": 0, - "members": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" - ], - "maxMembers": 999 - }, "vesting": { "vesting": [] }, @@ -154,18 +116,25 @@ "minCreateBond": 0, "maxPools": 16, "maxMembersPerPool": 32, - "maxMembers": 512 + "maxMembers": 512, + "globalMaxCommission": null }, "ddcStaking": { - "storages": [] + "storages": [], + "clusters": [] }, "ddcCustomers": { + "feederAccount": null, "buckets": [ [ - "0x0000000000000000000000000000000000000001", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - 10000000000, - false + { + "bucket_id": 1, + "owner_id": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "cluster_id": "0x0000000000000000000000000000000000000001", + "is_public": true, + "is_removed": false + }, + 10000000000 ] ] }, @@ -204,11 +173,15 @@ "manager_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "reserve_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "props": { - "node_provider_auth_contract": null - } + "node_provider_auth_contract": null, + "erasure_coding_required": 4, + "erasure_coding_total": 6, + "replication_total": 3 + }, + "status": "Activated" } ], - "clustersGovParams": [ + "clustersProtocolParams": [ [ "0x0000000000000000000000000000000000000001", { @@ -229,14 +202,19 @@ [ "0x0000000000000000000000000000000000000001", [ - { - "StoragePubKey": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" - } + [ + { + "StoragePubKey": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" + }, + "Genesis", + "ValidationSucceeded" + ] ] ] ] }, "ddcPayouts": { + "feederAccount": null, "authorisedCaller": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "debtorCustomers": [ [ @@ -248,4 +226,4 @@ } } } -} \ No newline at end of file +} diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index 05f71ff88..9a1c7aea3 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -4,6 +4,7 @@ use cere_dev_runtime as cere_dev; use cere_runtime as cere; #[cfg(feature = "cere-dev-native")] use cere_runtime_common::constants::currency::DOLLARS as TEST_UNITS; +use ddc_primitives::sr25519::AuthorityId as DdcVerificationId; use jsonrpsee::core::__reexports::serde_json; pub use node_primitives::{AccountId, Balance, Block, Signature}; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; @@ -74,7 +75,8 @@ where // Helper function to generate stash, controller and session key from seed pub fn authority_keys_from_seed( seed: &str, -) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId) { +) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, DdcVerificationId) +{ ( get_account_id_from_seed::(&format!("{}//stash", seed)), get_account_id_from_seed::(seed), @@ -82,6 +84,7 @@ pub fn authority_keys_from_seed( get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), + get_from_seed::(seed), ) } @@ -91,14 +94,14 @@ fn cere_dev_session_keys( babe: BabeId, im_online: ImOnlineId, authority_discovery: AuthorityDiscoveryId, + ddc_verification: DdcVerificationId, ) -> cere_dev::SessionKeys { - cere_dev::SessionKeys { grandpa, babe, im_online, authority_discovery } + cere_dev::SessionKeys { grandpa, babe, im_online, authority_discovery, ddc_verification } } /// Helper function to create Cere Dev `RuntimeGenesisConfig` for testing #[cfg(feature = "cere-dev-native")] pub fn cere_dev_genesis( - wasm_binary: &[u8], initial_authorities: Vec<( AccountId, AccountId, @@ -106,6 +109,7 @@ pub fn cere_dev_genesis( BabeId, ImOnlineId, AuthorityDiscoveryId, + DdcVerificationId, )>, initial_nominators: Vec, root_key: AccountId, @@ -161,11 +165,7 @@ pub fn cere_dev_genesis( const STASH: Balance = ENDOWMENT / 1000; cere_dev::RuntimeGenesisConfig { - system: cere_dev::SystemConfig { - // Add Wasm runtime to storage. - code: wasm_binary.to_vec(), - ..Default::default() - }, + system: cere_dev::SystemConfig::default(), balances: cere_dev::BalancesConfig { // Configure endowed accounts with initial balance of 1 << 60. balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(), @@ -178,7 +178,13 @@ pub fn cere_dev_genesis( ( x.0.clone(), x.0.clone(), - cere_dev_session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()), + cere_dev_session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + ), ) }) .collect::>(), @@ -212,6 +218,14 @@ pub fn cere_dev_genesis( ddc_clusters: Default::default(), ddc_nodes: Default::default(), ddc_payouts: Default::default(), + tech_comm: cere_dev::TechCommConfig { + members: endowed_accounts + .iter() + .take((endowed_accounts.len() + 1) / 2) + .cloned() + .collect(), + phantom: Default::default(), + }, } } @@ -229,9 +243,8 @@ pub fn cere_dev_native_chain_spec_properties() -> serde_json::map::Map cere_dev::RuntimeGenesisConfig { +fn cere_dev_config_genesis() -> cere_dev::RuntimeGenesisConfig { cere_dev_genesis( - wasm_binary, // Initial authorities vec![authority_keys_from_seed("Alice")], // Initial nominators @@ -252,26 +265,31 @@ fn cere_dev_config_genesis(wasm_binary: &[u8]) -> cere_dev::RuntimeGenesisConfig pub fn cere_dev_development_config() -> Result { let wasm_binary = cere_dev::WASM_BINARY.ok_or("Cere Dev development wasm not available")?; + #[allow(deprecated)] Ok(CereDevChainSpec::from_genesis( "Development", "cere_dev", ChainType::Development, - move || cere_dev_config_genesis(wasm_binary), + cere_dev_config_genesis, vec![], None, Some(DEFAULT_PROTOCOL_ID), None, Some(cere_dev_native_chain_spec_properties()), Default::default(), + wasm_binary, )) } #[cfg(feature = "cere-dev-native")] -fn cere_dev_local_testnet_genesis(wasm_binary: &[u8]) -> cere_dev::RuntimeGenesisConfig { +fn cere_dev_local_testnet_genesis() -> cere_dev::RuntimeGenesisConfig { cere_dev_genesis( - wasm_binary, // Initial authorities - vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], + vec![ + authority_keys_from_seed("Alice"), + authority_keys_from_seed("Bob"), + authority_keys_from_seed("Charlie"), + ], // Initial nominators vec![], // Sudo account @@ -290,17 +308,19 @@ fn cere_dev_local_testnet_genesis(wasm_binary: &[u8]) -> cere_dev::RuntimeGenesi pub fn cere_dev_local_testnet_config() -> Result { let wasm_binary = cere_dev::WASM_BINARY.ok_or("Cere Dev development wasm not available")?; + #[allow(deprecated)] Ok(CereDevChainSpec::from_genesis( "Local Testnet", "cere_dev_local_testnet", ChainType::Local, - move || cere_dev_local_testnet_genesis(wasm_binary), + cere_dev_local_testnet_genesis, vec![], None, Some(DEFAULT_PROTOCOL_ID), None, None, Default::default(), + wasm_binary, )) } diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index 27e9d89f4..2028ae451 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -397,6 +397,7 @@ where import_queue, block_announce_validator_builder: None, warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + block_relay: None, })?; if config.offchain_worker.enabled { diff --git a/pallets/chainbridge/src/mock.rs b/pallets/chainbridge/src/mock.rs index 4f8058410..4caae1fb4 100644 --- a/pallets/chainbridge/src/mock.rs +++ b/pallets/chainbridge/src/mock.rs @@ -66,6 +66,7 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type ReserveIdentifier = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = (); type RuntimeHoldReason = (); diff --git a/pallets/ddc-clusters-gov/Cargo.toml b/pallets/ddc-clusters-gov/Cargo.toml new file mode 100644 index 000000000..7b89960b2 --- /dev/null +++ b/pallets/ddc-clusters-gov/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "pallet-ddc-clusters-gov" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true + +[dependencies] +codec = { workspace = true } +ddc-primitives = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +hex-literal = { workspace = true } +log = { workspace = true } +pallet-referenda = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +frame-benchmarking = { workspace = true, default-features = true } +lazy_static = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-contracts = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-ddc-clusters = { workspace = true, default-features = true } +pallet-ddc-nodes = { workspace = true } +pallet-ddc-staking = { workspace = true } +pallet-insecure-randomness-collective-flip = { workspace = true, default-features = true } +pallet-preimage = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-timestamp = { workspace = true, default-features = true } +parking_lot = { workspace = true, default-features = true } +sp-arithmetic = { workspace = true } +sp-core = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } +sp-tracing = { workspace = true, default-features = true } +substrate-test-utils = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "ddc-primitives/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "ddc-primitives/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-system/try-runtime", + "pallet-referenda/try-runtime", +] diff --git a/pallets/ddc-clusters-gov/src/benchmarking.rs b/pallets/ddc-clusters-gov/src/benchmarking.rs new file mode 100644 index 000000000..837ff2353 --- /dev/null +++ b/pallets/ddc-clusters-gov/src/benchmarking.rs @@ -0,0 +1,541 @@ +//! DdcClustersGov pallet benchmarking. +#![cfg(feature = "runtime-benchmarks")] + +use ddc_primitives::{ + ClusterBondingParams, ClusterId, ClusterNodeKind, ClusterParams, ClusterProtocolParams, + NodeParams, StorageNodeMode, StorageNodeParams, StorageNodePubKey, +}; +use frame_benchmarking::{account, benchmarks}; +use frame_system::RawOrigin; +use pallet_referenda::Pallet as Referenda; +use sp_runtime::{Perquintill, SaturatedConversion}; +use sp_std::prelude::*; + +use super::*; +use crate::Pallet as DdcClustersGov; + +/// Grab a funded user with max Balance. +pub fn create_funded_user_with_balance( + name: &'static str, + n: u32, + balance_factor: u128, +) -> T::AccountId { + let user = account(name, n, 0); + let balance = ::Currency::minimum_balance() * + balance_factor.saturated_into::>(); + let _ = ::Currency::make_free_balance_be(&user, balance); + user +} + +pub fn fund_user(user: T::AccountId, balance_factor: u128) -> T::AccountId { + let balance = ::Currency::minimum_balance() * + balance_factor.saturated_into::>(); + let _ = ::Currency::make_free_balance_be(&user, balance); + user +} + +pub fn create_cluster_with_nodes( + cluster_id: ClusterId, + cluster_manager_id: T::AccountId, + cluster_reserve_id: T::AccountId, + nodes_keys: Vec<(NodePubKey, T::AccountId)>, + is_activated: bool, +) { + let bond_size: BalanceOf = 10000_u32.saturated_into::>(); + let cluster_protocol_params = ClusterProtocolParams { + treasury_share: Perquintill::from_percent(10), + validators_share: Perquintill::from_percent(20), + cluster_reserve_share: Perquintill::from_percent(30), + storage_bond_size: bond_size, + storage_chill_delay: BlockNumberFor::::from(20_u32), + storage_unbonding_delay: BlockNumberFor::::from(20_u32), + unit_per_mb_stored: 97656, + unit_per_mb_streamed: 48828, + unit_per_put_request: 10, + unit_per_get_request: 5, + }; + + let cluster_params = ClusterParams { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0, + }; + + T::ClusterCreator::create_cluster( + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + cluster_params, + cluster_protocol_params.clone(), + ) + .expect("Cluster is not created"); + + T::StakerCreator::bond_cluster( + cluster_reserve_id.clone(), + cluster_manager_id.clone(), + cluster_id, + ) + .expect("Cluster could not be bonded"); + + for (node_pub_key, node_provider) in nodes_keys.iter() { + let node_params = NodeParams::StorageParams(StorageNodeParams { + mode: StorageNodeMode::Storage, + host: vec![1u8; 255], + domain: vec![2u8; 255], + ssl: true, + http_port: 8080_u16, + grpc_port: 9090_u16, + p2p_port: 9070_u16, + }); + + T::NodeCreator::create_node(node_pub_key.clone(), node_provider.clone(), node_params) + .expect("Node is not created"); + + T::StakerCreator::bond_stake_and_participate( + node_provider.clone(), + node_provider.clone(), + node_pub_key.clone(), + bond_size, + cluster_id, + ) + .expect("Staking is not created"); + + T::ClusterManager::add_node(&cluster_id, node_pub_key, &ClusterNodeKind::Genesis) + .expect("Node could not be added to the cluster"); + + T::ClusterManager::validate_node(&cluster_id, node_pub_key, true) + .expect("Node could not be validated in the cluster"); + } + + if is_activated { + T::ClusterProtocol::activate_cluster_protocol(&cluster_id) + .expect("Could not activate cluster"); + } +} + +fn next_block() { + frame_system::Pallet::::set_block_number( + frame_system::Pallet::::block_number() + BlockNumberFor::::from(1_u32), + ); +} + +fn fast_forward_to(n: BlockNumberFor) { + while frame_system::Pallet::::block_number() < n { + next_block::(); + } +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +fn assert_has_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + +benchmarks! { + + propose_activate_cluster_protocol { + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + + }: propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id), cluster_id, ClusterProtocolParams::default()) + verify { + assert!(ClusterProposal::::contains_key(cluster_id)); + assert!(ClusterProposalVoting::::contains_key(cluster_id)); + } + + propose_update_cluster_protocol { + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes, true); + + }: propose_update_cluster_protocol(RawOrigin::Signed(cluster_manager_id), cluster_id, ClusterProtocolParams::default(), ClusterMember::ClusterManager) + verify { + assert!(ClusterProposal::::contains_key(cluster_id)); + assert!(ClusterProposalVoting::::contains_key(cluster_id)); + } + + vote_proposal { + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + let (node_pub_key_1, node_provider_1) = cluster_nodes.first() + .map(|(key, provider)| (key.clone(), provider.clone())) + .unwrap(); + + }: vote_proposal(RawOrigin::Signed(node_provider_1.clone()), cluster_id, true, ClusterMember::NodeProvider(node_pub_key_1.clone())) + verify { + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![node_provider_1.clone()]); + } + + close_early_approved { + let m in 4 .. 64; // nodes range + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + let _ = fund_user::(DdcClustersGov::::account_id(), 1000); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + + for i in 0 .. m { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + for j in 0 .. m { + let (node_pub_key, node_provider) = &cluster_nodes.get(j as usize).unwrap(); + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(node_provider.clone()).into(), + cluster_id, + true, + ClusterMember::NodeProvider(node_pub_key.clone()), + )?; + } + + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(cluster_manager_id.clone()).into(), + cluster_id, + true, + ClusterMember::ClusterManager, + )?; + + }: close_proposal(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id, ClusterMember::ClusterManager) + verify { + let cluster_id = ClusterId::from([1; 20]); + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + assert_has_event::(Event::Approved { cluster_id }.into()); + assert_last_event::(Event::Removed { cluster_id }.into()); + } + + close_approved { + let m in 4 .. 64; // nodes range + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + let _ = fund_user::(DdcClustersGov::::account_id(), 1000); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + + for i in 0 .. m { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + fn is_unanimous() -> bool { + let max_seats = 100; + max_seats == T::SeatsConsensus::get_threshold(max_seats) + } + + fn is_prime_vote() -> bool { + T::DefaultVote::default_vote(Some(true), 0, 0, 0) + } + + if is_prime_vote::() && !is_unanimous::() { + + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(cluster_manager_id.clone()).into(), + cluster_id, + true, + ClusterMember::ClusterManager, + )?; + + } else { + + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(cluster_manager_id.clone()).into(), + cluster_id, + true, + ClusterMember::ClusterManager, + )?; + + for j in 0 .. m { + let (node_pub_key, node_provider) = &cluster_nodes.get(j as usize).unwrap(); + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(node_provider.clone()).into(), + cluster_id, + true, + ClusterMember::NodeProvider(node_pub_key.clone()), + )?; + } + } + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + fast_forward_to::(votes.end + BlockNumberFor::::from(1_u32)); + + }: close_proposal(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id, ClusterMember::ClusterManager) + verify { + let cluster_id = ClusterId::from([1; 20]); + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + assert_has_event::(Event::Approved { cluster_id }.into()); + assert_last_event::(Event::Removed { cluster_id }.into()); + } + + close_early_disapproved { + let m in 4 .. 64; // nodes range + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + + for i in 0 .. m { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + for j in 0 .. m { + let (node_pub_key, node_provider) = &cluster_nodes.get(j as usize).unwrap(); + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(node_provider.clone()).into(), + cluster_id, + false, + ClusterMember::NodeProvider(node_pub_key.clone()), + )?; + } + + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(cluster_manager_id.clone()).into(), + cluster_id, + false, + ClusterMember::ClusterManager, + )?; + + }: close_proposal(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id, ClusterMember::ClusterManager) + verify { + let cluster_id = ClusterId::from([1; 20]); + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + assert_has_event::(Event::Disapproved { cluster_id }.into()); + assert_last_event::(Event::Removed { cluster_id }.into()); + } + + close_disapproved { + let m in 4 .. 64; // nodes range + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + + for i in 0 .. m { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + fast_forward_to::(votes.end + BlockNumberFor::::from(1_u32)); + + }: close_proposal(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id, ClusterMember::ClusterManager) + verify { + let cluster_id = ClusterId::from([1; 20]); + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + assert_has_event::(Event::Disapproved { cluster_id }.into()); + } + + retract_proposal { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + assert!(ClusterProposal::::contains_key(cluster_id)); + assert!(ClusterProposalVoting::::contains_key(cluster_id)); + + }: retract_proposal(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id) + verify { + let cluster_id = ClusterId::from([1; 20]); + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + assert_last_event::(Event::Removed { cluster_id }.into()); + } + + refund_submission_deposit { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + let _ = fund_user::(DdcClustersGov::::account_id(), 1000); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + DdcClustersGov::::propose_activate_cluster_protocol(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterProtocolParams::default())?; + + for i in 0 .. 3 { + let (node_pub_key, node_provider) = &cluster_nodes.get(i as usize).unwrap(); + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(node_provider.clone()).into(), + cluster_id, + true, + ClusterMember::NodeProvider(node_pub_key.clone()), + )?; + } + + DdcClustersGov::::vote_proposal( + RawOrigin::Signed(cluster_manager_id.clone()).into(), + cluster_id, + true, + ClusterMember::ClusterManager, + )?; + + DdcClustersGov::::close_proposal(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id, ClusterMember::ClusterManager).expect("Could not close proposal"); + + let referenda_index = pallet_referenda::ReferendumCount::::get() - 1; + + assert!(SubmissionDeposits::::contains_key(referenda_index)); + + Referenda::::place_decision_deposit(RawOrigin::Signed(cluster_manager_id.clone()).into(), referenda_index).expect("Could not place decision deposit"); + Referenda::::cancel(RawOrigin::Root.into(), referenda_index).expect("Could not cancel referendum"); + + }: refund_submission_deposit(RawOrigin::Signed(cluster_manager_id.clone()), referenda_index) + verify { + assert!(!SubmissionDeposits::::contains_key(referenda_index)); + } + + activate_cluster_protocol { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), false); + next_block::(); + + }: activate_cluster_protocol(RawOrigin::Root, cluster_id, ClusterProtocolParams::default()) + verify { + let cluster_id = ClusterId::from([1; 20]); + let cluster_status = >::get_cluster_status(&cluster_id).unwrap(); + assert_eq!(cluster_status, ClusterStatus::Activated); + } + + update_cluster_protocol { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5); + + let mut cluster_nodes: Vec<(NodePubKey, T::AccountId)> = Vec::new(); + for i in 0 .. 3 { + let node_provider = create_funded_user_with_balance::("node-provider", i, 5); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([i as u8; 32])); + cluster_nodes.push((node_pub_key.clone(), node_provider.clone())); + } + + create_cluster_with_nodes::(cluster_id, cluster_manager_id.clone(), cluster_reserve_id.clone(), cluster_nodes.clone(), true); + next_block::(); + + }: update_cluster_protocol(RawOrigin::Root, cluster_id, ClusterProtocolParams { + treasury_share: Perquintill::from_percent(5), + validators_share: Perquintill::from_percent(10), + cluster_reserve_share: Perquintill::from_percent(15), + storage_bond_size: 10000_u128.saturated_into::>(), + storage_chill_delay: BlockNumberFor::::from(20_u32), + storage_unbonding_delay: BlockNumberFor::::from(20_u32), + unit_per_mb_stored: 97656, + unit_per_mb_streamed: 48828, + unit_per_put_request: 10, + unit_per_get_request: 5, + }) + verify { + let cluster_id = ClusterId::from([1; 20]); + let bonding_params = T::ClusterProtocol::get_bonding_params(&cluster_id).unwrap(); + let updated_bonding = ClusterBondingParams { + storage_bond_size: 10000_u128, + storage_chill_delay: BlockNumberFor::::from(20_u32), + storage_unbonding_delay: BlockNumberFor::::from(20_u32) + }; + assert_eq!(bonding_params, updated_bonding); + } + +} diff --git a/pallets/ddc-clusters-gov/src/lib.rs b/pallets/ddc-clusters-gov/src/lib.rs new file mode 100644 index 000000000..788ee0656 --- /dev/null +++ b/pallets/ddc-clusters-gov/src/lib.rs @@ -0,0 +1,809 @@ +//! # DDC Nodes Pallet +//! +//! The DDC Clusters pallet is used to manage DDC Clusters +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +//! +//! ## GenesisConfig +//! +//! The DDC Clusters pallet depends on the [`GenesisConfig`]. The +//! `GenesisConfig` is optional and allow to set some initial nodes in DDC. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +use codec::{Decode, Encode}; +#[cfg(feature = "runtime-benchmarks")] +use ddc_primitives::traits::{node::NodeCreator, staking::StakerCreator}; +use ddc_primitives::{ + traits::{ + cluster::{ClusterCreator, ClusterManager, ClusterProtocol, ClusterQuery}, + cluster_gov::{DefaultVote, MemberCount, SeatsConsensus}, + node::NodeVisitor, + pallet::GetDdcOrigin, + }, + ClusterId, ClusterNodeStatus, ClusterProtocolParams, ClusterStatus, NodePubKey, +}; +use frame_support::{ + dispatch::{GetDispatchInfo, Pays}, + pallet_prelude::*, + traits::{ + schedule::DispatchTime, Currency, ExistenceRequirement, LockableCurrency, OriginTrait, + StorePreimage, UnfilteredDispatchable, + }, + weights::Weight, +}; +use frame_system::pallet_prelude::*; +pub use frame_system::Config as SysConfig; +pub use pallet::*; +use pallet_referenda::ReferendumIndex; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{AccountIdConversion, Dispatchable}, + DispatchError, DispatchResult, RuntimeDebug, SaturatedConversion, +}; +use sp_std::prelude::*; + +#[cfg(test)] +pub(crate) mod mock; +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::WeightInfo; + +/// The balance type of this pallet. +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +pub type ReferendaCall = pallet_referenda::Call; + +/// Info for keeping track of a proposal being voted on. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct Votes { + /// The max number of members that can vote on this proposal. + seats: MemberCount, + /// The number of approval votes that are needed to pass the proposal. + threshold: MemberCount, + /// The current set of voters that approved it. + ayes: Vec, + /// The current set of voters that rejected it. + nays: Vec, + /// Block at which proposal was inited. + start: BlockNumber, + /// The hard end time of this vote. + end: BlockNumber, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub enum ClusterMember { + ClusterManager, + NodeProvider(NodePubKey), +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct Proposal { + author: AccountId, + kind: ProposalKind, + call: Call, +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum ProposalKind { + ActivateClusterProtocol, + UpdateClusterProtocol, +} + +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct SubmissionDeposit { + depositor: AccountId, + amount: u128, +} + +#[frame_support::pallet] +pub mod pallet { + use frame_support::PalletId; + + use super::*; + + /// The current storage version. + const STORAGE_VERSION: frame_support::traits::StorageVersion = + frame_support::traits::StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_referenda::Config { + type PalletId: Get; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Currency: LockableCurrency>; + type WeightInfo: WeightInfo; + type OpenGovActivatorTrackOrigin: GetDdcOrigin; + type OpenGovActivatorOrigin: EnsureOrigin; + type OpenGovUpdaterTrackOrigin: GetDdcOrigin; + type OpenGovUpdaterOrigin: EnsureOrigin; + type ClusterProposalDuration: Get>; + type ClusterProposalCall: Parameter + + From> + + Dispatchable + + IsType<::RuntimeCall> + + GetDispatchInfo; + type ClusterCreator: ClusterCreator>; + type ClusterManager: ClusterManager; + type ClusterProtocol: ClusterProtocol>; + type NodeVisitor: NodeVisitor; + type SeatsConsensus: SeatsConsensus; + type DefaultVote: DefaultVote; + type MinValidatedNodesCount: Get; + type ReferendumEnactmentDuration: Get>; + #[cfg(feature = "runtime-benchmarks")] + type NodeCreator: NodeCreator; + #[cfg(feature = "runtime-benchmarks")] + type StakerCreator: StakerCreator>; + } + + #[pallet::storage] + #[pallet::getter(fn proposal_of)] + pub type ClusterProposal = StorageMap< + _, + Identity, + ClusterId, + Proposal, + OptionQuery, + >; + + /// Votes on a given cluster proposal, if it is ongoing. + #[pallet::storage] + #[pallet::getter(fn voting)] + pub type ClusterProposalVoting = + StorageMap<_, Identity, ClusterId, Votes>, OptionQuery>; + + /// Public referendums initiated by clusters + #[pallet::storage] + #[pallet::getter(fn submission_depositor)] + pub type SubmissionDeposits = + StorageMap<_, Identity, ReferendumIndex, SubmissionDeposit, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// A proposal (given hash) has been proposed (by given account) with a threshold (given + /// `MemberCount`). + Proposed { account: T::AccountId, cluster_id: ClusterId, threshold: MemberCount }, + /// A proposal (given hash) has been voted on by given account, leaving + /// a tally (yes votes and no votes given respectively as `MemberCount`). + Voted { + account: T::AccountId, + cluster_id: ClusterId, + voted: bool, + yes: MemberCount, + no: MemberCount, + }, + /// A proposal was approved by the required threshold. + Approved { cluster_id: ClusterId }, + /// A proposal was not approved by the required threshold. + Disapproved { cluster_id: ClusterId }, + /// A proposal was executed; result will be `Ok` if it returned without error. + ReferendumSubmitted { cluster_id: ClusterId }, + /// A proposal was closed because its threshold was reached or after its duration was up. + Closed { cluster_id: ClusterId, yes: MemberCount, no: MemberCount }, + /// A proposal was not removed by its author. + Removed { cluster_id: ClusterId }, + /// The submission deposit has been refunded. + SubmissionDepositRetained { + /// Index of the referendum. + referenda_index: ReferendumIndex, + /// The account who placed the deposit. + depositor: T::AccountId, + /// The amount placed by the account. + amount: BalanceOf, + }, + /// The submission deposit has been refunded. + SubmissionDepositRefunded { + /// Index of the referendum. + referenda_index: ReferendumIndex, + /// The account who placed the deposit. + depositor: T::AccountId, + /// The amount placed by the account. + amount: BalanceOf, + }, + } + + #[pallet::error] + pub enum Error { + /// Account is not a member + NotClusterMember, + /// Account is not a cluster manager + NotClusterManager, + /// Account is not a member + NotValidatedNode, + /// Cluster does not exist + NoCluster, + /// Cluster does not contain this node + NoClusterNode, + /// Account is not proposal author + NotProposalAuthor, + /// Proposal must exist + ProposalMissing, + /// Active proposal is ongoing + ActiveProposal, + /// Duplicate vote ignored + DuplicateVote, + /// The close call was made too early, before the end of the voting. + TooEarly, + AwaitsValidation, + NotEnoughValidatedNodes, + UnexpectedState, + VoteProhibited, + NoSubmissionDeposit, + NotNodeProvider, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::propose_activate_cluster_protocol())] + pub fn propose_activate_cluster_protocol( + origin: OriginFor, + cluster_id: ClusterId, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + Self::ensure_cluster_manager(caller_id.clone(), cluster_id)?; + + ensure!(!>::contains_key(cluster_id), Error::::ActiveProposal); + + let cluster_status = + >::get_cluster_status(&cluster_id) + .map_err(|_| Error::::NoCluster)?; + ensure!(cluster_status == ClusterStatus::Bonded, Error::::UnexpectedState); + + let cluster_nodes_stats = T::ClusterManager::get_nodes_stats(&cluster_id) + .map_err(|_| Error::::NoCluster)?; + ensure!(cluster_nodes_stats.await_validation == 0, Error::::AwaitsValidation); + ensure!( + cluster_nodes_stats.validation_succeeded >= T::MinValidatedNodesCount::get(), + Error::::NotEnoughValidatedNodes + ); + + // All Nodes validated by this moment + 1 Cluster Manager + let seats = cluster_nodes_stats.validation_succeeded as u32 + 1; + let threshold = T::SeatsConsensus::get_threshold(seats); + let votes = { + let start = frame_system::Pallet::::block_number(); + let end = start + T::ClusterProposalDuration::get(); + Votes { seats, threshold, ayes: vec![], nays: vec![], start, end } + }; + let call: ::ClusterProposalCall = + T::ClusterProposalCall::from(Call::::activate_cluster_protocol { + cluster_id, + cluster_protocol_params, + }); + let proposal = Proposal { + call, + author: caller_id.clone(), + kind: ProposalKind::ActivateClusterProtocol, + }; + + >::insert(cluster_id, proposal); + >::insert(cluster_id, votes); + Self::deposit_event(Event::Proposed { account: caller_id, cluster_id, threshold }); + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::propose_update_cluster_protocol())] + pub fn propose_update_cluster_protocol( + origin: OriginFor, + cluster_id: ClusterId, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + member: ClusterMember, + ) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + Self::ensure_validated_member(caller_id.clone(), cluster_id, member)?; + + ensure!(!>::contains_key(cluster_id), Error::::ActiveProposal); + + let cluster_status = + >::get_cluster_status(&cluster_id) + .map_err(|_| Error::::NoCluster)?; + ensure!(cluster_status == ClusterStatus::Activated, Error::::UnexpectedState); + + let cluster_nodes_stats = T::ClusterManager::get_nodes_stats(&cluster_id) + .map_err(|_| Error::::NoCluster)?; + ensure!(cluster_nodes_stats.await_validation == 0, Error::::AwaitsValidation); + ensure!( + cluster_nodes_stats.validation_succeeded >= T::MinValidatedNodesCount::get(), + Error::::NotEnoughValidatedNodes + ); + + // All Nodes validated by this moment + 1 Cluster Manager + let seats = cluster_nodes_stats.validation_succeeded as u32 + 1; + let threshold = T::SeatsConsensus::get_threshold(seats); + let votes = { + let start = frame_system::Pallet::::block_number(); + let end = start + T::ClusterProposalDuration::get(); + Votes { seats, threshold, ayes: vec![], nays: vec![], start, end } + }; + let call: ::ClusterProposalCall = + T::ClusterProposalCall::from(Call::::update_cluster_protocol { + cluster_id, + cluster_protocol_params, + }); + let proposal = Proposal { + call, + author: caller_id.clone(), + kind: ProposalKind::UpdateClusterProtocol, + }; + + >::insert(cluster_id, proposal); + >::insert(cluster_id, votes); + Self::deposit_event(Event::Proposed { account: caller_id, cluster_id, threshold }); + + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::vote_proposal())] + pub fn vote_proposal( + origin: OriginFor, + cluster_id: ClusterId, + approve: bool, + member: ClusterMember, + ) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + Self::ensure_allowed_voter(caller_id.clone(), cluster_id, member)?; + let _ = Self::do_vote(caller_id, cluster_id, approve)?; + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight( + { + let m = 64; + ::WeightInfo::close_early_approved(m) + .max(::WeightInfo::close_early_disapproved(m)) + .max(::WeightInfo::close_approved(m)) + .max(::WeightInfo::close_disapproved(m)) + } + )] + pub fn close_proposal( + origin: OriginFor, + cluster_id: ClusterId, + member: ClusterMember, + ) -> DispatchResultWithPostInfo { + let caller_id = ensure_signed(origin)?; + Self::ensure_validated_member(caller_id.clone(), cluster_id, member)?; + Self::do_close(cluster_id, caller_id) + } + + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::retract_proposal())] + pub fn retract_proposal(origin: OriginFor, cluster_id: ClusterId) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + let proposal = + ClusterProposal::::get(cluster_id).ok_or(Error::::ProposalMissing)?; + if proposal.author != caller_id { + Err(Error::::NotProposalAuthor.into()) + } else { + Self::do_remove_proposal(cluster_id); + Ok(()) + } + } + + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::refund_submission_deposit())] + pub fn refund_submission_deposit( + origin: OriginFor, + referenda_index: ReferendumIndex, + ) -> DispatchResult { + ensure_signed_or_root(origin)?; + let submission_deposit = SubmissionDeposits::::get(referenda_index) + .ok_or(Error::::NoSubmissionDeposit)?; + + let refund_call = + pallet_referenda::Call::::refund_submission_deposit { index: referenda_index }; + let result = refund_call + .dispatch_bypass_filter(frame_system::RawOrigin::Signed(Self::account_id()).into()); + + match result { + Ok(_) => (), + // Check the error type as the 'refund_submission_deposit' extrinsic might have been + // called in the original 'pallet_referenda' before the current extrinsic calleed, + // so the funds are already unlocked for the pallet's balance and need to be + // refunded to the original depositor. + Err(ref e) if e.error == pallet_referenda::Error::::NoDeposit.into() => (), + Err(e) => return Err(e.error), + } + + Self::do_refund_submission_deposit( + referenda_index, + submission_deposit.depositor, + submission_deposit.amount.saturated_into::>(), + )?; + + Ok(()) + } + + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::activate_cluster_protocol())] + pub fn activate_cluster_protocol( + origin: OriginFor, + cluster_id: ClusterId, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + T::OpenGovActivatorOrigin::ensure_origin(origin)?; + T::ClusterProtocol::activate_cluster_protocol(&cluster_id)?; + T::ClusterProtocol::update_cluster_protocol(&cluster_id, cluster_protocol_params) + } + + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::update_cluster_protocol())] + pub fn update_cluster_protocol( + origin: OriginFor, + cluster_id: ClusterId, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + T::OpenGovUpdaterOrigin::ensure_origin(origin)?; + T::ClusterProtocol::update_cluster_protocol(&cluster_id, cluster_protocol_params) + } + } + + impl Pallet { + pub fn account_id() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + fn ensure_cluster_manager( + origin: T::AccountId, + cluster_id: ClusterId, + ) -> Result<(), DispatchError> { + let cluster_manager = T::ClusterManager::get_manager_account_id(&cluster_id) + .map_err(|_| Error::::NoCluster)?; + ensure!(origin == cluster_manager, Error::::NotClusterManager); + Ok(()) + } + + fn ensure_validated_member( + origin: T::AccountId, + cluster_id: ClusterId, + member: ClusterMember, + ) -> Result<(), DispatchError> { + match member { + ClusterMember::ClusterManager => Self::ensure_cluster_manager(origin, cluster_id), + ClusterMember::NodeProvider(node_pub_key) => { + let is_validated_node = T::ClusterManager::contains_node( + &cluster_id, + &node_pub_key, + Some(ClusterNodeStatus::ValidationSucceeded), + ); + if !is_validated_node { + Err(Error::::NotValidatedNode.into()) + } else { + let node_provider = T::NodeVisitor::get_node_provider_id(&node_pub_key)?; + if origin == node_provider { + Ok(()) + } else { + Err(Error::::NotNodeProvider.into()) + } + } + }, + } + } + + fn ensure_allowed_voter( + origin: T::AccountId, + cluster_id: ClusterId, + member: ClusterMember, + ) -> Result<(), DispatchError> { + match member { + ClusterMember::ClusterManager => Self::ensure_cluster_manager(origin, cluster_id), + ClusterMember::NodeProvider(node_pub_key) => { + let node_state = T::ClusterManager::get_node_state(&cluster_id, &node_pub_key) + .map_err(|_| Error::::NoClusterNode)?; + if node_state.status != ClusterNodeStatus::ValidationSucceeded { + Err(Error::::NotValidatedNode.into()) + } else { + let node_provider = T::NodeVisitor::get_node_provider_id(&node_pub_key)?; + if origin == node_provider { + let voting = ClusterProposalVoting::::get(cluster_id) + .ok_or(Error::::ProposalMissing)?; + if node_state.added_at < voting.start { + Ok(()) + } else { + Err(Error::::VoteProhibited.into()) + } + } else { + Err(Error::::NotNodeProvider.into()) + } + } + }, + } + } + + fn do_vote( + voter_id: T::AccountId, + cluster_id: ClusterId, + approve: bool, + ) -> Result { + let mut voting = Self::voting(cluster_id).ok_or(Error::::ProposalMissing)?; + + let position_yes = voting.ayes.iter().position(|a| a == &voter_id); + let position_no = voting.nays.iter().position(|a| a == &voter_id); + + // Detects first vote of the member in the motion + let is_account_voting_first_time = position_yes.is_none() && position_no.is_none(); + + if approve { + if position_yes.is_none() { + voting.ayes.push(voter_id.clone()); + } else { + return Err(Error::::DuplicateVote.into()); + } + if let Some(pos) = position_no { + voting.nays.swap_remove(pos); + } + } else { + if position_no.is_none() { + voting.nays.push(voter_id.clone()); + } else { + return Err(Error::::DuplicateVote.into()); + } + if let Some(pos) = position_yes { + voting.ayes.swap_remove(pos); + } + } + + let yes_votes = voting.ayes.len() as MemberCount; + let no_votes = voting.nays.len() as MemberCount; + Self::deposit_event(Event::Voted { + account: voter_id, + cluster_id, + voted: approve, + yes: yes_votes, + no: no_votes, + }); + + ClusterProposalVoting::::insert(cluster_id, voting); + + Ok(is_account_voting_first_time) + } + + /// Close a vote that is either approved, disapproved or whose voting period has ended. + fn do_close(cluster_id: ClusterId, caller_id: T::AccountId) -> DispatchResultWithPostInfo { + let voting = Self::voting(cluster_id).ok_or(Error::::ProposalMissing)?; + + let mut no_votes = voting.nays.len() as MemberCount; + let mut yes_votes = voting.ayes.len() as MemberCount; + let seats = voting.seats as MemberCount; + let approved = yes_votes >= voting.threshold; + let disapproved = seats.saturating_sub(no_votes) < voting.threshold; + // Allow (dis-)approving the proposal as soon as there are enough votes. + if approved { + let (proposal, _) = Self::validate_and_get_public_proposal(&cluster_id)?; + Self::deposit_event(Event::Closed { cluster_id, yes: yes_votes, no: no_votes }); + let proposal_weight = Self::do_approve_proposal(cluster_id, proposal, caller_id)?; + + return Ok(( + Some( + ::WeightInfo::close_early_approved(seats) + .saturating_add(proposal_weight), + ), + Pays::Yes, + ) + .into()); + } else if disapproved { + Self::deposit_event(Event::Closed { cluster_id, yes: yes_votes, no: no_votes }); + Self::do_disapprove_proposal(cluster_id); + + return Ok(( + Some(::WeightInfo::close_early_disapproved(seats)), + Pays::Yes, + ) + .into()); + } + + // Only allow actual closing of the proposal after the voting period has ended. + ensure!(frame_system::Pallet::::block_number() >= voting.end, Error::::TooEarly); + + let cluster_manager = T::ClusterManager::get_manager_account_id(&cluster_id)?; + let cluster_manager_vote = voting.ayes.iter().any(|a| a == &cluster_manager); + + // default voting strategy. + let default = T::DefaultVote::default_vote( + Some(cluster_manager_vote), + yes_votes, + no_votes, + seats, + ); + + let abstentions = seats - (yes_votes + no_votes); + match default { + true => yes_votes += abstentions, + false => no_votes += abstentions, + } + let approved = yes_votes >= voting.threshold; + + if approved { + let (proposal, _) = Self::validate_and_get_public_proposal(&cluster_id)?; + Self::deposit_event(Event::Closed { cluster_id, yes: yes_votes, no: no_votes }); + let proposal_weight = Self::do_approve_proposal(cluster_id, proposal, caller_id)?; + + Ok(( + Some( + ::WeightInfo::close_approved(seats) + .saturating_add(proposal_weight), + ), + Pays::Yes, + ) + .into()) + } else { + Self::deposit_event(Event::Closed { cluster_id, yes: yes_votes, no: no_votes }); + Self::do_disapprove_proposal(cluster_id); + + Ok((Some(::WeightInfo::close_disapproved(seats)), Pays::Yes) + .into()) + } + } + + fn do_approve_proposal( + cluster_id: ClusterId, + proposal: ReferendaCall, + depositor: T::AccountId, + ) -> Result { + Self::deposit_event(Event::Approved { cluster_id }); + + let dispatch_weight = proposal.get_dispatch_info().weight; + let submission_deposit = Self::do_submission_deposit(depositor.clone())?; + + let post_info = proposal + .dispatch_bypass_filter(frame_system::RawOrigin::Signed(Self::account_id()).into()) + .map_err(|e| e.error)?; + Self::deposit_event(Event::ReferendumSubmitted { cluster_id }); + + let referenda_index = pallet_referenda::ReferendumCount::::get() - 1; + Self::do_retain_submission_deposit(referenda_index, depositor, submission_deposit); + let proposal_weight = post_info.actual_weight.unwrap_or(dispatch_weight); + + Self::do_remove_proposal(cluster_id); + Ok(proposal_weight) + } + + /// Removes a proposal from the pallet, and deposit the `Disapproved` event. + fn do_disapprove_proposal(cluster_id: ClusterId) { + Self::deposit_event(Event::Disapproved { cluster_id }); + Self::do_remove_proposal(cluster_id) + } + + /// Removes a proposal from the pallet, cleaning up votes and the vector of proposals. + fn do_remove_proposal(cluster_id: ClusterId) { + ClusterProposal::::remove(cluster_id); + ClusterProposalVoting::::remove(cluster_id); + Self::deposit_event(Event::Removed { cluster_id }); + } + + fn validate_and_get_public_proposal( + cluster_id: &ClusterId, + ) -> Result<(ReferendaCall, usize), DispatchError> { + let proposal = + ClusterProposal::::get(cluster_id).ok_or(Error::::ProposalMissing)?; + + let call: ::RuntimeCall = proposal.call.into(); + let bounded_call = + T::Preimages::bound(call).map_err(|_| Error::::ProposalMissing)?; + + let proposal_origin = match proposal.kind { + ProposalKind::ActivateClusterProtocol => T::OpenGovActivatorTrackOrigin::get(), + ProposalKind::UpdateClusterProtocol => T::OpenGovUpdaterTrackOrigin::get(), + }; + + let pallets_origin: ::PalletsOrigin = + proposal_origin.caller().clone(); + let referenda_call = pallet_referenda::Call::::submit { + proposal_origin: Box::new(pallets_origin), + proposal: bounded_call, + enactment_moment: DispatchTime::After(T::ReferendumEnactmentDuration::get()), + }; + + let referenda_call_len = referenda_call.encode().len(); + + Ok((referenda_call, referenda_call_len)) + } + + fn do_submission_deposit(depositor: T::AccountId) -> Result, DispatchError> { + let submission_deposit = + ::SubmissionDeposit::get().saturated_into::(); + let amount = submission_deposit.saturated_into::>(); + ::Currency::transfer( + &depositor, + &Self::account_id(), + amount, + ExistenceRequirement::KeepAlive, + )?; + Ok(amount) + } + + fn do_retain_submission_deposit( + referenda_index: ReferendumIndex, + depositor: T::AccountId, + amount: BalanceOf, + ) { + let deposit = SubmissionDeposit { + depositor: depositor.clone(), + amount: amount.saturated_into::(), + }; + SubmissionDeposits::::insert(referenda_index, deposit); + Self::deposit_event(Event::SubmissionDepositRetained { + depositor, + referenda_index, + amount, + }); + } + + fn do_refund_submission_deposit( + referenda_index: ReferendumIndex, + depositor: T::AccountId, + amount: BalanceOf, + ) -> Result<(), DispatchError> { + ::Currency::transfer( + &Self::account_id(), + &depositor, + amount, + ExistenceRequirement::AllowDeath, + )?; + SubmissionDeposits::::remove(referenda_index); + Self::deposit_event(Event::SubmissionDepositRefunded { + referenda_index, + depositor, + amount, + }); + Ok(()) + } + } +} + +/// Set 'Nay' as default vote. +pub struct NayAsDefaultVote; +impl DefaultVote for NayAsDefaultVote { + fn default_vote( + _prime_vote: Option, + _yes_votes: MemberCount, + _no_votes: MemberCount, + _len: MemberCount, + ) -> bool { + false + } +} + +/// Set the prime member's vote as the default vote. +pub struct PrimeDefaultVote; +impl DefaultVote for PrimeDefaultVote { + fn default_vote( + prime_vote: Option, + _yes_votes: MemberCount, + _no_votes: MemberCount, + _len: MemberCount, + ) -> bool { + prime_vote.unwrap_or(false) + } +} +pub struct Unanimous; +impl SeatsConsensus for Unanimous { + fn get_threshold(seats: MemberCount) -> MemberCount { + seats + } +} +pub struct Supermajority; +impl SeatsConsensus for Supermajority { + fn get_threshold(seats: MemberCount) -> MemberCount { + (seats * 2 + 2 - 1) / 3 + } +} diff --git a/pallets/ddc-clusters-gov/src/mock.rs b/pallets/ddc-clusters-gov/src/mock.rs new file mode 100644 index 000000000..f71f05619 --- /dev/null +++ b/pallets/ddc-clusters-gov/src/mock.rs @@ -0,0 +1,760 @@ +//! Test utilities + +#![allow(dead_code)] + +use std::cell::RefCell; + +use ddc_primitives::{ + traits::{ + pallet::{GetDdcOrigin, PalletsOriginOf}, + SeatsConsensus, + }, + ClusterId, ClusterNodeKind, ClusterParams, ClusterProtocolParams, NodeParams, NodePubKey, + StorageNodeParams, DOLLARS, +}; +use frame_support::{ + parameter_types, + traits::{ + fungible::HoldConsideration, ConstBool, ConstU32, ConstU64, EnsureOriginWithArg, + EqualPrivilegeOnly, Everything, LinearStoragePrice, Nothing, + }, + weights::constants::RocksDbWeight, + PalletId, +}; +use frame_system::{ + mocking::{MockBlock, MockUncheckedExtrinsic}, + EnsureRoot, +}; +use lazy_static::lazy_static; +use pallet_ddc_clusters::cluster::Cluster; +use pallet_ddc_nodes::StorageNode; +use pallet_referenda::Curve; +use parking_lot::{ReentrantMutex, ReentrantMutexGuard}; +use sp_core::H256; +use sp_io::TestExternalities; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, Perbill, +}; + +use crate::{self as pallet_ddc_clusters_gov, *}; + +/// The AccountId alias in this test module. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub(crate) type AccountIndex = u64; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u128; + +pub const MILLISECS_PER_BLOCK: u64 = 6000; +pub const SECS_PER_BLOCK: u64 = MILLISECS_PER_BLOCK / 1000; +pub const MINUTES: BlockNumber = 60 / (SECS_PER_BLOCK as BlockNumber); +pub const CERE: u128 = 10000000000; + +pub type Signature = MultiSignature; +type UncheckedExtrinsic = MockUncheckedExtrinsic; +type Block = MockBlock; + +frame_support::construct_runtime!( + pub struct Test + { + System: frame_system::{Pallet, Call, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event, HoldReason}, + Referenda: pallet_referenda::{Pallet, Call, Storage, Event}, + ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event, HoldReason}, + Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, + DdcNodes: pallet_ddc_nodes::{Pallet, Call, Storage, Event}, + DdcClusters: pallet_ddc_clusters::{Pallet, Call, Storage, Config, Event}, + DdcStaking: pallet_ddc_staking::{Pallet, Call, Storage, Event}, + Origins: pallet_mock_origins::{Origin}, + DdcClustersGov: pallet_ddc_clusters_gov::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + } + +); + +pub type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + +impl Convert> for Test { + fn convert(w: Weight) -> BalanceOf { + w.ref_time().into() + } +} + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type Block = Block; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<5>; + type WeightInfo = (); +} + +parameter_types! { + pub static ExistentialDeposit: Balance = 1; +} + +impl pallet_balances::Config for Test { + type MaxLocks = ConstU32<1024>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type RuntimeFreezeReason = (); + type MaxFreezes = (); + type MaxHolds = ConstU32<2>; + type RuntimeHoldReason = RuntimeHoldReason; +} + +parameter_types! { + pub const PreimageMaxSize: u32 = 4096 * 1024; + pub const PreimageBaseDeposit: Balance = 0; + pub const PreimageByteDeposit: Balance = 0; + pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + +impl pallet_preimage::Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; +} + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub const SubmissionDeposit: Balance = DOLLARS; + pub const UndecidingTimeout: BlockNumber = 5 * MINUTES; +} + +impl pallet_referenda::Config for Test { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = EnsureOfPermittedReferendaOrigin; + type CancelOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Slash = (); + type Votes = pallet_conviction_voting::VotesOf; + type Tally = pallet_conviction_voting::TallyOf; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<100>; + type UndecidingTimeout = UndecidingTimeout; + type AlarmInterval = AlarmInterval; + type Tracks = TracksInfo; + type Preimages = Preimage; +} + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 3 * MINUTES; +} + +impl pallet_conviction_voting::Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type VoteLockingPeriod = VoteLockingPeriod; + type MaxVotes = ConstU32<512>; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + type Polls = Referenda; +} + +impl pallet_scheduler::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = (); + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<512>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +parameter_types! { + pub const DepositPerItem: Balance = 0; + pub const DepositPerByte: Balance = 0; + pub const SignedClaimHandicap: BlockNumber = 2; + pub const TombstoneDeposit: Balance = 16; + pub const StorageSizeOffset: u32 = 8; + pub const RentByteFee: Balance = 4; + pub const RentDepositOffset: Balance = 10_000; + pub const SurchargeReward: Balance = 150; + pub const MaxDepth: u32 = 100; + pub const MaxValueSize: u32 = 16_384; + pub Schedule: pallet_contracts::Schedule = Default::default(); + pub static DefaultDepositLimit: Balance = 10_000_000; + pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + pub const MaxDelegateDependencies: u32 = 32; +} + +impl pallet_contracts::Config for Test { + type Time = Timestamp; + type Randomness = Randomness; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type CallStack = [pallet_contracts::Frame; 5]; + type WeightPrice = Self; //pallet_transaction_payment::Module; + type WeightInfo = (); + type ChainExtension = (); + type Schedule = Schedule; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type DefaultDepositLimit = DefaultDepositLimit; + type AddressGenerator = pallet_contracts::DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxStorageKeyLen = ConstU32<128>; + type UnsafeUnstableInterface = ConstBool; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type MaxDelegateDependencies = MaxDelegateDependencies; + type RuntimeHoldReason = RuntimeHoldReason; + type Debug = (); + type Environment = (); + type Migrations = (); + type Xcm = (); +} + +impl pallet_insecure_randomness_collective_flip::Config for Test {} + +impl pallet_ddc_nodes::Config for Test { + type RuntimeEvent = RuntimeEvent; + type StakingVisitor = pallet_ddc_staking::Pallet; + type WeightInfo = (); +} + +impl pallet_ddc_clusters::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NodeRepository = pallet_ddc_nodes::Pallet; + type StakingVisitor = pallet_ddc_staking::Pallet; + type StakerCreator = pallet_ddc_staking::Pallet; + type Currency = Balances; + type WeightInfo = (); + type MinErasureCodingRequiredLimit = ConstU32<0>; + type MinErasureCodingTotalLimit = ConstU32<0>; + type MinReplicationTotalLimit = ConstU32<0>; +} + +parameter_types! { + pub const ClusterBondingAmount: Balance = DOLLARS; + pub const ClusterUnboningDelay: BlockNumber = MINUTES; +} + +impl pallet_ddc_staking::Config for Test { + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ClusterProtocol = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type ClusterManager = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type NodeCreator = pallet_ddc_nodes::Pallet; + type ClusterBondingAmount = ClusterBondingAmount; + type ClusterUnboningDelay = ClusterUnboningDelay; +} + +impl pallet_mock_origins::Config for Test {} + +parameter_types! { + pub const ClustersGovPalletId: PalletId = PalletId(*b"clustgov"); + pub const ClusterProposalDuration: BlockNumber = MINUTES; + pub const MinValidatedNodesCount: u16 = 3; + pub ClusterProtocolActivatorTrackOrigin: RuntimeOrigin = pallet_mock_origins::Origin::ClusterProtocolActivator.into(); + pub ClusterProtocolUpdaterTrackOrigin: RuntimeOrigin = pallet_mock_origins::Origin::ClusterProtocolUpdater.into(); + pub const ReferendumEnactmentDuration: BlockNumber = 1; +} + +impl crate::pallet::Config for Test { + type RuntimeEvent = RuntimeEvent; + type PalletId = ClustersGovPalletId; + type Currency = Balances; + type WeightInfo = (); + type OpenGovActivatorTrackOrigin = DdcOriginAsNative; + type OpenGovActivatorOrigin = pallet_mock_origins::ClusterProtocolActivator; + type OpenGovUpdaterTrackOrigin = DdcOriginAsNative; + type OpenGovUpdaterOrigin = pallet_mock_origins::ClusterProtocolUpdater; + type ClusterProposalCall = RuntimeCall; + type ClusterProposalDuration = ClusterProposalDuration; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type SeatsConsensus = MockedSeatsConsensus; + type DefaultVote = MockedDefaultVote; // pallet_ddc_clusters_gov::PrimeDefaultVote; + type MinValidatedNodesCount = MinValidatedNodesCount; + type ReferendumEnactmentDuration = ReferendumEnactmentDuration; + #[cfg(feature = "runtime-benchmarks")] + type NodeCreator = pallet_ddc_nodes::Pallet; + #[cfg(feature = "runtime-benchmarks")] + type StakerCreator = pallet_ddc_staking::Pallet; +} + +pub enum DefaultVoteVariant { + NayAsDefaultVote, + PrimeDefaultVote, +} + +lazy_static! { + // We have to use the ReentrantMutex as every test's thread that needs to perform some configuration on the mock acquires the lock at least 2 times: + // the first time when the mock configuration happens, and + // the second time when the pallet calls the MockedDefaultVote during execution + static ref MOCK_DEFAULT_VOTE: ReentrantMutex> = + ReentrantMutex::new(RefCell::new(MockDefaultVote::default())); +} +pub struct MockDefaultVote { + pub strategy: DefaultVoteVariant, +} + +impl Default for MockDefaultVote { + fn default() -> Self { + Self { strategy: DefaultVoteVariant::PrimeDefaultVote } + } +} + +pub struct MockedDefaultVote; +impl MockedDefaultVote { + // Every test's thread must hold the lock till the end of its test + pub fn set_and_hold_lock( + mock: MockDefaultVote, + ) -> ReentrantMutexGuard<'static, RefCell> { + let lock = MOCK_DEFAULT_VOTE.lock(); + *lock.borrow_mut() = mock; + lock + } + + // Every test's thread must release the lock that it previously acquired in the end of its + // test + pub fn reset_and_release_lock(lock: ReentrantMutexGuard<'static, RefCell>) { + *lock.borrow_mut() = MockDefaultVote::default(); + } +} + +impl DefaultVote for MockedDefaultVote { + fn default_vote( + prime_vote: Option, + yes_votes: MemberCount, + no_votes: MemberCount, + len: MemberCount, + ) -> bool { + let lock = MOCK_DEFAULT_VOTE.lock(); + let mock_ref = lock.borrow(); + match mock_ref.strategy { + DefaultVoteVariant::NayAsDefaultVote => + NayAsDefaultVote::default_vote(prime_vote, yes_votes, no_votes, len), + DefaultVoteVariant::PrimeDefaultVote => + PrimeDefaultVote::default_vote(prime_vote, yes_votes, no_votes, len), + } + } +} + +pub enum ConsensusVariant { + Supermajority, + Unanimous, +} + +lazy_static! { + // We have to use the ReentrantMutex as every test's thread that needs to perform some configuration on the mock acquires the lock at least 2 times: + // the first time when the mock configuration happens, and + // the second time when the pallet calls the MockedSeatsConsensus during execution + static ref MOCK_SEATS_CONSENSUS: ReentrantMutex> = + ReentrantMutex::new(RefCell::new(MockSeatsConsensus::default())); +} +pub struct MockSeatsConsensus { + pub consensus: ConsensusVariant, +} + +impl Default for MockSeatsConsensus { + fn default() -> Self { + Self { consensus: ConsensusVariant::Supermajority } + } +} + +pub struct MockedSeatsConsensus; +impl MockedSeatsConsensus { + // Every test's thread must hold the lock till the end of its test + pub fn set_and_hold_lock( + mock: MockSeatsConsensus, + ) -> ReentrantMutexGuard<'static, RefCell> { + let lock = MOCK_SEATS_CONSENSUS.lock(); + *lock.borrow_mut() = mock; + lock + } + + // Every test's thread must release the lock that it previously acquired in the end of its + // test + pub fn reset_and_release_lock(lock: ReentrantMutexGuard<'static, RefCell>) { + *lock.borrow_mut() = MockSeatsConsensus::default(); + } +} + +impl SeatsConsensus for MockedSeatsConsensus { + fn get_threshold(seats: MemberCount) -> MemberCount { + let lock = MOCK_SEATS_CONSENSUS.lock(); + let mock_ref = lock.borrow(); + match mock_ref.consensus { + ConsensusVariant::Supermajority => Supermajority::get_threshold(seats), + ConsensusVariant::Unanimous => Unanimous::get_threshold(seats), + } + } +} + +pub struct DdcOriginAsNative(PhantomData<(DdcOrigin, RuntimeOrigin)>); +impl, T: frame_system::Config> GetDdcOrigin + for DdcOriginAsNative +{ + fn get() -> T::RuntimeOrigin { + DdcOrigin::get() + } +} + +pub struct EnsureOfPermittedReferendaOrigin(PhantomData); +impl EnsureOriginWithArg> + for EnsureOfPermittedReferendaOrigin +where + ::RuntimeOrigin: OriginTrait, +{ + type Success = T::AccountId; + + fn try_origin( + o: T::RuntimeOrigin, + proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = as EnsureOrigin<_>>::try_origin(o.clone())?; + + let track_id = + match >::track_for( + proposal_origin, + ) { + Ok(track_id) => track_id, + Err(_) => return Err(o), + }; + + if track_id == CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID || + track_id == CLUSTER_PROTOCOL_UPDATER_TRACK_ID + { + let clusters_governance = ClustersGovPalletId::get().into_account_truncating(); + if origin == clusters_governance { + Ok(origin) + } else { + Err(o) + } + } else { + Ok(origin) + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin( + _proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = frame_benchmarking::account::("successful_origin", 0, 0); + Ok(frame_system::RawOrigin::Signed(origin).into()) + } +} + +const fn percent(x: i32) -> sp_arithmetic::FixedI64 { + sp_arithmetic::FixedI64::from_rational(x as u128, 100) +} + +const APP_CLUSTER_PROTOCOL_ACTIVATOR: Curve = Curve::make_linear(10, 28, percent(0), percent(10)); +const SUP_CLUSTER_PROTOCOL_ACTIVATOR: Curve = + Curve::make_reciprocal(1, 28, percent(4), percent(0), percent(10)); +const APP_CLUSTER_PROTOCOL_UPDATER: Curve = Curve::make_linear(10, 28, percent(0), percent(10)); +const SUP_CLUSTER_PROTOCOL_UPDATER: Curve = + Curve::make_reciprocal(1, 28, percent(4), percent(0), percent(10)); + +pub const CLUSTER_PROTOCOL_ACTIVATOR_DECISION_DEPOSIT: Balance = 30 * DOLLARS; +pub const CLUSTER_PROTOCOL_UPDATER_DECISION_DEPOSIT: Balance = 20 * DOLLARS; + +pub const CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID: u16 = 100; +pub const CLUSTER_PROTOCOL_UPDATER_TRACK_ID: u16 = 101; + +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 2] = [ + ( + CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_activator", + max_deciding: 50, + decision_deposit: CLUSTER_PROTOCOL_ACTIVATOR_DECISION_DEPOSIT, + prepare_period: 0, + decision_period: MINUTES / 2, + confirm_period: MINUTES / 4, + min_enactment_period: 0, + min_approval: APP_CLUSTER_PROTOCOL_ACTIVATOR, + min_support: SUP_CLUSTER_PROTOCOL_ACTIVATOR, + }, + ), + ( + CLUSTER_PROTOCOL_UPDATER_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_updater", + max_deciding: 50, + decision_deposit: CLUSTER_PROTOCOL_UPDATER_DECISION_DEPOSIT, + prepare_period: 0, + decision_period: MINUTES / 2, + confirm_period: MINUTES / 4, + min_enactment_period: 0, + min_approval: APP_CLUSTER_PROTOCOL_UPDATER, + min_support: SUP_CLUSTER_PROTOCOL_UPDATER, + }, + ), +]; + +pub struct TracksInfo; +impl pallet_referenda::TracksInfo for TracksInfo { + type Id = u16; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo)] { + &TRACKS_DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(custom_origin) = pallet_mock_origins::Origin::try_from(id.clone()) { + match custom_origin { + pallet_mock_origins::Origin::ClusterProtocolActivator => + Ok(CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID), + pallet_mock_origins::Origin::ClusterProtocolUpdater => + Ok(CLUSTER_PROTOCOL_UPDATER_TRACK_ID), + } + } else { + Err(()) + } + } +} +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +#[allow(unused_imports)] +#[frame_support::pallet] +mod pallet_mock_origins { + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug)] + #[pallet::origin] + pub enum Origin { + ClusterProtocolActivator, + ClusterProtocolUpdater, + } + + macro_rules! decl_unit_ensures { + ( $name:ident: $success_type:ty = $success:expr ) => { + pub struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + Origin::$name => Ok($success), + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(O::from(Origin::$name)) + } + } + }; + ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; + ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { + decl_unit_ensures! { $name: $success_type = $success } + decl_unit_ensures! { $( $rest )* } + }; + ( $name:ident, $( $rest:tt )* ) => { + decl_unit_ensures! { $name } + decl_unit_ensures! { $( $rest )* } + }; + () => {} + } + decl_unit_ensures!(ClusterProtocolActivator, ClusterProtocolUpdater,); + + #[allow(unused_macros)] + macro_rules! decl_ensure { + ( + $vis:vis type $name:ident: EnsureOrigin { + $( $item:ident = $success:expr, )* + } + ) => { + $vis struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + $( + Origin::$item => Ok($success), + )* + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + // By convention the more privileged origins go later, so for greatest chance + // of success, we want the last one. + let _result: Result = Err(()); + $( + let _result: Result = Ok(O::from(Origin::$item)); + )* + _result + } + } + } + } +} + +pub const CLUSTER_ID: [u8; 20] = [1; 20]; +pub const CLUSTER_MANAGER_ID: [u8; 32] = [1; 32]; +pub const CLUSTER_RESERVE_ID: [u8; 32] = [2; 32]; + +pub const NODE_PROVIDER_ID_1: [u8; 32] = [11; 32]; +pub const NODE_PROVIDER_ID_2: [u8; 32] = [12; 32]; +pub const NODE_PROVIDER_ID_3: [u8; 32] = [13; 32]; + +pub const NODE_PUB_KEY_1: [u8; 32] = [111; 32]; +pub const NODE_PUB_KEY_2: [u8; 32] = [112; 32]; +pub const NODE_PUB_KEY_3: [u8; 32] = [113; 32]; + +pub const ENDOWMENT: u128 = 1000 * CERE; + +#[allow(clippy::type_complexity)] +pub type BuiltCluster = (Cluster, ClusterProtocolParams); +#[allow(clippy::type_complexity)] +pub type BuiltNode = (NodePubKey, StorageNode, ClusterNodeStatus, ClusterNodeKind); + +pub fn build_cluster( + cluster_id: [u8; 20], + manager_id: [u8; 32], + reserve_id: [u8; 32], + params: ClusterParams, + protocol_params: ClusterProtocolParams, + status: ClusterStatus, +) -> BuiltCluster { + let mut cluster = Cluster::new( + ClusterId::from(cluster_id), + AccountId::from(manager_id), + AccountId::from(reserve_id), + params, + ); + cluster.status = status; + (cluster, protocol_params) +} + +pub fn build_cluster_node( + pub_key: [u8; 32], + provider_id: [u8; 32], + params: StorageNodeParams, + cluster_id: [u8; 20], + status: ClusterNodeStatus, + kind: ClusterNodeKind, +) -> BuiltNode { + let key = NodePubKey::StoragePubKey(AccountId::from(pub_key)); + let mut node = StorageNode::new( + key.clone(), + AccountId::from(provider_id), + NodeParams::StorageParams(params), + ) + .unwrap(); + node.cluster_id = Some(ClusterId::from(cluster_id)); + (key, node, status, kind) +} + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build(self, cluster: BuiltCluster, cluster_nodes: Vec) -> TestExternalities { + sp_tracing::try_init_simple(); + let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + let mut balances: Vec<(AccountId, Balance)> = Vec::new(); + let mut storage_nodes: Vec> = Vec::new(); + let mut cluster_storage_nodes: Vec<(NodePubKey, ClusterNodeKind, ClusterNodeStatus)> = + Vec::new(); + + for (pub_key, node, status, kind) in cluster_nodes.iter() { + balances.push((node.provider_id.clone(), ENDOWMENT)); + cluster_storage_nodes.push((pub_key.clone(), kind.clone(), status.clone())); + storage_nodes.push(node.clone()); + } + + let (clust, cluster_protocol_params) = cluster; + balances.push((clust.manager_id.clone(), ENDOWMENT)); + balances.push((clust.reserve_id.clone(), ENDOWMENT)); + + // endow system account to allow dispatching transaction + balances.push((DdcClustersGov::account_id(), ENDOWMENT)); + + let _ = + pallet_balances::GenesisConfig:: { balances }.assimilate_storage(&mut storage); + + let _ = pallet_ddc_nodes::GenesisConfig:: { storage_nodes } + .assimilate_storage(&mut storage); + + let _ = pallet_ddc_clusters::GenesisConfig:: { + clusters: vec![clust.clone()], + clusters_protocol_params: vec![(clust.cluster_id, cluster_protocol_params)], + clusters_nodes: vec![(clust.cluster_id, cluster_storage_nodes)], + } + .assimilate_storage(&mut storage); + + TestExternalities::new(storage) + } + + pub fn build_and_execute( + self, + cluster: BuiltCluster, + cluster_nodes: Vec, + test: impl FnOnce(), + ) { + sp_tracing::try_init_simple(); + let mut ext = self.build(cluster, cluster_nodes); + ext.execute_with(test); + } +} diff --git a/pallets/ddc-clusters-gov/src/tests.rs b/pallets/ddc-clusters-gov/src/tests.rs new file mode 100644 index 000000000..313868370 --- /dev/null +++ b/pallets/ddc-clusters-gov/src/tests.rs @@ -0,0 +1,4077 @@ +//! Tests for the module. + +use ddc_primitives::{ClusterNodeKind, ClusterParams, ClusterProtocolParams, StorageNodeParams}; +use frame_support::{assert_noop, assert_ok}; +use pallet_conviction_voting::{AccountVote, Conviction, Vote}; +use pallet_ddc_clusters::Event::{ClusterActivated, ClusterProtocolParamsSet}; +use pallet_referenda::ReferendumInfo; +use sp_runtime::Perquintill; + +use super::{mock::*, *}; +use crate::SubmissionDeposit as ReferendaSubmissionDeposit; + +fn next_block() { + System::set_block_number(System::block_number() + 1); + Scheduler::on_initialize(System::block_number()); + Referenda::on_initialize(System::block_number()); +} + +fn fast_forward_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} + +#[test] +fn cluster_protocol_activation_proposal_initiated() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let not_cluster_manager = AccountId::from([0; 32]); + let cluster_protocol_params = ClusterProtocolParams::default(); + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + Error::::NotClusterManager + ); + + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let not_cluster_id = ClusterId::from([0; 20]); + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + not_cluster_id, + cluster_protocol_params.clone() + ), + Error::::NoCluster + ); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + let proposal = ClusterProposal::::get(cluster_id); + assert_eq!( + proposal, + Some(Proposal { + author: cluster_manager.clone(), + kind: ProposalKind::ActivateClusterProtocol, + call: ::ClusterProposalCall::from( + Call::::activate_cluster_protocol { + cluster_id, + cluster_protocol_params: cluster_protocol_params.clone(), + } + ) + }) + ); + + let votes = ClusterProposalVoting::::get(cluster_id); + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + assert_eq!(votes, Some(Votes { seats, threshold, ayes: vec![], nays: vec![], start, end })); + System::assert_last_event( + Event::Proposed { account: cluster_manager, cluster_id, threshold }.into(), + ) + }) +} + +#[test] +fn cluster_protocol_activation_proposal_fails_on_unexpected_state() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Unbonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + Error::::UnexpectedState + ); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_fails_if_there_are_not_enough_validated_nodes() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + Error::::NotEnoughValidatedNodes + ); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_fails_if_there_is_active_proposal() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + Error::::ActiveProposal + ); + }) +} + +#[test] +fn cluster_protocol_activation_is_restricted_for_system_origins() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + ExtBuilder.build_and_execute(cluster, vec![], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + DispatchError::BadOrigin + ); + + assert_noop!( + DdcClustersGov::activate_cluster_protocol( + RuntimeOrigin::root(), + cluster_id, + cluster_protocol_params.clone() + ), + DispatchError::BadOrigin + ); + }) +} + +#[test] +fn cluster_protocol_activation_is_allowed_for_referenda_cluster_protocol_activator_track_origin() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + ExtBuilder.build_and_execute(cluster, vec![], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_protocol_params = ClusterProtocolParams { + treasury_share: Perquintill::from_float(5.0), + validators_share: Perquintill::from_float(10.0), + cluster_reserve_share: Perquintill::from_float(15.0), + storage_bond_size: 10 * CERE, + storage_chill_delay: 20, + storage_unbonding_delay: 20, + unit_per_mb_stored: 97656, + unit_per_mb_streamed: 48828, + unit_per_put_request: 10, + unit_per_get_request: 5, + }; + let open_gov_activator = ::OpenGovActivatorTrackOrigin::get(); + assert_ok!(DdcClustersGov::activate_cluster_protocol( + open_gov_activator, + cluster_id, + cluster_protocol_params.clone() + )); + + let cluster = pallet_ddc_clusters::Clusters::::get(cluster_id).unwrap(); + assert_eq!(cluster.status, ClusterStatus::Activated); + + let updated_cluster_protocol_params = + pallet_ddc_clusters::ClustersGovParams::::get(cluster_id).unwrap(); + assert_eq!(cluster_protocol_params, updated_cluster_protocol_params); + assert_eq!(System::events().len(), 2); + System::assert_has_event(ClusterActivated { cluster_id }.into()); + System::assert_last_event(ClusterProtocolParamsSet { cluster_id }.into()); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_can_be_retracted_by_its_author() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + let not_cluster_id = ClusterId::from([0; 20]); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert!(ClusterProposal::::contains_key(cluster_id)); + assert!(ClusterProposalVoting::::contains_key(cluster_id)); + + assert_noop!( + DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + not_cluster_id, + ), + Error::::ProposalMissing + ); + + assert_noop!( + DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ), + Error::::NotProposalAuthor + ); + + assert_ok!(DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + )); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + System::assert_last_event(Event::Removed { cluster_id }.into()); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_cannot_be_initated_for_active_cluster() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + assert_noop!( + DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + ), + Error::::UnexpectedState + ); + }) +} + +#[test] +fn cluster_protocol_update_proposal_initiated() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + let cluster_protocol_params = ClusterProtocolParams { + treasury_share: Perquintill::from_float(5.0), + validators_share: Perquintill::from_float(10.0), + cluster_reserve_share: Perquintill::from_float(15.0), + storage_bond_size: 10 * CERE, + storage_chill_delay: 20, + storage_unbonding_delay: 20, + unit_per_mb_stored: 97656, + unit_per_mb_streamed: 48828, + unit_per_put_request: 10, + unit_per_get_request: 5, + }; + + let not_cluster_member = AccountId::from([0; 32]); + let not_cluster_node_key = + NodePubKey::StoragePubKey(AccountId::from(AccountId::from([128; 32]))); + assert_noop!( + DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(not_cluster_member.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(not_cluster_node_key.clone()) + ), + Error::::NotValidatedNode + ); + + let not_cluster_id = ClusterId::from([0; 20]); + assert_noop!( + DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + not_cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + ), + Error::::NotValidatedNode + ); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key) + )); + + let proposal = ClusterProposal::::get(cluster_id); + assert_eq!( + proposal, + Some(Proposal { + author: cluster_node_1_provider.clone(), + kind: ProposalKind::UpdateClusterProtocol, + call: ::ClusterProposalCall::from( + Call::::update_cluster_protocol { + cluster_id, + cluster_protocol_params: cluster_protocol_params.clone(), + } + ) + }) + ); + + let votes = ClusterProposalVoting::::get(cluster_id); + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + assert_eq!(votes, Some(Votes { seats, threshold, ayes: vec![], nays: vec![], start, end })); + System::assert_last_event( + Event::Proposed { account: cluster_node_1_provider, cluster_id, threshold }.into(), + ) + }) +} + +#[test] +fn cluster_protocol_update_proposal_fails_on_unexpected_state() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Unbonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + ), + Error::::UnexpectedState + ); + }) +} + +#[test] +fn cluster_protocol_update_proposal_fails_if_there_are_not_enough_validated_nodes() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + ), + Error::::NotEnoughValidatedNodes + ); + }) +} + +#[test] +fn cluster_protocol_update_proposal_fails_if_there_is_active_proposal() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + )); + + assert_noop!( + DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + ), + Error::::ActiveProposal + ); + }) +} + +#[test] +fn cluster_protocol_update_is_restricted_for_system_origins() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_noop!( + DdcClustersGov::update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ), + DispatchError::BadOrigin + ); + + assert_noop!( + DdcClustersGov::update_cluster_protocol( + RuntimeOrigin::root(), + cluster_id, + cluster_protocol_params.clone(), + ), + DispatchError::BadOrigin + ); + }) +} + +#[test] +fn cluster_protocol_update_is_allowed_for_referenda_cluster_protocol_updater_track_origin() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + ExtBuilder.build_and_execute(cluster, vec![], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_protocol_params = ClusterProtocolParams { + treasury_share: Perquintill::from_float(5.0), + validators_share: Perquintill::from_float(10.0), + cluster_reserve_share: Perquintill::from_float(15.0), + storage_bond_size: 10 * CERE, + storage_chill_delay: 20, + storage_unbonding_delay: 20, + unit_per_mb_stored: 97656, + unit_per_mb_streamed: 48828, + unit_per_put_request: 10, + unit_per_get_request: 5, + }; + let open_gov_updater = ::OpenGovUpdaterTrackOrigin::get(); + assert_ok!(DdcClustersGov::update_cluster_protocol( + open_gov_updater, + cluster_id, + cluster_protocol_params.clone() + )); + + let updated_cluster_protocol_params = + pallet_ddc_clusters::ClustersGovParams::::get(cluster_id).unwrap(); + assert_eq!(cluster_protocol_params, updated_cluster_protocol_params); + assert_eq!(System::events().len(), 1); + System::assert_last_event(ClusterProtocolParamsSet { cluster_id }.into()); + }) +} + +#[test] +fn cluster_protocol_update_proposal_can_be_retracted_by_its_author() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + let not_cluster_id = ClusterId::from([0; 20]); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key) + )); + + assert!(ClusterProposal::::contains_key(cluster_id)); + assert!(ClusterProposalVoting::::contains_key(cluster_id)); + + assert_noop!( + DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + not_cluster_id, + ), + Error::::ProposalMissing + ); + + assert_noop!( + DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ), + Error::::NotProposalAuthor + ); + + assert_ok!(DdcClustersGov::retract_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + )); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + System::assert_last_event(Event::Removed { cluster_id }.into()); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_early_approved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + System::assert_last_event( + Event::Proposed { account: cluster_manager.clone(), cluster_id, threshold }.into(), + ); + + let not_cluster_manager = AccountId::from([0; 32]); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_manager.clone(), + cluster_id, + voted: true, + yes: 1, + no: 0, + } + .into(), + ); + + let not_node_provider = AccountId::from([128; 32]); + let not_cluster_node_key = NodePubKey::StoragePubKey(AccountId::from([128; 32])); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NoClusterNode + ); + + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone(), cluster_node_1_provider.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_node_1_provider.clone(), + cluster_id, + voted: true, + yes: 2, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_2_provider.clone(), + cluster_id, + voted: true, + yes: 3, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone(), + cluster_node_3_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_3_provider.clone(), + cluster_id, + voted: true, + yes: 4, + no: 0, + } + .into(), + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NotValidatedNode + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + // OpenGov + + let balance_before_decision_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(Referenda::place_decision_deposit( + RuntimeOrigin::signed(cluster_manager.clone()), + referenda_index, + )); + let balance_after_decision_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_decision_deposit + .saturating_sub(CLUSTER_PROTOCOL_ACTIVATOR_DECISION_DEPOSIT), + balance_after_decision_deposit + ); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Ongoing(..))); + + assert_ok!(ConvictionVoting::vote( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + referenda_index, + AccountVote::Standard { + vote: Vote { aye: true, conviction: Conviction::Locked6x }, + balance: Balances::free_balance(cluster_node_1_provider.clone()) + } + )); + + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + fast_forward_to(3); + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Approved(..))); + + let balance_before_submission_deposit_refund = + Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::refund_submission_deposit( + RuntimeOrigin::signed(cluster_manager.clone()), + referenda_index, + )); + let balance_after_submission_deposit_refund = + Balances::free_balance(cluster_manager.clone()); + + assert_eq!( + balance_before_submission_deposit_refund.saturating_add(submission_deposit_amount), + balance_after_submission_deposit_refund + ); + System::assert_last_event( + Event::SubmissionDepositRefunded { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_approved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_early_disapproved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 1, no: 3 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_disapproved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + false, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 1, no: 3 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_cannot_be_closed_if_threshold_is_not_reached_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::TooEarly + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_early_approved_with_unanimous_consensus_and_nay_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + System::assert_last_event( + Event::Proposed { account: cluster_manager.clone(), cluster_id, threshold }.into(), + ); + + let not_cluster_manager = AccountId::from([0; 32]); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_manager.clone(), + cluster_id, + voted: true, + yes: 1, + no: 0, + } + .into(), + ); + + let not_node_provider = AccountId::from([128; 32]); + let not_cluster_node_key = NodePubKey::StoragePubKey(AccountId::from([128; 32])); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NoClusterNode + ); + + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone(), cluster_node_1_provider.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_node_1_provider.clone(), + cluster_id, + voted: true, + yes: 2, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_2_provider.clone(), + cluster_id, + voted: true, + yes: 3, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone(), + cluster_node_3_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_3_provider.clone(), + cluster_id, + voted: true, + yes: 4, + no: 0, + } + .into(), + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NotValidatedNode + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + // OpenGov + + let balance_before_decision_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(Referenda::place_decision_deposit( + RuntimeOrigin::signed(cluster_manager.clone()), + referenda_index, + )); + let balance_after_decision_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_decision_deposit + .saturating_sub(CLUSTER_PROTOCOL_ACTIVATOR_DECISION_DEPOSIT), + balance_after_decision_deposit + ); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Ongoing(..))); + + assert_ok!(ConvictionVoting::vote( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + referenda_index, + AccountVote::Standard { + vote: Vote { aye: true, conviction: Conviction::Locked6x }, + balance: Balances::free_balance(cluster_node_1_provider.clone()) + } + )); + + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + fast_forward_to(3); + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Approved(..))); + + let balance_before_submission_deposit_refund = + Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::refund_submission_deposit( + RuntimeOrigin::signed(cluster_manager.clone()), + referenda_index, + )); + let balance_after_submission_deposit_refund = + Balances::free_balance(cluster_manager.clone()); + + assert_eq!( + balance_before_submission_deposit_refund.saturating_add(submission_deposit_amount), + balance_after_submission_deposit_refund + ); + System::assert_last_event( + Event::SubmissionDepositRefunded { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_approved_with_unanimous_consensus_and_nay_default_vote() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_early_disapproved_with_unanimous_consensus_and_nay_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_disapproved_with_unanimous_consensus_and_nay_default_vote() +{ + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_activation_proposal_cannot_be_closed_if_threshold_is_not_reached_with_unanimous_consensus_and_nay_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + assert_ok!(DdcClustersGov::propose_activate_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone() + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::TooEarly + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_early_approved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + System::assert_last_event( + Event::Proposed { account: cluster_node_3_provider.clone(), cluster_id, threshold } + .into(), + ); + + let not_cluster_manager = AccountId::from([0; 32]); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_manager.clone(), + cluster_id, + voted: true, + yes: 1, + no: 0, + } + .into(), + ); + + let not_node_provider = AccountId::from([128; 32]); + let not_cluster_node_key = NodePubKey::StoragePubKey(AccountId::from([128; 32])); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NoClusterNode + ); + + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone(), cluster_node_1_provider.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_node_1_provider.clone(), + cluster_id, + voted: true, + yes: 2, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_2_provider.clone(), + cluster_id, + voted: true, + yes: 3, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone(), + cluster_node_3_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_3_provider.clone(), + cluster_id, + voted: true, + yes: 4, + no: 0, + } + .into(), + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NotValidatedNode + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + // OpenGov + + let balance_before_decision_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_ok!(Referenda::place_decision_deposit( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + referenda_index, + )); + let balance_after_decision_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_eq!( + balance_before_decision_deposit + .saturating_sub(CLUSTER_PROTOCOL_UPDATER_DECISION_DEPOSIT), + balance_after_decision_deposit + ); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Ongoing(..))); + + assert_ok!(ConvictionVoting::vote( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + referenda_index, + AccountVote::Standard { + vote: Vote { aye: true, conviction: Conviction::Locked6x }, + balance: Balances::free_balance(cluster_node_1_provider.clone()) + } + )); + + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + fast_forward_to(3); + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Approved(..))); + + let balance_before_submission_deposit_refund = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_ok!(DdcClustersGov::refund_submission_deposit( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + referenda_index, + )); + let balance_after_submission_deposit_refund = + Balances::free_balance(cluster_node_3_provider.clone()); + + assert_eq!( + balance_before_submission_deposit_refund.saturating_add(submission_deposit_amount), + balance_after_submission_deposit_refund + ); + System::assert_last_event( + Event::SubmissionDepositRefunded { + referenda_index, + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_approved_with_supermajority_consensus_and_prime_default_vote() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_early_disapproved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 1, no: 3 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_disapproved_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + false, + ClusterMember::ClusterManager + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 1, no: 3 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_cannot_be_closed_if_threshold_is_not_reached_with_supermajority_consensus_and_prime_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::PrimeDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Supermajority, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key) + ), + Error::::TooEarly + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_early_approved_with_unanimous_consensus_and_nay_default_vote() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + let seats = 4; // 3 validated nodes + 1 cluster manager + let threshold = ::SeatsConsensus::get_threshold(seats); + System::assert_last_event( + Event::Proposed { account: cluster_node_3_provider.clone(), cluster_id, threshold } + .into(), + ); + + let not_cluster_manager = AccountId::from([0; 32]); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_manager.clone(), + cluster_id, + voted: true, + yes: 1, + no: 0, + } + .into(), + ); + + let not_node_provider = AccountId::from([128; 32]); + let not_cluster_node_key = NodePubKey::StoragePubKey(AccountId::from([128; 32])); + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NoClusterNode + ); + + assert_noop!( + DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!(votes.ayes, vec![cluster_manager.clone(), cluster_node_1_provider.clone()]); + System::assert_last_event( + Event::Voted { + account: cluster_node_1_provider.clone(), + cluster_id, + voted: true, + yes: 2, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_2_provider.clone(), + cluster_id, + voted: true, + yes: 3, + no: 0, + } + .into(), + ); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + let votes = ClusterProposalVoting::::get(cluster_id).unwrap(); + assert_eq!( + votes.ayes, + vec![ + cluster_manager.clone(), + cluster_node_1_provider.clone(), + cluster_node_2_provider.clone(), + cluster_node_3_provider.clone() + ] + ); + System::assert_last_event( + Event::Voted { + account: cluster_node_3_provider.clone(), + cluster_id, + voted: true, + yes: 4, + no: 0, + } + .into(), + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + ), + Error::::NotClusterManager + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(not_cluster_node_key.clone()), + ), + Error::::NotValidatedNode + ); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(not_node_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + ), + Error::::NotNodeProvider + ); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + // OpenGov + + let balance_before_decision_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_ok!(Referenda::place_decision_deposit( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + referenda_index, + )); + let balance_after_decision_deposit = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_eq!( + balance_before_decision_deposit + .saturating_sub(CLUSTER_PROTOCOL_UPDATER_DECISION_DEPOSIT), + balance_after_decision_deposit + ); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Ongoing(..))); + + assert_ok!(ConvictionVoting::vote( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + referenda_index, + AccountVote::Standard { + vote: Vote { aye: true, conviction: Conviction::Locked6x }, + balance: Balances::free_balance(cluster_node_1_provider.clone()) + } + )); + + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + fast_forward_to(3); + assert_ok!(Referenda::nudge_referendum(RuntimeOrigin::root(), referenda_index)); + + let referendum = pallet_referenda::ReferendumInfoFor::::get(referenda_index).unwrap(); + assert!(matches!(referendum, ReferendumInfo::Approved(..))); + + let balance_before_submission_deposit_refund = + Balances::free_balance(cluster_node_3_provider.clone()); + assert_ok!(DdcClustersGov::refund_submission_deposit( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + referenda_index, + )); + let balance_after_submission_deposit_refund = + Balances::free_balance(cluster_node_3_provider.clone()); + + assert_eq!( + balance_before_submission_deposit_refund.saturating_add(submission_deposit_amount), + balance_after_submission_deposit_refund + ); + System::assert_last_event( + Event::SubmissionDepositRefunded { + referenda_index, + depositor: cluster_node_3_provider.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_approved_with_unanimous_consensus_and_nay_default_vote() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + let balance_before_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + let referenda_count = pallet_referenda::ReferendumCount::::get(); + assert_eq!(referenda_count, 1); + let referenda_index = referenda_count - 1; + + let submission_deposit_amount = + ::SubmissionDeposit::get().saturated_into::(); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + assert_eq!( + SubmissionDeposits::::get(referenda_index), + Some(ReferendaSubmissionDeposit { + depositor: cluster_manager.clone(), + amount: submission_deposit_amount + }) + ); + + let balance_after_submission_deposit = Balances::free_balance(cluster_manager.clone()); + assert_eq!( + balance_before_submission_deposit.saturating_sub(submission_deposit_amount), + balance_after_submission_deposit + ); + + System::assert_has_event(Event::Closed { cluster_id, yes: 4, no: 0 }.into()); + System::assert_has_event(Event::Approved { cluster_id }.into()); + System::assert_has_event(Event::ReferendumSubmitted { cluster_id }.into()); + System::assert_has_event( + Event::SubmissionDepositRetained { + referenda_index, + depositor: cluster_manager.clone(), + amount: submission_deposit_amount, + } + .into(), + ); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_early_disapproved_with_unanimous_consensus_and_nay_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::ClusterManager + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + false, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_disapproved_with_unanimous_consensus_and_nay_default_vote() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_manager = AccountId::from(CLUSTER_MANAGER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + true, + ClusterMember::ClusterManager, + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + let start = BlockNumber::from(1_u64); + let end = start + ::ClusterProposalDuration::get(); + fast_forward_to(end + 1); + + assert_ok!(DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_manager.clone()), + cluster_id, + ClusterMember::ClusterManager, + )); + + // As the quorum is not reached, the proposal gets rejected and no referenda is created + assert_eq!(pallet_referenda::ReferendumCount::::get(), 0); + + assert!(!ClusterProposal::::contains_key(cluster_id)); + assert!(!ClusterProposalVoting::::contains_key(cluster_id)); + + System::assert_has_event(Event::Closed { cluster_id, yes: 3, no: 1 }.into()); + System::assert_has_event(Event::Disapproved { cluster_id }.into()); + System::assert_has_event(Event::Removed { cluster_id }.into()); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} + +#[test] +fn cluster_protocol_update_proposal_cannot_be_closed_if_threshold_is_not_reached_with_unanimous_consensus_and_nay_default_vote( +) { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_MANAGER_ID, + CLUSTER_RESERVE_ID, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + + let node_1 = build_cluster_node( + NODE_PUB_KEY_1, + NODE_PROVIDER_ID_1, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_2 = build_cluster_node( + NODE_PUB_KEY_2, + NODE_PROVIDER_ID_2, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + let node_3 = build_cluster_node( + NODE_PUB_KEY_3, + NODE_PROVIDER_ID_3, + StorageNodeParams::default(), + CLUSTER_ID, + ClusterNodeStatus::ValidationSucceeded, + ClusterNodeKind::Genesis, + ); + + ExtBuilder.build_and_execute(cluster, vec![node_1, node_2, node_3], || { + let lock1 = MockedDefaultVote::set_and_hold_lock(MockDefaultVote { + strategy: DefaultVoteVariant::NayAsDefaultVote, + }); + let lock2 = MockedSeatsConsensus::set_and_hold_lock(MockSeatsConsensus { + consensus: ConsensusVariant::Unanimous, + }); + fast_forward_to(1); + + let cluster_id = ClusterId::from(CLUSTER_ID); + let cluster_protocol_params = ClusterProtocolParams::default(); + + let cluster_node_1_provider = AccountId::from(NODE_PROVIDER_ID_1); + let cluster_node_1_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_1)); + + let cluster_node_2_provider = AccountId::from(NODE_PROVIDER_ID_2); + let cluster_node_2_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_2)); + + let cluster_node_3_provider = AccountId::from(NODE_PROVIDER_ID_3); + let cluster_node_3_key = NodePubKey::StoragePubKey(AccountId::from(NODE_PUB_KEY_3)); + + assert_ok!(DdcClustersGov::propose_update_cluster_protocol( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + cluster_protocol_params.clone(), + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_1_key.clone()) + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_2_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_2_key.clone()), + )); + + assert_ok!(DdcClustersGov::vote_proposal( + RuntimeOrigin::signed(cluster_node_3_provider.clone()), + cluster_id, + true, + ClusterMember::NodeProvider(cluster_node_3_key), + )); + + assert_noop!( + DdcClustersGov::close_proposal( + RuntimeOrigin::signed(cluster_node_1_provider.clone()), + cluster_id, + ClusterMember::NodeProvider(cluster_node_1_key) + ), + Error::::TooEarly + ); + + MockedDefaultVote::reset_and_release_lock(lock1); + MockedSeatsConsensus::reset_and_release_lock(lock2); + }) +} diff --git a/pallets/ddc-clusters-gov/src/weights.rs b/pallets/ddc-clusters-gov/src/weights.rs new file mode 100644 index 000000000..5bddb6ffc --- /dev/null +++ b/pallets/ddc-clusters-gov/src/weights.rs @@ -0,0 +1,365 @@ +//! Autogenerated weights for pallet_ddc_clusters_gov +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/cere +// benchmark +// pallet +// --chain=dev +// --execution=wasm +// --wasm-execution=compiled +// --pallet=pallet_ddc_clusters_gov +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --template=./.maintain/frame-weight-template.hbs +// --output=pallets/ddc-clusters-gov/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_ddc_clusters_gov. +pub trait WeightInfo { + fn propose_activate_cluster_protocol() -> Weight; + fn propose_update_cluster_protocol() -> Weight; + fn vote_proposal() -> Weight; + fn close_early_approved(m: u32, ) -> Weight; + fn close_approved(m: u32, ) -> Weight; + fn close_early_disapproved(m: u32, ) -> Weight; + fn close_disapproved(m: u32, ) -> Weight; + fn retract_proposal() -> Weight; + fn refund_submission_deposit() -> Weight; + fn activate_cluster_protocol() -> Weight; + fn update_cluster_protocol() -> Weight; +} + +/// Weights for pallet_ddc_clusters_gov using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn propose_activate_cluster_protocol() -> Weight { + Weight::from_parts(40_987_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn propose_update_cluster_protocol() -> Weight { + Weight::from_parts(40_476_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::ClustersNodes` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn vote_proposal() -> Weight { + Weight::from_parts(44_935_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::StatusFor` (r:1 w:1) + // Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumCount` (r:1 w:1) + // Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `Scheduler::Agenda` (r:1 w:1) + // Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `DdcClustersGov::SubmissionDeposits` (r:0 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::PreimageFor` (r:0 w:1) + // Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + /// The range of component `m` is `[4, 64]`. + fn close_early_approved(m: u32, ) -> Weight { + Weight::from_parts(200_906_983_u64, 0) + // Standard Error: 6_098 + .saturating_add(Weight::from_parts(224_016_u64, 0).saturating_mul(m as u64)) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::StatusFor` (r:1 w:1) + // Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumCount` (r:1 w:1) + // Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `Scheduler::Agenda` (r:1 w:1) + // Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `DdcClustersGov::SubmissionDeposits` (r:0 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::PreimageFor` (r:0 w:1) + // Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + /// The range of component `m` is `[4, 64]`. + fn close_approved(m: u32, ) -> Weight { + Weight::from_parts(253_269_234_u64, 0) + // Standard Error: 11_311 + .saturating_add(Weight::from_parts(102_968_u64, 0).saturating_mul(m as u64)) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `m` is `[4, 64]`. + fn close_early_disapproved(m: u32, ) -> Weight { + Weight::from_parts(44_770_482_u64, 0) + // Standard Error: 2_212 + .saturating_add(Weight::from_parts(126_113_u64, 0).saturating_mul(m as u64)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `m` is `[4, 64]`. + fn close_disapproved(m: u32, ) -> Weight { + Weight::from_parts(73_437_989_u64, 0) + // Standard Error: 3_685 + .saturating_add(Weight::from_parts(6_156_u64, 0).saturating_mul(m as u64)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn retract_proposal() -> Weight { + Weight::from_parts(24_526_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClustersGov::SubmissionDeposits` (r:1 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn refund_submission_deposit() -> Weight { + Weight::from_parts(120_817_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn activate_cluster_protocol() -> Weight { + Weight::from_parts(39_905_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_cluster_protocol() -> Weight { + Weight::from_parts(25_347_000_u64, 0) + .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: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn propose_activate_cluster_protocol() -> Weight { + Weight::from_parts(40_987_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn propose_update_cluster_protocol() -> Weight { + Weight::from_parts(40_476_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::ClustersNodes` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn vote_proposal() -> Weight { + Weight::from_parts(44_935_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::StatusFor` (r:1 w:1) + // Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumCount` (r:1 w:1) + // Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `Scheduler::Agenda` (r:1 w:1) + // Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `DdcClustersGov::SubmissionDeposits` (r:0 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::PreimageFor` (r:0 w:1) + // Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + /// The range of component `m` is `[4, 64]`. + fn close_early_approved(m: u32, ) -> Weight { + Weight::from_parts(200_906_983_u64, 0) + // Standard Error: 6_098 + .saturating_add(Weight::from_parts(224_016_u64, 0).saturating_mul(m as u64)) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::StatusFor` (r:1 w:1) + // Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumCount` (r:1 w:1) + // Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `Scheduler::Agenda` (r:1 w:1) + // Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + // Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `DdcClustersGov::SubmissionDeposits` (r:0 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Preimage::PreimageFor` (r:0 w:1) + // Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + /// The range of component `m` is `[4, 64]`. + fn close_approved(m: u32, ) -> Weight { + Weight::from_parts(253_269_234_u64, 0) + // Standard Error: 11_311 + .saturating_add(Weight::from_parts(102_968_u64, 0).saturating_mul(m as u64)) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `m` is `[4, 64]`. + fn close_early_disapproved(m: u32, ) -> Weight { + Weight::from_parts(44_770_482_u64, 0) + // Standard Error: 2_212 + .saturating_add(Weight::from_parts(126_113_u64, 0).saturating_mul(m as u64)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposal` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `m` is `[4, 64]`. + fn close_disapproved(m: u32, ) -> Weight { + Weight::from_parts(73_437_989_u64, 0) + // Standard Error: 3_685 + .saturating_add(Weight::from_parts(6_156_u64, 0).saturating_mul(m as u64)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClustersGov::ClusterProposal` (r:1 w:1) + // Proof: `DdcClustersGov::ClusterProposal` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClustersGov::ClusterProposalVoting` (r:0 w:1) + // Proof: `DdcClustersGov::ClusterProposalVoting` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn retract_proposal() -> Weight { + Weight::from_parts(24_526_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClustersGov::SubmissionDeposits` (r:1 w:1) + // Proof: `DdcClustersGov::SubmissionDeposits` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + // Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn refund_submission_deposit() -> Weight { + Weight::from_parts(120_817_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn activate_cluster_protocol() -> Weight { + Weight::from_parts(39_905_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_cluster_protocol() -> Weight { + Weight::from_parts(25_347_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/ddc-clusters/Cargo.toml b/pallets/ddc-clusters/Cargo.toml index 7dda7df3f..eda0f8c4f 100644 --- a/pallets/ddc-clusters/Cargo.toml +++ b/pallets/ddc-clusters/Cargo.toml @@ -23,6 +23,7 @@ frame-system = { workspace = true } pallet-contracts = { workspace = true } pallet-contracts-primitives = { workspace = true } sp-core = { workspace = true } +sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } diff --git a/pallets/ddc-clusters/src/benchmarking.rs b/pallets/ddc-clusters/src/benchmarking.rs index bcf46b49c..9e4a61fd4 100644 --- a/pallets/ddc-clusters/src/benchmarking.rs +++ b/pallets/ddc-clusters/src/benchmarking.rs @@ -1,6 +1,8 @@ //! DdcStaking pallet benchmarking. -use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams, NodePubKey}; +use ddc_primitives::{ + ClusterId, ClusterNodeKind, ClusterParams, ClusterProtocolParams, NodePubKey, +}; pub use frame_benchmarking::{ account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, BenchmarkError, @@ -17,6 +19,10 @@ use crate::{cluster::ClusterProps, Pallet as DdcClusters}; const USER_SEED: u32 = 999666; const USER_SEED_2: u32 = 999555; +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + benchmarks! { where_clause { where T::AccountId: UncheckedFrom + AsRef<[u8]> } @@ -30,7 +36,7 @@ benchmarks! { erasure_coding_total: 6, replication_total: 3 }; - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = ClusterProtocolParams { treasury_share: Perquintill::default(), validators_share: Perquintill::default(), cluster_reserve_share: Perquintill::default(), @@ -42,7 +48,7 @@ benchmarks! { unit_per_put_request: 10, unit_per_get_request: 10, }; - }: _(RawOrigin::Root, cluster_id, user.clone(), user, cluster_params, cluster_gov_params) + }: _(RawOrigin::Signed(user.clone()), cluster_id, user.clone(), cluster_params, cluster_protocol_params) verify { assert!(Clusters::::contains_key(cluster_id)); } @@ -55,6 +61,19 @@ benchmarks! { let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); let _ = ::Currency::make_free_balance_be(&user, balance); let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); + }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone(), ClusterNodeKind::Genesis) + verify { + assert!(ClustersNodes::::contains_key(cluster_id, node_pub_key)); + } + + join_cluster { + let bytes = [0u8; 32]; + let node_pub_key = NodePubKey::StoragePubKey(AccountId32::from(bytes)); + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone()) verify { assert!(ClustersNodes::::contains_key(cluster_id, node_pub_key)); @@ -71,7 +90,8 @@ benchmarks! { let _ = DdcClusters::::add_node( RawOrigin::Signed(user.clone()).into(), cluster_id, - node_pub_key.clone() + node_pub_key.clone(), + ClusterNodeKind::Genesis ); }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone()) verify { @@ -97,30 +117,24 @@ benchmarks! { node_provider_auth_contract: Some(user_2), erasure_coding_required: 4, erasure_coding_total: 6, - replication_total: 3 + replication_total: 3, } ); } - set_cluster_gov_params { + validate_node { + let bytes = [0u8; 32]; + let node_pub_key = NodePubKey::StoragePubKey(AccountId32::from(bytes)); let cluster_id = ClusterId::from([1; 20]); let user = account::("user", USER_SEED, 0u32); - let _ = config_cluster::(user, cluster_id); - let new_cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { - treasury_share: Perquintill::default(), - validators_share: Perquintill::default(), - cluster_reserve_share: Perquintill::default(), - storage_bond_size: 10u32.into(), - storage_chill_delay: 5u32.into(), - storage_unbonding_delay: 5u32.into(), - unit_per_mb_stored: 1, - unit_per_mb_streamed: 1, - unit_per_put_request: 1, - unit_per_get_request: 1, - }; - }: _(RawOrigin::Root, cluster_id, new_cluster_gov_params.clone()) + let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); + DdcClusters::::add_node(RawOrigin::Signed(user.clone()).into(), cluster_id, node_pub_key.clone(), ClusterNodeKind::Genesis)?; + + }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone(), true) verify { - assert_eq!(ClustersGovParams::::try_get(cluster_id).unwrap(), new_cluster_gov_params); + assert_last_event::(Event::ClusterNodeValidated { cluster_id, node_pub_key, succeeded: true}.into()); } impl_benchmark_test_suite!( diff --git a/pallets/ddc-clusters/src/cluster.rs b/pallets/ddc-clusters/src/cluster.rs index 0d9abae99..bc3fe15d5 100644 --- a/pallets/ddc-clusters/src/cluster.rs +++ b/pallets/ddc-clusters/src/cluster.rs @@ -1,11 +1,9 @@ use codec::{Decode, Encode}; -use ddc_primitives::{ClusterId, ClusterParams}; +use ddc_primitives::{ClusterId, ClusterParams, ClusterStatus, DdcEra}; use frame_support::{pallet_prelude::*, parameter_types}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use crate::pallet::Error; - parameter_types! { pub MaxClusterParamsLen: u16 = 2048; } @@ -16,6 +14,8 @@ pub struct Cluster { pub manager_id: AccountId, pub reserve_id: AccountId, pub props: ClusterProps, + pub status: ClusterStatus, + pub last_validated_era_id: DdcEra, } #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] @@ -32,8 +32,8 @@ impl Cluster { manager_id: AccountId, reserve_id: AccountId, cluster_params: ClusterParams, - ) -> Result, ClusterError> { - Ok(Cluster { + ) -> Cluster { + Cluster { cluster_id, manager_id, reserve_id, @@ -43,31 +43,25 @@ impl Cluster { erasure_coding_total: cluster_params.erasure_coding_total, replication_total: cluster_params.replication_total, }, - }) + status: ClusterStatus::Unbonded, + last_validated_era_id: DdcEra::default(), + } } - pub fn set_params( - &mut self, - cluster_params: ClusterParams, - ) -> Result<(), ClusterError> { + pub fn set_params(&mut self, cluster_params: ClusterParams) { self.props = ClusterProps { node_provider_auth_contract: cluster_params.node_provider_auth_contract, erasure_coding_required: cluster_params.erasure_coding_required, erasure_coding_total: cluster_params.erasure_coding_total, replication_total: cluster_params.replication_total, }; - Ok(()) } -} -pub enum ClusterError { - ClusterParamsExceedsLimit, -} + pub fn set_status(&mut self, status: ClusterStatus) { + self.status = status; + } -impl From for Error { - fn from(error: ClusterError) -> Self { - match error { - ClusterError::ClusterParamsExceedsLimit => Error::::ClusterParamsExceedsLimit, - } + pub fn can_manage_nodes(&self) -> bool { + matches!(self.status, ClusterStatus::Bonded | ClusterStatus::Activated) } } diff --git a/pallets/ddc-clusters/src/lib.rs b/pallets/ddc-clusters/src/lib.rs index 337b04ed4..78ecb63cf 100644 --- a/pallets/ddc-clusters/src/lib.rs +++ b/pallets/ddc-clusters/src/lib.rs @@ -26,15 +26,17 @@ pub(crate) mod mock; #[cfg(test)] mod tests; -pub mod migration; +pub mod migrations; +const LOG_TARGET: &str = "runtime::ddc-clusters"; use ddc_primitives::{ traits::{ - cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + cluster::{ClusterCreator, ClusterProtocol, ClusterQuery, ClusterValidator}, staking::{StakerCreator, StakingVisitor, StakingVisitorError}, }, - ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterId, ClusterParams, - ClusterPricingParams, NodePubKey, NodeType, + ClusterBondingParams, ClusterFeesParams, ClusterId, ClusterNodeKind, ClusterNodeState, + ClusterNodeStatus, ClusterNodesStats, ClusterParams, ClusterPricingParams, + ClusterProtocolParams, ClusterStatus, DdcEra, NodePubKey, NodeType, }; use frame_support::{ assert_ok, @@ -62,13 +64,13 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { - use ddc_primitives::traits::cluster::{ClusterManager, ClusterManagerError}; + use ddc_primitives::traits::cluster::ClusterManager; use super::*; /// The current storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = - frame_support::traits::StorageVersion::new(1); + frame_support::traits::StorageVersion::new(3); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -98,7 +100,13 @@ pub mod pallet { ClusterNodeAdded { cluster_id: ClusterId, node_pub_key: NodePubKey }, ClusterNodeRemoved { cluster_id: ClusterId, node_pub_key: NodePubKey }, ClusterParamsSet { cluster_id: ClusterId }, - ClusterGovParamsSet { cluster_id: ClusterId }, + ClusterProtocolParamsSet { cluster_id: ClusterId }, + ClusterActivated { cluster_id: ClusterId }, + ClusterBonded { cluster_id: ClusterId }, + ClusterUnbonding { cluster_id: ClusterId }, + ClusterUnbonded { cluster_id: ClusterId }, + ClusterNodeValidated { cluster_id: ClusterId, node_pub_key: NodePubKey, succeeded: bool }, + ClusterEraValidated { cluster_id: ClusterId, era_id: DdcEra }, } #[pallet::error] @@ -111,6 +119,7 @@ pub mod pallet { AttemptToRemoveNonExistentNode, AttemptToRemoveNotAssignedNode, OnlyClusterManager, + OnlyNodeProvider, NodeIsNotAuthorized, NodeHasNoActivatedStake, NodeStakeIsInvalid, @@ -122,6 +131,13 @@ pub mod pallet { ErasureCodingRequiredDidNotMeetMinimum, ErasureCodingTotalNotMeetMinimum, ReplicationTotalDidNotMeetMinimum, + ClusterAlreadyActivated, + UnexpectedClusterStatus, + AttemptToValidateNotAssignedNode, + ClusterProtocolParamsNotSet, + ArithmeticOverflow, + NodeIsNotAssignedToCluster, + ControllerDoesNotExist, } #[pallet::storage] @@ -130,9 +146,13 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, ClusterId, Cluster>; #[pallet::storage] - #[pallet::getter(fn clusters_gov_params)] - pub type ClustersGovParams = - StorageMap<_, Twox64Concat, ClusterId, ClusterGovParams, BlockNumberFor>>; + #[pallet::getter(fn clusters_protocol_params)] + pub type ClustersGovParams = StorageMap< + _, + Twox64Concat, + ClusterId, + ClusterProtocolParams, BlockNumberFor>, + >; #[pallet::storage] #[pallet::getter(fn clusters_nodes)] @@ -142,24 +162,30 @@ pub mod pallet { ClusterId, Blake2_128Concat, NodePubKey, - bool, + ClusterNodeState>, OptionQuery, >; + #[pallet::storage] + #[pallet::getter(fn clusters_nodes_stats)] + pub type ClustersNodesStats = + StorageMap<_, Twox64Concat, ClusterId, ClusterNodesStats>; + #[pallet::genesis_config] pub struct GenesisConfig { pub clusters: Vec>, #[allow(clippy::type_complexity)] - pub clusters_gov_params: - Vec<(ClusterId, ClusterGovParams, BlockNumberFor>)>, - pub clusters_nodes: Vec<(ClusterId, Vec)>, + pub clusters_protocol_params: + Vec<(ClusterId, ClusterProtocolParams, BlockNumberFor>)>, + #[allow(clippy::type_complexity)] + pub clusters_nodes: Vec<(ClusterId, Vec<(NodePubKey, ClusterNodeKind, ClusterNodeStatus)>)>, } impl Default for GenesisConfig { fn default() -> Self { GenesisConfig { clusters: Default::default(), - clusters_gov_params: Default::default(), + clusters_protocol_params: Default::default(), clusters_nodes: Default::default(), } } @@ -173,20 +199,19 @@ pub mod pallet { fn build(&self) { for cluster in &self.clusters { assert_ok!(Pallet::::create_cluster( - frame_system::Origin::::Root.into(), + frame_system::Origin::::Signed(cluster.manager_id.clone()).into(), cluster.cluster_id, - cluster.manager_id.clone(), cluster.reserve_id.clone(), ClusterParams:: { node_provider_auth_contract: cluster .props .node_provider_auth_contract .clone(), - erasure_coding_required: 4, - erasure_coding_total: 6, - replication_total: 3 + erasure_coding_required: cluster.props.erasure_coding_required, + erasure_coding_total: cluster.props.erasure_coding_total, + replication_total: cluster.props.replication_total, }, - self.clusters_gov_params + self.clusters_protocol_params .iter() .find(|(id, _)| id == &cluster.cluster_id) .unwrap() @@ -194,10 +219,44 @@ pub mod pallet { .clone(), )); + Clusters::::mutate(cluster.cluster_id, |value| { + if let Some(clust) = value { + clust.status = cluster.status.clone(); + }; + }); + for (cluster_id, nodes) in &self.clusters_nodes { - for node_pub_key in nodes { - >::insert(cluster_id, node_pub_key, true); + let mut stats = ClusterNodesStats { + await_validation: 0, + validation_succeeded: 0, + validation_failed: 0, + }; + + for (node_pub_key, kind, status) in nodes { + >::insert( + cluster_id, + node_pub_key, + ClusterNodeState { + kind: kind.clone(), + status: status.clone(), + added_at: frame_system::Pallet::::block_number(), + }, + ); + match status { + ClusterNodeStatus::AwaitsValidation => { + stats.await_validation = stats.await_validation.saturating_add(1); + }, + ClusterNodeStatus::ValidationSucceeded => { + stats.validation_succeeded = + stats.validation_succeeded.saturating_add(1); + }, + ClusterNodeStatus::ValidationFailed => { + stats.validation_failed = stats.validation_failed.saturating_add(1); + }, + } } + + >::insert(cluster.cluster_id, stats); } } } @@ -213,18 +272,17 @@ pub mod pallet { pub fn create_cluster( origin: OriginFor, cluster_id: ClusterId, - cluster_manager_id: T::AccountId, cluster_reserve_id: T::AccountId, cluster_params: ClusterParams, - cluster_gov_params: ClusterGovParams, BlockNumberFor>, + initial_protocol_params: ClusterProtocolParams, BlockNumberFor>, ) -> DispatchResult { - ensure_root(origin)?; // requires Governance approval + let cluster_manager_id = ensure_signed(origin)?; Self::do_create_cluster( cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params, - cluster_gov_params, + initial_protocol_params, ) } @@ -234,6 +292,7 @@ pub mod pallet { origin: OriginFor, cluster_id: ClusterId, node_pub_key: NodePubKey, + node_kind: ClusterNodeKind, ) -> DispatchResult { let caller_id = ensure_signed(origin)?; let cluster = @@ -253,29 +312,10 @@ pub mod pallet { ensure!(!has_chilling_attempt, Error::::NodeChillingIsProhibited); // Node with this node with this public key exists. - let node = T::NodeRepository::get(node_pub_key.clone()) + T::NodeRepository::get(node_pub_key.clone()) .map_err(|_| Error::::AttemptToAddNonExistentNode)?; - // Cluster extension smart contract allows joining. - if let Some(address) = cluster.props.node_provider_auth_contract { - let auth_contract = NodeProviderAuthContract::::new(address, caller_id); - - let is_authorized = auth_contract - .is_authorized( - node.get_provider_id().to_owned(), - node.get_pub_key(), - node.get_type(), - ) - .map_err(Into::>::into)?; - ensure!(is_authorized, Error::::NodeIsNotAuthorized); - }; - - // Add node to the cluster. - >::add_node(&cluster_id, &node_pub_key) - .map_err(Into::>::into)?; - Self::deposit_event(Event::::ClusterNodeAdded { cluster_id, node_pub_key }); - - Ok(()) + Self::do_add_node(cluster, node_pub_key, node_kind) } #[pallet::call_index(2)] @@ -291,12 +331,7 @@ pub mod pallet { ensure!(cluster.manager_id == caller_id, Error::::OnlyClusterManager); - // Remove node from the cluster. - >::remove_node(&cluster_id, &node_pub_key) - .map_err(Into::>::into)?; - Self::deposit_event(Event::::ClusterNodeRemoved { cluster_id, node_pub_key }); - - Ok(()) + Self::do_remove_node(cluster, node_pub_key) } // Sets Governance non-sensetive parameters only @@ -323,28 +358,77 @@ pub mod pallet { cluster_params.replication_total >= T::MinReplicationTotalLimit::get(), Error::::ReplicationTotalDidNotMeetMinimum ); - cluster.set_params(cluster_params).map_err(Into::>::into)?; + cluster.set_params(cluster_params); Clusters::::insert(cluster_id, cluster); Self::deposit_event(Event::::ClusterParamsSet { cluster_id }); Ok(()) } - // Requires Governance approval #[pallet::call_index(4)] - #[pallet::weight(::WeightInfo::set_cluster_gov_params())] - pub fn set_cluster_gov_params( + #[pallet::weight(::WeightInfo::validate_node())] + pub fn validate_node( origin: OriginFor, cluster_id: ClusterId, - cluster_gov_params: ClusterGovParams, BlockNumberFor>, + node_pub_key: NodePubKey, + succeeded: bool, ) -> DispatchResult { - ensure_root(origin)?; // requires Governance approval - let _cluster = + let caller_id = ensure_signed(origin)?; + let cluster = Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; - ClustersGovParams::::insert(cluster_id, cluster_gov_params); - Self::deposit_event(Event::::ClusterGovParamsSet { cluster_id }); + // todo: allow to execute this extrinsic to Validator's manager only + ensure!(cluster.manager_id == caller_id, Error::::OnlyClusterManager); - Ok(()) + Self::do_validate_node(cluster_id, node_pub_key, succeeded) + } + + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::join_cluster())] + pub fn join_cluster( + origin: OriginFor, + cluster_id: ClusterId, + node_pub_key: NodePubKey, + ) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + + // Cluster with a given id exists and has an auth smart contract. + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + let node_provider_auth_contract_address = cluster + .props + .node_provider_auth_contract + .clone() + .ok_or(Error::::NodeIsNotAuthorized)?; + + // Node with this public key exists and belongs to the caller. + let node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; + ensure!(*node.get_provider_id() == caller_id, Error::::OnlyNodeProvider); + + // Sufficient funds are locked at the DDC Staking module. + let has_activated_stake = + T::StakingVisitor::has_activated_stake(&node_pub_key, &cluster_id) + .map_err(Into::>::into)?; + ensure!(has_activated_stake, Error::::NodeHasNoActivatedStake); + + // Candidate is not planning to pause operations any time soon. + let has_chilling_attempt = T::StakingVisitor::has_chilling_attempt(&node_pub_key) + .map_err(Into::>::into)?; + ensure!(!has_chilling_attempt, Error::::NodeChillingIsProhibited); + + // Cluster auth smart contract allows joining. + let auth_contract = + NodeProviderAuthContract::::new(node_provider_auth_contract_address, caller_id); + let is_authorized = auth_contract + .is_authorized( + node.get_provider_id().to_owned(), + node.get_pub_key(), + node.get_type(), + ) + .map_err(Into::>::into)?; + ensure!(is_authorized, Error::::NodeIsNotAuthorized); + + Self::do_join_cluster(cluster, node_pub_key) } } @@ -354,7 +438,7 @@ pub mod pallet { cluster_manager_id: T::AccountId, cluster_reserve_id: T::AccountId, cluster_params: ClusterParams, - cluster_gov_params: ClusterGovParams, BlockNumberFor>, + initial_protocol_params: ClusterProtocolParams, BlockNumberFor>, ) -> DispatchResult { ensure!(!Clusters::::contains_key(cluster_id), Error::::ClusterAlreadyExists); @@ -371,150 +455,530 @@ pub mod pallet { Error::::ReplicationTotalDidNotMeetMinimum ); + ensure!(!Clusters::::contains_key(cluster_id), Error::::ClusterAlreadyExists); let cluster = - Cluster::new(cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params) - .map_err(Into::>::into)?; + Cluster::new(cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params); + Clusters::::insert(cluster_id, cluster); - ClustersGovParams::::insert(cluster_id, cluster_gov_params); Self::deposit_event(Event::::ClusterCreated { cluster_id }); + ClustersGovParams::::insert(cluster_id, initial_protocol_params); + Self::deposit_event(Event::::ClusterProtocolParamsSet { cluster_id }); + ClustersNodesStats::::insert(cluster_id, ClusterNodesStats::default()); + + Ok(()) + } + + fn do_bond_cluster(cluster_id: &ClusterId) -> DispatchResult { + let mut cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!(cluster.status == ClusterStatus::Unbonded, Error::::UnexpectedClusterStatus); + + cluster.set_status(ClusterStatus::Bonded); + Clusters::::insert(cluster_id, cluster); + Self::deposit_event(Event::::ClusterBonded { cluster_id: *cluster_id }); + + Ok(()) + } + + fn do_activate_cluster_protocol(cluster_id: &ClusterId) -> DispatchResult { + let mut cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!(cluster.status == ClusterStatus::Bonded, Error::::UnexpectedClusterStatus); + + cluster.set_status(ClusterStatus::Activated); + Clusters::::insert(cluster_id, cluster); + Self::deposit_event(Event::::ClusterActivated { cluster_id: *cluster_id }); + + Ok(()) + } + + fn do_start_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult { + let mut cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!(Self::can_unbond_cluster(cluster_id), Error::::UnexpectedClusterStatus); + + cluster.set_status(ClusterStatus::Unbonding); + Clusters::::insert(cluster_id, cluster); + Self::deposit_event(Event::::ClusterUnbonding { cluster_id: *cluster_id }); + + Ok(()) + } + + fn do_end_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult { + let mut cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!( + cluster.status == ClusterStatus::Unbonding, + Error::::UnexpectedClusterStatus + ); + + cluster.set_status(ClusterStatus::Unbonded); + Clusters::::insert(cluster_id, cluster); + Self::deposit_event(Event::::ClusterUnbonded { cluster_id: *cluster_id }); + + Ok(()) + } + + fn do_update_cluster_protocol( + cluster_id: &ClusterId, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + ensure!( + ClustersGovParams::::contains_key(cluster_id), + Error::::ClusterProtocolParamsNotSet + ); + + ClustersGovParams::::insert(cluster_id, cluster_protocol_params); + Self::deposit_event(Event::::ClusterProtocolParamsSet { cluster_id: *cluster_id }); + + Ok(()) + } + + fn do_add_node( + cluster: Cluster, + node_pub_key: NodePubKey, + node_kind: ClusterNodeKind, + ) -> DispatchResult { + ensure!(cluster.can_manage_nodes(), Error::::UnexpectedClusterStatus); + + let mut node: pallet_ddc_nodes::Node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; + + ensure!(node.get_cluster_id().is_none(), Error::::AttemptToAddAlreadyAssignedNode); + node.set_cluster_id(Some(cluster.cluster_id)); + T::NodeRepository::update(node).map_err(|_| Error::::AttemptToAddNonExistentNode)?; + + ClustersNodes::::insert( + cluster.cluster_id, + node_pub_key.clone(), + ClusterNodeState { + kind: node_kind.clone(), + status: ClusterNodeStatus::AwaitsValidation, + added_at: frame_system::Pallet::::block_number(), + }, + ); + Self::deposit_event(Event::::ClusterNodeAdded { + cluster_id: cluster.cluster_id, + node_pub_key, + }); + + let mut current_stats = ClustersNodesStats::::try_get(cluster.cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + current_stats.await_validation = current_stats + .await_validation + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + + ClustersNodesStats::::insert(cluster.cluster_id, current_stats); + + Ok(()) + } + + fn do_join_cluster( + cluster: Cluster, + node_pub_key: NodePubKey, + ) -> DispatchResult { + ensure!(cluster.can_manage_nodes(), Error::::UnexpectedClusterStatus); + + let mut node: pallet_ddc_nodes::Node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; + ensure!(node.get_cluster_id().is_none(), Error::::AttemptToAddAlreadyAssignedNode); + + node.set_cluster_id(Some(cluster.cluster_id)); + T::NodeRepository::update(node).map_err(|_| Error::::AttemptToAddNonExistentNode)?; + + ClustersNodes::::insert( + cluster.cluster_id, + node_pub_key.clone(), + ClusterNodeState { + kind: ClusterNodeKind::External, + status: ClusterNodeStatus::ValidationSucceeded, + added_at: frame_system::Pallet::::block_number(), + }, + ); + Self::deposit_event(Event::::ClusterNodeAdded { + cluster_id: cluster.cluster_id, + node_pub_key, + }); + + let mut current_stats = ClustersNodesStats::::try_get(cluster.cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + ClustersNodesStats::::insert(cluster.cluster_id, current_stats); Ok(()) } + + fn do_remove_node( + cluster: Cluster, + node_pub_key: NodePubKey, + ) -> DispatchResult { + ensure!(cluster.can_manage_nodes(), Error::::UnexpectedClusterStatus); + + let mut node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToRemoveNonExistentNode)?; + + ensure!( + node.get_cluster_id() == &Some(cluster.cluster_id), + Error::::AttemptToRemoveNotAssignedNode + ); + + node.set_cluster_id(None); + T::NodeRepository::update(node) + .map_err(|_| Error::::AttemptToRemoveNonExistentNode)?; + + let current_node_state = + ClustersNodes::::take(cluster.cluster_id, node_pub_key.clone()) + .ok_or(Error::::AttemptToRemoveNotAssignedNode)?; + Self::deposit_event(Event::::ClusterNodeRemoved { + cluster_id: cluster.cluster_id, + node_pub_key, + }); + + let mut current_stats = ClustersNodesStats::::try_get(cluster.cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + + let updated_stats = match current_node_state.status { + ClusterNodeStatus::AwaitsValidation => { + current_stats.await_validation = current_stats + .await_validation + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + ClusterNodeStatus::ValidationSucceeded => { + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + ClusterNodeStatus::ValidationFailed => { + current_stats.validation_failed = current_stats + .validation_failed + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + }; + + ClustersNodesStats::::insert(cluster.cluster_id, updated_stats); + + Ok(()) + } + + fn do_validate_node( + cluster_id: ClusterId, + node_pub_key: NodePubKey, + succeeded: bool, + ) -> DispatchResult { + let mut current_stats = ClustersNodesStats::::try_get(cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + let mut current_node_state = + ClustersNodes::::try_get(cluster_id, node_pub_key.clone()) + .map_err(|_| Error::::AttemptToValidateNotAssignedNode)?; + + let updated_stats = match current_node_state.status { + ClusterNodeStatus::AwaitsValidation if succeeded => { + current_stats.await_validation = current_stats + .await_validation + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + ClusterNodeStatus::AwaitsValidation if !succeeded => { + current_stats.await_validation = current_stats + .await_validation + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats.validation_failed = current_stats + .validation_failed + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + ClusterNodeStatus::ValidationSucceeded if !succeeded => { + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats.validation_failed = current_stats + .validation_failed + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + ClusterNodeStatus::ValidationFailed if succeeded => { + current_stats.validation_failed = current_stats + .validation_failed + .checked_sub(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + current_stats + }, + _ => current_stats, + }; + + current_node_state.status = if succeeded { + ClusterNodeStatus::ValidationSucceeded + } else { + ClusterNodeStatus::ValidationFailed + }; + + ClustersNodes::::insert(cluster_id, node_pub_key.clone(), current_node_state); + Self::deposit_event(Event::::ClusterNodeValidated { + cluster_id, + node_pub_key, + succeeded, + }); + ClustersNodesStats::::insert(cluster_id, updated_stats); + + Ok(()) + } + + fn can_unbond_cluster(cluster_id: &ClusterId) -> bool { + let cluster = match Clusters::::get(cluster_id) { + Some(cluster) => cluster, + None => return false, + }; + + let is_empty_nodes = ClustersNodesStats::::try_get(cluster_id) + .map(|status| { + status.await_validation + status.validation_succeeded + status.validation_failed == + 0 + }) + .unwrap_or(false); + + is_empty_nodes && + matches!(cluster.status, ClusterStatus::Bonded | ClusterStatus::Activated) + } } - impl ClusterVisitor for Pallet { - fn ensure_cluster(cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { - Clusters::::get(cluster_id) - .map(|_| ()) - .ok_or(ClusterVisitorError::ClusterDoesNotExist) + impl ClusterQuery for Pallet { + fn cluster_exists(cluster_id: &ClusterId) -> bool { + Clusters::::contains_key(cluster_id) + } + + fn get_cluster_status(cluster_id: &ClusterId) -> Result { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Ok(cluster.status) } + fn get_manager_and_reserve_id( + cluster_id: &ClusterId, + ) -> Result<(T::AccountId, T::AccountId), DispatchError> { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Ok((cluster.manager_id, cluster.reserve_id)) + } + } + + impl ClusterProtocol> for Pallet + where + T::AccountId: UncheckedFrom + AsRef<[u8]>, + { fn get_bond_size( cluster_id: &ClusterId, node_type: NodeType, - ) -> Result { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + ) -> Result { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; match node_type { NodeType::Storage => - Ok(cluster_gov_params.storage_bond_size.saturated_into::()), + Ok(cluster_protocol_params.storage_bond_size.saturated_into::()), } } fn get_pricing_params( cluster_id: &ClusterId, - ) -> Result { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + ) -> Result { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; Ok(ClusterPricingParams { - unit_per_mb_stored: cluster_gov_params.unit_per_mb_stored, - unit_per_mb_streamed: cluster_gov_params.unit_per_mb_streamed, - unit_per_put_request: cluster_gov_params.unit_per_put_request, - unit_per_get_request: cluster_gov_params.unit_per_get_request, + unit_per_mb_stored: cluster_protocol_params.unit_per_mb_stored, + unit_per_mb_streamed: cluster_protocol_params.unit_per_mb_streamed, + unit_per_put_request: cluster_protocol_params.unit_per_put_request, + unit_per_get_request: cluster_protocol_params.unit_per_get_request, }) } - fn get_fees_params( - cluster_id: &ClusterId, - ) -> Result { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + fn get_fees_params(cluster_id: &ClusterId) -> Result { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; Ok(ClusterFeesParams { - treasury_share: cluster_gov_params.treasury_share, - validators_share: cluster_gov_params.validators_share, - cluster_reserve_share: cluster_gov_params.cluster_reserve_share, + treasury_share: cluster_protocol_params.treasury_share, + validators_share: cluster_protocol_params.validators_share, + cluster_reserve_share: cluster_protocol_params.cluster_reserve_share, }) } - fn get_reserve_account_id( - cluster_id: &ClusterId, - ) -> Result { - let cluster = Clusters::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterDoesNotExist)?; - Ok(cluster.reserve_id) - } - fn get_chill_delay( cluster_id: &ClusterId, node_type: NodeType, - ) -> Result, ClusterVisitorError> { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + ) -> Result, DispatchError> { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; match node_type { - NodeType::Storage => Ok(cluster_gov_params.storage_chill_delay), + NodeType::Storage => Ok(cluster_protocol_params.storage_chill_delay), } } fn get_unbonding_delay( cluster_id: &ClusterId, node_type: NodeType, - ) -> Result, ClusterVisitorError> { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + ) -> Result, DispatchError> { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; match node_type { - NodeType::Storage => Ok(cluster_gov_params.storage_unbonding_delay), + NodeType::Storage => Ok(cluster_protocol_params.storage_unbonding_delay), } } fn get_bonding_params( cluster_id: &ClusterId, - ) -> Result>, ClusterVisitorError> { - let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) - .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + ) -> Result>, DispatchError> { + let cluster_protocol_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; Ok(ClusterBondingParams { - storage_bond_size: cluster_gov_params.storage_bond_size.saturated_into::(), - storage_chill_delay: cluster_gov_params.storage_chill_delay, - storage_unbonding_delay: cluster_gov_params.storage_unbonding_delay, + storage_bond_size: cluster_protocol_params + .storage_bond_size + .saturated_into::(), + storage_chill_delay: cluster_protocol_params.storage_chill_delay, + storage_unbonding_delay: cluster_protocol_params.storage_unbonding_delay, }) } - } - impl ClusterManager for Pallet { - fn contains_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool { - ClustersNodes::::get(cluster_id, node_pub_key).is_some() + fn get_reserve_account_id(cluster_id: &ClusterId) -> Result { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Ok(cluster.reserve_id) } - fn add_node( + fn activate_cluster_protocol(cluster_id: &ClusterId) -> DispatchResult { + Self::do_activate_cluster_protocol(cluster_id) + } + + fn update_cluster_protocol( cluster_id: &ClusterId, - node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { - let mut node = T::NodeRepository::get(node_pub_key.clone()) - .map_err(|_| ClusterManagerError::AttemptToAddNonExistentNode)?; + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + Self::do_update_cluster_protocol(cluster_id, cluster_protocol_params) + } - ensure!( - node.get_cluster_id().is_none(), - ClusterManagerError::AttemptToAddAlreadyAssignedNode - ); + fn bond_cluster(cluster_id: &ClusterId) -> DispatchResult { + Self::do_bond_cluster(cluster_id) + } - node.set_cluster_id(Some(*cluster_id)); - T::NodeRepository::update(node) - .map_err(|_| ClusterManagerError::AttemptToAddNonExistentNode)?; + fn start_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult { + Self::do_start_unbond_cluster(cluster_id) + } - ClustersNodes::::insert(cluster_id, node_pub_key.clone(), true); + fn end_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult { + Self::do_end_unbond_cluster(cluster_id) + } + } + impl ClusterValidator for Pallet { + fn set_last_validated_era( + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Result<(), DispatchError> { + let mut cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + + cluster.last_validated_era_id = era_id; + Clusters::::insert(cluster_id, cluster); + Self::deposit_event(Event::::ClusterEraValidated { + cluster_id: *cluster_id, + era_id, + }); Ok(()) } + } - fn remove_node( + impl ClusterManager for Pallet { + fn get_manager_account_id(cluster_id: &ClusterId) -> Result { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Ok(cluster.manager_id) + } + + fn get_nodes(cluster_id: &ClusterId) -> Result, DispatchError> { + let mut nodes = Vec::new(); + + // Iterate through all nodes associated with the cluster_id + for (node_pubkey, _) in ClustersNodes::::iter_prefix(cluster_id) { + nodes.push(node_pubkey); + } + + Ok(nodes) + } + + fn contains_node( cluster_id: &ClusterId, node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { - let mut node = T::NodeRepository::get(node_pub_key.clone()) - .map_err(|_| ClusterManagerError::AttemptToRemoveNonExistentNode)?; + validation_status: Option, + ) -> bool { + match validation_status { + Some(status) => ClustersNodes::::try_get(cluster_id, node_pub_key) + .map(|n| n.status == status) + .unwrap_or(false), + None => ClustersNodes::::get(cluster_id, node_pub_key).is_some(), + } + } - ensure!( - node.get_cluster_id() == &Some(*cluster_id), - ClusterManagerError::AttemptToRemoveNotAssignedNode - ); + fn add_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + node_kind: &ClusterNodeKind, + ) -> Result<(), DispatchError> { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Self::do_add_node(cluster, node_pub_key.clone(), node_kind.clone()) + } - node.set_cluster_id(None); - T::NodeRepository::update(node) - .map_err(|_| ClusterManagerError::AttemptToRemoveNonExistentNode)?; + fn remove_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result<(), DispatchError> { + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + Self::do_remove_node(cluster, node_pub_key.clone()) + } - ClustersNodes::::remove(cluster_id, node_pub_key.clone()); + fn get_node_state( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result>, DispatchError> { + let node_state = ClustersNodes::::try_get(cluster_id, node_pub_key) + .map_err(|_| Error::::NodeIsNotAssignedToCluster)?; + Ok(node_state) + } - Ok(()) + fn get_nodes_stats(cluster_id: &ClusterId) -> Result { + let current_stats = ClustersNodesStats::::try_get(cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + Ok(current_stats) + } + + fn validate_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + succeeded: bool, + ) -> Result<(), DispatchError> { + Self::do_validate_node(*cluster_id, node_pub_key.clone(), succeeded) } } @@ -522,19 +986,19 @@ pub mod pallet { where T::AccountId: UncheckedFrom + AsRef<[u8]>, { - fn create_new_cluster( + fn create_cluster( cluster_id: ClusterId, cluster_manager_id: T::AccountId, cluster_reserve_id: T::AccountId, cluster_params: ClusterParams, - cluster_gov_params: ClusterGovParams, BlockNumberFor>, + initial_protocol_params: ClusterProtocolParams, BlockNumberFor>, ) -> DispatchResult { Self::do_create_cluster( cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params, - cluster_gov_params, + initial_protocol_params, ) } } @@ -544,6 +1008,7 @@ pub mod pallet { match error { StakingVisitorError::NodeStakeDoesNotExist => Error::::NodeHasNoActivatedStake, StakingVisitorError::NodeStakeIsInBadState => Error::::NodeStakeIsInvalid, + StakingVisitorError::ControllerDoesNotExist => Error::::ControllerDoesNotExist, } } } @@ -560,19 +1025,4 @@ pub mod pallet { } } } - - impl From for Error { - fn from(error: ClusterManagerError) -> Self { - match error { - ClusterManagerError::AttemptToRemoveNotAssignedNode => - Error::::AttemptToRemoveNotAssignedNode, - ClusterManagerError::AttemptToRemoveNonExistentNode => - Error::::AttemptToRemoveNonExistentNode, - ClusterManagerError::AttemptToAddNonExistentNode => - Error::::AttemptToAddNonExistentNode, - ClusterManagerError::AttemptToAddAlreadyAssignedNode => - Error::::AttemptToAddAlreadyAssignedNode, - } - } - } } diff --git a/pallets/ddc-clusters/src/migration.rs b/pallets/ddc-clusters/src/migration.rs deleted file mode 100644 index cdb17d46e..000000000 --- a/pallets/ddc-clusters/src/migration.rs +++ /dev/null @@ -1,198 +0,0 @@ -#[cfg(feature = "try-runtime")] -use frame_support::ensure; -use frame_support::{ - storage_alias, - traits::{Get, GetStorageVersion, OnRuntimeUpgrade, StorageVersion}, - weights::Weight, -}; -use log::info; -#[cfg(feature = "try-runtime")] -use sp_runtime::DispatchError; -use sp_runtime::Saturating; - -use super::*; -use crate::cluster::ClusterProps; - -const LOG_TARGET: &str = "ddc-clusters"; - -pub mod v0 { - use frame_support::pallet_prelude::*; - - use super::*; - - #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] - pub struct Cluster { - pub cluster_id: ClusterId, - pub manager_id: AccountId, - pub reserve_id: AccountId, - pub props: ClusterProps, - } - - #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] - pub struct ClusterProps { - pub node_provider_auth_contract: Option, - } - - #[storage_alias] - pub(super) type Clusters = StorageMap< - crate::Pallet, - Blake2_128Concat, - ClusterId, - Cluster<::AccountId>, - >; -} - -pub fn migrate_to_v1() -> Weight { - let on_chain_version = Pallet::::on_chain_storage_version(); - let current_version = Pallet::::current_storage_version(); - - info!( - target: LOG_TARGET, - "Running migration with current storage version {:?} / onchain {:?}", - current_version, - on_chain_version - ); - - if on_chain_version == 0 && current_version == 1 { - let mut translated = 0u64; - let count = v0::Clusters::::iter().count(); - info!( - target: LOG_TARGET, - " >>> Updating DDC Cluster storage. Migrating {} clusters...", count - ); - - Clusters::::translate::, _>( - |cluster_id: ClusterId, cluster: v0::Cluster| { - info!(target: LOG_TARGET, " Migrating cluster for cluster ID {:?}...", cluster_id); - translated.saturating_inc(); - let props = ClusterProps { - node_provider_auth_contract: cluster.props.node_provider_auth_contract, - erasure_coding_required: 16, - erasure_coding_total: 48, - replication_total: 20, - }; - - Some(Cluster { - cluster_id: cluster.cluster_id, - manager_id: cluster.manager_id, - reserve_id: cluster.reserve_id, - props, - }) - }, - ); - - // Update storage version. - StorageVersion::new(1).put::>(); - info!( - target: LOG_TARGET, - "Upgraded {} records, storage to version {:?}", - translated, - current_version - ); - - T::DbWeight::get().reads_writes(translated + 1, translated + 1) - } else { - info!(target: LOG_TARGET, " >>> Unused migration!"); - T::DbWeight::get().reads(1) - } -} -pub struct MigrateToV1(sp_std::marker::PhantomData); -impl OnRuntimeUpgrade for MigrateToV1 { - fn on_runtime_upgrade() -> Weight { - migrate_to_v1::() - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, DispatchError> { - let prev_count = v0::Clusters::::iter().count(); - - Ok((prev_count as u64).encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_state: Vec) -> Result<(), DispatchError> { - let prev_count: u64 = - Decode::decode(&mut &prev_state[..]).expect("pre_upgrade provides a valid state; qed"); - - let post_count = Clusters::::iter().count() as u64; - ensure!( - prev_count == post_count, - "the cluster count before and after the migration should be the same" - ); - - let current_version = Pallet::::current_storage_version(); - let on_chain_version = Pallet::::on_chain_storage_version(); - - frame_support::ensure!(current_version == 1, "must_upgrade"); - ensure!( - current_version == on_chain_version, - "after migration, the current_version and on_chain_version should be the same" - ); - Ok(()) - } -} - -#[cfg(test)] -#[cfg(feature = "try-runtime")] -mod test { - - use frame_support::pallet_prelude::StorageVersion; - - use super::*; - use crate::mock::{Test as T, *}; - - #[test] - fn cluster_migration_works() { - ExtBuilder.build_and_execute(|| { - let cluster_id0 = ClusterId::from([0; 20]); - let cluster_id1 = ClusterId::from([1; 20]); - let cluster_id2 = ClusterId::from([2; 20]); - let cluster_manager_id = AccountId::from([1; 32]); - let cluster_reserve_id = AccountId::from([2; 32]); - let auth_contract = AccountId::from([3; 32]); - - assert_eq!(StorageVersion::get::>(), 0); - - let cluster1 = v0::Cluster { - cluster_id: cluster_id1, - manager_id: cluster_manager_id.clone(), - reserve_id: cluster_reserve_id.clone(), - props: v0::ClusterProps { - node_provider_auth_contract: Some(auth_contract.clone()), - }, - }; - - v0::Clusters::::insert(cluster_id1, cluster1); - let cluster2 = v0::Cluster { - cluster_id: cluster_id2, - manager_id: cluster_manager_id, - reserve_id: cluster_reserve_id, - props: v0::ClusterProps { - node_provider_auth_contract: Some(auth_contract.clone()), - }, - }; - - v0::Clusters::::insert(cluster_id2, cluster2); - let cluster_count = v0::Clusters::::iter_values().count() as u32; - - assert_eq!(cluster_count, 3); - let state = MigrateToV1::::pre_upgrade().unwrap(); - let _weight = MigrateToV1::::on_runtime_upgrade(); - MigrateToV1::::post_upgrade(state).unwrap(); - - let cluster_count_after_upgrade = Clusters::::iter_values().count() as u32; - - assert_eq!(StorageVersion::get::>(), 1); - assert_eq!(cluster_count_after_upgrade, 3); - assert_eq!(Clusters::::get(cluster_id0).unwrap().props.erasure_coding_required, 16); - assert_eq!(Clusters::::get(cluster_id0).unwrap().props.erasure_coding_total, 48); - assert_eq!(Clusters::::get(cluster_id0).unwrap().props.replication_total, 20); - assert_eq!(Clusters::::get(cluster_id1).unwrap().props.erasure_coding_required, 16); - assert_eq!(Clusters::::get(cluster_id1).unwrap().props.erasure_coding_total, 48); - assert_eq!(Clusters::::get(cluster_id1).unwrap().props.replication_total, 20); - assert_eq!(Clusters::::get(cluster_id2).unwrap().props.erasure_coding_required, 16); - assert_eq!(Clusters::::get(cluster_id2).unwrap().props.erasure_coding_total, 48); - assert_eq!(Clusters::::get(cluster_id2).unwrap().props.replication_total, 20); - }); - } -} diff --git a/pallets/ddc-clusters/src/migrations.rs b/pallets/ddc-clusters/src/migrations.rs new file mode 100644 index 000000000..d0b6c2af0 --- /dev/null +++ b/pallets/ddc-clusters/src/migrations.rs @@ -0,0 +1,668 @@ +use frame_support::{storage_alias, traits::OnRuntimeUpgrade}; +use log::info; +use serde::{Deserialize, Serialize}; +use sp_runtime::Saturating; + +use super::*; + +const LOG_TARGET: &str = "ddc-clusters"; + +pub mod v0 { + use frame_support::pallet_prelude::*; + + use super::*; + + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + pub struct Cluster { + pub cluster_id: ClusterId, + pub manager_id: AccountId, + pub reserve_id: AccountId, + pub props: ClusterProps, + } + + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + pub struct ClusterProps { + pub node_provider_auth_contract: Option, + } + + #[storage_alias] + pub(super) type Clusters = StorageMap< + crate::Pallet, + Blake2_128Concat, + ClusterId, + Cluster<::AccountId>, + >; +} + +pub mod v1 { + use frame_support::pallet_prelude::*; + + use super::*; + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct Cluster { + pub cluster_id: ClusterId, + pub manager_id: AccountId, + pub reserve_id: AccountId, + pub props: ClusterProps, + } + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct ClusterProps { + pub node_provider_auth_contract: Option, + pub erasure_coding_required: u32, // new field + pub erasure_coding_total: u32, // new field + pub replication_total: u32, // new field + } + + #[storage_alias] + pub(super) type Clusters = StorageMap< + crate::Pallet, + Blake2_128Concat, + ClusterId, + Cluster<::AccountId>, + >; + + pub fn migrate_to_v1() -> Weight { + let on_chain_version = Pallet::::on_chain_storage_version(); + let current_version = Pallet::::current_storage_version(); + + info!( + target: LOG_TARGET, + "Running migration with current storage version {:?} / onchain {:?}", + current_version, + on_chain_version + ); + + if on_chain_version == 0 && current_version == 1 { + let mut translated = 0u64; + let count = v0::Clusters::::iter().count(); + info!( + target: LOG_TARGET, + " >>> Updating DDC Cluster storage. Migrating {} clusters...", count + ); + + v1::Clusters::::translate::, _>( + |cluster_id: ClusterId, cluster: v0::Cluster| { + info!(target: LOG_TARGET, " Migrating cluster for cluster ID {:?}...", cluster_id); + translated.saturating_inc(); + let props = ClusterProps { + node_provider_auth_contract: cluster.props.node_provider_auth_contract, + erasure_coding_required: 16, + erasure_coding_total: 48, + replication_total: 20, + }; + + Some(v1::Cluster { + cluster_id: cluster.cluster_id, + manager_id: cluster.manager_id, + reserve_id: cluster.reserve_id, + props, + }) + }, + ); + + // Update storage version. + StorageVersion::new(1).put::>(); + info!( + target: LOG_TARGET, + "Upgraded {} records, storage to version {:?}", + translated, + current_version + ); + + T::DbWeight::get().reads_writes(translated + 1, translated + 1) + } else { + info!(target: LOG_TARGET, " >>> Unused migration!"); + T::DbWeight::get().reads(1) + } + } + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v1::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + let prev_count = v0::Clusters::::iter().count(); + + Ok((prev_count as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_state: Vec) -> Result<(), DispatchError> { + let prev_count: u64 = Decode::decode(&mut &prev_state[..]) + .expect("pre_upgrade provides a valid state; qed"); + + let post_count = Clusters::::iter().count() as u64; + ensure!( + prev_count == post_count, + "the cluster count before and after the migration should be the same" + ); + + let current_version = Pallet::::current_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + + frame_support::ensure!(current_version == 1, "must_upgrade"); + ensure!( + current_version == on_chain_version, + "after migration, the current_version and on_chain_version should be the same" + ); + Ok(()) + } + } + + #[cfg(test)] + #[cfg(feature = "try-runtime")] + mod test { + + use frame_support::pallet_prelude::StorageVersion; + + use super::*; + use crate::mock::{Test as T, *}; + + #[test] + fn cluster_migration_works() { + ExtBuilder.build_and_execute(|| { + let cluster_id0 = ClusterId::from([0; 20]); + let cluster_id1 = ClusterId::from([1; 20]); + let cluster_id2 = ClusterId::from([2; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + + assert_eq!(StorageVersion::get::>(), 0); + + let cluster1 = v0::Cluster { + cluster_id: cluster_id1, + manager_id: cluster_manager_id.clone(), + reserve_id: cluster_reserve_id.clone(), + props: v0::ClusterProps { + node_provider_auth_contract: Some(auth_contract.clone()), + }, + }; + + v0::Clusters::::insert(cluster_id1, cluster1); + let cluster2 = v0::Cluster { + cluster_id: cluster_id2, + manager_id: cluster_manager_id, + reserve_id: cluster_reserve_id, + props: v0::ClusterProps { + node_provider_auth_contract: Some(auth_contract.clone()), + }, + }; + + v0::Clusters::::insert(cluster_id2, cluster2); + let cluster_count = v0::Clusters::::iter_values().count() as u32; + + assert_eq!(cluster_count, 3); + let state = MigrateToV1::::pre_upgrade().unwrap(); + let _weight = MigrateToV1::::on_runtime_upgrade(); + MigrateToV1::::post_upgrade(state).unwrap(); + + let cluster_count_after_upgrade = Clusters::::iter_values().count() as u32; + + assert_eq!(StorageVersion::get::>(), 1); + assert_eq!(cluster_count_after_upgrade, 3); + assert_eq!( + Clusters::::get(cluster_id0).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id0).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id0).unwrap().props.replication_total, 20); + assert_eq!( + Clusters::::get(cluster_id1).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id1).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id1).unwrap().props.replication_total, 20); + assert_eq!( + Clusters::::get(cluster_id2).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id2).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id2).unwrap().props.replication_total, 20); + }); + } + } +} + +pub mod v2 { + use ddc_primitives::{ + ClusterId, ClusterNodeKind, ClusterNodeState, ClusterNodeStatus, ClusterNodesCount, + ClusterNodesStats, ClusterStatus, + }; + use frame_support::{pallet_prelude::*, traits::OnRuntimeUpgrade, weights::Weight}; + use sp_runtime::Saturating; + use sp_std::collections::btree_map::BTreeMap; + #[cfg(feature = "try-runtime")] + use sp_std::vec::Vec; + + use super::*; + use crate::{ClustersNodes, ClustersNodesStats, Config, Pallet, LOG_TARGET}; + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct Cluster { + pub cluster_id: ClusterId, + pub manager_id: AccountId, + pub reserve_id: AccountId, + pub props: ClusterProps, + pub status: ClusterStatus, // new field + } + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct ClusterProps { + pub node_provider_auth_contract: Option, + pub erasure_coding_required: u32, + pub erasure_coding_total: u32, + pub replication_total: u32, + } + + #[storage_alias] + pub(super) type Clusters = StorageMap< + crate::Pallet, + Blake2_128Concat, + ClusterId, + Cluster<::AccountId>, + >; + + pub type OldNodeStatus = bool; + + pub fn migrate_to_v2() -> Weight { + let current_version = Pallet::::current_storage_version(); + let onchain_version = Pallet::::on_chain_storage_version(); + let mut weight = T::DbWeight::get().reads(1); + + if onchain_version == 1 && current_version == 2 { + let mut nodes_count_by_cluster: BTreeMap = + BTreeMap::new(); + + let mut translated_clusters = 0u64; + v2::Clusters::::translate::, _>( + |cluster_id, old_cluster| { + translated_clusters.saturating_inc(); + nodes_count_by_cluster.insert(cluster_id, 0); + + // all clusters are unbonded by default + let status = ClusterStatus::Unbonded; + let new_cluster = Cluster { + cluster_id: old_cluster.cluster_id, + manager_id: old_cluster.manager_id, + reserve_id: old_cluster.reserve_id, + props: ClusterProps { + node_provider_auth_contract: old_cluster + .props + .node_provider_auth_contract, + erasure_coding_required: old_cluster.props.erasure_coding_required, + erasure_coding_total: old_cluster.props.erasure_coding_total, + replication_total: old_cluster.props.replication_total, + }, + status, + }; + + Some(new_cluster) + }, + ); + weight.saturating_accrue( + T::DbWeight::get().reads_writes(translated_clusters, translated_clusters), + ); + + current_version.put::>(); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + log::info!( + target: LOG_TARGET, + "Upgraded {} clusters, storage to version {:?}", + translated_clusters, + current_version + ); + + let mut translated_clusters_nodes = 0u64; + ClustersNodes::::translate::( + |cluster_id, _node_pub_key, _old_value| { + translated_clusters_nodes.saturating_inc(); + + nodes_count_by_cluster + .entry(cluster_id) + .and_modify(|count| *count = count.saturating_add(1)) // If exists, update + .or_insert(1); // If not, insert with a count of 1 + + Some(ClusterNodeState { + kind: ClusterNodeKind::External, + status: ClusterNodeStatus::ValidationSucceeded, + added_at: >::block_number(), + }) + }, + ); + weight.saturating_accrue( + T::DbWeight::get() + .reads_writes(translated_clusters_nodes, translated_clusters_nodes), + ); + log::info!( + target: LOG_TARGET, + "Upgraded {} clusters nodes statuses, storage to version {:?}", + translated_clusters_nodes, + current_version + ); + + for (cluster_id, nodes_count) in nodes_count_by_cluster.iter() { + ClustersNodesStats::::insert( + cluster_id, + ClusterNodesStats { + await_validation: 0, + validation_succeeded: *nodes_count, + validation_failed: 0, + }, + ); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + log::info!( + target: LOG_TARGET, + "Upgraded {} clusters statistics, storage to version {:?}", + nodes_count_by_cluster.len(), + current_version + ); + + // Update storage version. + StorageVersion::new(2).put::>(); + + weight + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); + + weight + } + } + + pub struct MigrateToV2(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV2 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v2::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + frame_support::ensure!( + Pallet::::on_chain_storage_version() == 1, + "must upgrade linearly" + ); + let pre_clusters_count = Clusters::::iter_keys().count(); + let pre_clusters_nodes_count = ClustersNodes::::iter_keys().count(); + let pre_clusters_nodes_stats_count = ClustersNodesStats::::iter_keys().count(); + + assert_eq!( + pre_clusters_nodes_stats_count, 0, + "clusters statistics should be empty before the migration" + ); + + Ok((pre_clusters_count as u32, pre_clusters_nodes_count as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), DispatchError> { + let (pre_clusters_count, pre_clusters_nodes_count): (u32, u32) = Decode::decode( + &mut &state[..], + ) + .expect("the state parameter should be something that was generated by pre_upgrade"); + + let post_clusters_count = Clusters::::iter().count() as u32; + assert_eq!( + pre_clusters_count, post_clusters_count, + "the clusters count before (pre: {}) and after (post: {}) the migration should be the same", + pre_clusters_count, post_clusters_count + ); + let post_clusters_nodes_count = ClustersNodes::::iter().count() as u32; + assert_eq!( + pre_clusters_nodes_count, post_clusters_nodes_count, + "the clusters nodes count before (pre: {}) and after (post: {}) the migration should be the same", + pre_clusters_nodes_count, post_clusters_nodes_count + ); + + let post_clusters_nodes_stats_count = ClustersNodesStats::::iter().count() as u32; + assert_eq!( + post_clusters_nodes_stats_count, post_clusters_count, + "the clusters statistics ({}) should be equal to clusters count ({}) after the migration", + post_clusters_nodes_stats_count, post_clusters_count + ); + + let current_version = Pallet::::current_storage_version(); + let onchain_version = Pallet::::on_chain_storage_version(); + + frame_support::ensure!(current_version == 2, "must_upgrade"); + assert_eq!( + current_version, onchain_version, + "after migration, the current_version and onchain_version should be the same" + ); + + Clusters::::iter().for_each(|(_id, cluster)| { + assert!( + cluster.status == ClusterStatus::Unbonded, + "cluster status should only be 'Unbonded'." + ) + }); + Ok(()) + } + } +} + +pub mod v3 { + use frame_support::{ + pallet_prelude::*, + traits::{Get, OnRuntimeUpgrade}, + weights::Weight, + }; + use sp_std::marker::PhantomData; + #[cfg(feature = "try-runtime")] + use sp_std::vec::Vec; + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct Cluster { + pub cluster_id: ClusterId, + pub manager_id: AccountId, + pub reserve_id: AccountId, + pub props: ClusterProps, + pub status: ClusterStatus, + pub last_validated_era_id: DdcEra, // new field + } + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + pub struct ClusterProps { + pub node_provider_auth_contract: Option, + pub erasure_coding_required: u32, + pub erasure_coding_total: u32, + pub replication_total: u32, + } + + #[storage_alias] + pub(super) type Clusters = StorageMap< + crate::Pallet, + Blake2_128Concat, + ClusterId, + Cluster<::AccountId>, + >; + + use super::*; + pub fn migrate_to_v3() -> Weight { + let on_chain_version = Pallet::::on_chain_storage_version(); + let current_version = Pallet::::current_storage_version(); + + info!( + target: LOG_TARGET, + "Running migration with current storage version {:?} / onchain {:?}", + current_version, + on_chain_version + ); + + if on_chain_version == 2 && current_version == 3 { + let mut translated = 0u64; + let count = v2::Clusters::::iter().count(); + info!( + target: LOG_TARGET, + " >>> Updating DDC Cluster storage. Migrating {} clusters...", count + ); + + v3::Clusters::::translate::, _>( + |cluster_id: ClusterId, old_cluster: v2::Cluster| { + info!(target: LOG_TARGET, " Migrating cluster for cluster ID {:?}...", cluster_id); + translated.saturating_inc(); + Some(Cluster { + cluster_id: old_cluster.cluster_id, + manager_id: old_cluster.manager_id, + reserve_id: old_cluster.reserve_id, + props: ClusterProps { + node_provider_auth_contract: old_cluster + .props + .node_provider_auth_contract, + erasure_coding_required: old_cluster.props.erasure_coding_required, + erasure_coding_total: old_cluster.props.erasure_coding_total, + replication_total: old_cluster.props.replication_total, + }, + status: old_cluster.status, + last_validated_era_id: 0, + }) + }, + ); + + // Update storage version. + StorageVersion::new(3).put::>(); + info!( + target: LOG_TARGET, + "Upgraded {} records, storage to version {:?}", + count, + current_version + ); + + T::DbWeight::get().reads_writes(translated + 1, translated + 1) + } else { + info!(target: LOG_TARGET, " >>> Unused migration!"); + T::DbWeight::get().reads(1) + } + } + + pub struct MigrateToV3(PhantomData); + + impl OnRuntimeUpgrade for MigrateToV3 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v3::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + let prev_count = v2::Clusters::::iter().count(); + + Ok((prev_count as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_state: Vec) -> Result<(), sp_runtime::DispatchError> { + let prev_count: u64 = Decode::decode(&mut &prev_state[..]) + .expect("pre_upgrade provides a valid state; qed"); + + let post_count = v3::Clusters::::iter().count() as u64; + ensure!( + prev_count == post_count, + "the cluster count before and after the migration should be the same" + ); + + let current_version = Pallet::::current_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + + ensure!(current_version == 3, "must_upgrade"); + ensure!( + current_version == on_chain_version, + "after migration, the current_version and on_chain_version should be the same" + ); + Ok(()) + } + } + + #[cfg(test)] + #[cfg(feature = "try-runtime")] + mod test { + use ddc_primitives::ClusterStatus; + use frame_support::pallet_prelude::StorageVersion; + + use super::*; + use crate::mock::{Test as T, *}; + + #[test] + fn cluster_migration_works() { + ExtBuilder.build_and_execute(|| { + let cluster_id0 = ClusterId::from([0; 20]); + let cluster_id1 = ClusterId::from([1; 20]); + let cluster_id2 = ClusterId::from([2; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + + assert_eq!(StorageVersion::get::>(), 2); + + let cluster1 = Cluster { + cluster_id: cluster_id1, + manager_id: cluster_manager_id.clone(), + reserve_id: cluster_reserve_id.clone(), + props: ClusterProps { + node_provider_auth_contract: Some(auth_contract.clone()), + erasure_coding_required: 16, + erasure_coding_total: 48, + replication_total: 20, + }, + last_validated_era_id: 0, + status: ClusterStatus::Activated, + }; + + Clusters::::insert(cluster_id1, cluster1); + let cluster2 = Cluster { + cluster_id: cluster_id2, + manager_id: cluster_manager_id, + reserve_id: cluster_reserve_id, + props: ClusterProps { + node_provider_auth_contract: Some(auth_contract.clone()), + erasure_coding_required: 16, + erasure_coding_total: 48, + replication_total: 20, + }, + last_validated_era_id: 0, + status: ClusterStatus::Activated, + }; + + Clusters::::insert(cluster_id2, cluster2); + let cluster_count = Clusters::::iter_values().count() as u32; + + assert_eq!(cluster_count, 3); + let state = MigrateToV3::::pre_upgrade().unwrap(); + let _weight = MigrateToV3::::on_runtime_upgrade(); + MigrateToV3::::post_upgrade(state).unwrap(); + + let cluster_count_after_upgrade = Clusters::::iter_values().count() as u32; + + assert_eq!(StorageVersion::get::>(), 3); + assert_eq!(cluster_count_after_upgrade, 3); + assert_eq!( + Clusters::::get(cluster_id0).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id0).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id0).unwrap().props.replication_total, 20); + assert_eq!(Clusters::::get(cluster_id0).unwrap().last_validated_era_id, 0); + assert_eq!( + Clusters::::get(cluster_id1).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id1).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id1).unwrap().props.replication_total, 20); + assert_eq!(Clusters::::get(cluster_id1).unwrap().last_validated_era_id, 0); + assert_eq!( + Clusters::::get(cluster_id2).unwrap().props.erasure_coding_required, + 16 + ); + assert_eq!(Clusters::::get(cluster_id2).unwrap().props.erasure_coding_total, 48); + assert_eq!(Clusters::::get(cluster_id2).unwrap().props.replication_total, 20); + assert_eq!(Clusters::::get(cluster_id2).unwrap().last_validated_era_id, 0); + }); + } + } +} diff --git a/pallets/ddc-clusters/src/mock.rs b/pallets/ddc-clusters/src/mock.rs index b333afb9a..6a7a9a910 100644 --- a/pallets/ddc-clusters/src/mock.rs +++ b/pallets/ddc-clusters/src/mock.rs @@ -20,7 +20,7 @@ use sp_runtime::{ traits::{ BlakeTwo256, Convert, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify, }, - BuildStorage, MultiSignature, Perbill, Perquintill, + BuildStorage, DispatchResult, MultiSignature, Perbill, Perquintill, }; use crate::{self as pallet_ddc_clusters, *}; @@ -101,6 +101,7 @@ impl contracts::Config for Test { type Debug = (); type Environment = (); type Migrations = (); + type Xcm = (); } use frame_system::offchain::{ @@ -178,6 +179,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; @@ -226,6 +228,9 @@ impl StakingVisitor for TestStakingVisitor { fn has_chilling_attempt(_node_pub_key: &NodePubKey) -> Result { Ok(false) } + fn stash_by_ctrl(_controller: &T::AccountId) -> Result { + todo!() + } } impl StakerCreator> for TestStaker { @@ -235,7 +240,15 @@ impl StakerCreator> for TestStaker { _node: NodePubKey, _value: BalanceOf, _cluster_id: ClusterId, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { + Ok(()) + } + + fn bond_cluster( + _cluster_stash: T::AccountId, + _cluster_controller: T::AccountId, + _cluster_id: ClusterId, + ) -> DispatchResult { Ok(()) } } @@ -258,7 +271,7 @@ impl ExtBuilder { } .assimilate_storage(&mut t); - let cluster_gov_params = ClusterGovParams { + let cluster_protocol_params = ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -273,10 +286,7 @@ impl ExtBuilder { let node_pub_key = NodePubKey::StoragePubKey(AccountId::from([0; 32])); - // For testing purposes only - pallet_ddc_clusters::GenesisConfig::::default().build(); - - if let Ok(cluster) = Cluster::new( + let cluster = Cluster::new( ClusterId::from([0; 20]), AccountId::from([0; 32]), AccountId::from([0; 32]), @@ -286,14 +296,21 @@ impl ExtBuilder { erasure_coding_total: 6, replication_total: 3, }, - ) { - let _ = pallet_ddc_clusters::GenesisConfig:: { - clusters: vec![cluster], - clusters_gov_params: vec![(ClusterId::from([0; 20]), cluster_gov_params)], - clusters_nodes: vec![(ClusterId::from([0; 20]), vec![node_pub_key])], - } - .assimilate_storage(&mut t); + ); + + let _ = pallet_ddc_clusters::GenesisConfig:: { + clusters: vec![cluster], + clusters_protocol_params: vec![(ClusterId::from([0; 20]), cluster_protocol_params)], + clusters_nodes: vec![( + ClusterId::from([0; 20]), + vec![( + node_pub_key, + ClusterNodeKind::Genesis, + ClusterNodeStatus::ValidationSucceeded, + )], + )], } + .assimilate_storage(&mut t); TestExternalities::new(t) } diff --git a/pallets/ddc-clusters/src/node_provider_auth.rs b/pallets/ddc-clusters/src/node_provider_auth.rs index 1e9dee165..6f293e36f 100644 --- a/pallets/ddc-clusters/src/node_provider_auth.rs +++ b/pallets/ddc-clusters/src/node_provider_auth.rs @@ -63,7 +63,7 @@ where .result .map_err(|_| NodeProviderAuthContractError::ContractCallFailed)? .data - .first() + .get(1) .is_some_and(|x| *x == 1); Ok(is_authorized) diff --git a/pallets/ddc-clusters/src/test_data/metadata.json b/pallets/ddc-clusters/src/test_data/metadata.json index 4ffe526d6..f05b952e2 100644 --- a/pallets/ddc-clusters/src/test_data/metadata.json +++ b/pallets/ddc-clusters/src/test_data/metadata.json @@ -1,8 +1,17 @@ { "source": { - "hash": "0x26d3338336a945f457e19992f37947659b0b7e4b2962369fe2f97ca7ebb95a09", - "language": "ink! 3.4.0", - "compiler": "rustc 1.69.0-nightly" + "hash": "0xa41834e9d696d32cbc6ba6e83d7e904ad494bf41016f41d9e0186979de6965c3", + "language": "ink! 4.3.0", + "compiler": "rustc 1.76.0", + "build_info": { + "build_mode": "Release", + "cargo_contract_version": "2.2.1", + "rust_toolchain": "stable-x86_64-unknown-linux-gnu", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } }, "contract": { "name": "node_provider_auth_white_list", @@ -13,353 +22,620 @@ "description": "Node provider authorization layer based on admin approval", "license": "Apache-2.0" }, - "V3": { - "spec": { - "constructors": [ - { - "args": [], - "docs": [], - "label": "new", - "payable": false, - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [], - "messages": [ - { - "args": [ - { - "label": "_node_provider", - "type": { - "displayName": [ - "AccountId" - ], - "type": 0 - } - }, - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } - }, - { - "label": "_node_variant", - "type": { - "displayName": [ - "NodeType" - ], - "type": 2 - } - } + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" ], - "docs": [], - "label": "is_authorized", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 5 - }, - "selector": "0x96b0453e" + "type": 4 }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 0 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 13 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 16 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 17 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 14 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 15 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 6 + }, + "messages": [ + { + "args": [ + { + "label": "_node_provider", + "type": { + "displayName": [ + "AccountId" + ], + "type": 0 } - ], - "docs": [], - "label": "add_node_pub_key", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 7 }, - "selector": "0x7a04093d" - }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } - ], - "docs": [], - "label": "remove_node_pub_key", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 7 }, - "selector": "0xed3668b6" - }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + { + "label": "_node_variant", + "type": { + "displayName": [ + "NodeType" + ], + "type": 2 } + } + ], + "default": false, + "docs": [], + "label": "is_authorized", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" ], - "docs": [], - "label": "has_node_pub_key", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 5 - }, - "selector": "0x9b868ecf" + "type": 8 }, - { - "args": [], - "docs": [], - "label": "get_admin", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "AccountId" - ], - "type": 0 - }, - "selector": "0x57b8a8a7" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 0 - } - }, - "name": "admin" - }, - { - "layout": { - "cell": { - "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 3 - } - }, - "name": "list" - } - ] - } - }, - "types": [ + "selector": "0x96b0453e" + }, { - "id": 0, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 1, - "typeName": "[u8; 32]" - } - ] + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - } + } + ], + "default": false, + "docs": [], + "label": "add_node_pub_key", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 9 + }, + "selector": "0x7a04093d" }, { - "id": 1, - "type": { - "def": { - "array": { - "len": 32, - "type": 2 + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } } - } + ], + "default": false, + "docs": [], + "label": "remove_node_pub_key", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 9 + }, + "selector": "0xed3668b6" }, { - "id": 2, - "type": { - "def": { - "primitive": "u8" + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 + } } - } + ], + "default": false, + "docs": [], + "label": "has_node_pub_key", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 8 + }, + "selector": "0x9b868ecf" }, { - "id": 3, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 6, - "typeName": "Key" - } - ] - } - }, - "params": [ + "args": [], + "default": false, + "docs": [], + "label": "get_admin", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 12 + }, + "selector": "0x57b8a8a7" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [ { - "name": "K", - "type": 4 + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 0 + } + }, + "name": "admin" }, { - "name": "V", - "type": 5 + "layout": { + "root": { + "layout": { + "leaf": { + "key": "0x8e4a1f5b", + "ty": 3 + } + }, + "root_key": "0x8e4a1f5b" + } + }, + "name": "list" } ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] + "name": "WhiteListAuthContract" } }, - { - "id": 4, - "type": { - "def": { - "sequence": { - "type": 2 - } + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 1, + "typeName": "[u8; 32]" + } + ] } - } - }, - { - "id": 5, - "type": { - "def": { - "primitive": "bool" + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "array": { + "len": 32, + "type": 2 } } - }, - { - "id": 6, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 1, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "Key" - ] + } + }, + { + "id": 2, + "type": { + "def": { + "primitive": "u8" } - }, - { - "id": 7, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 9 - } - ], - "index": 1, - "name": "Err" - } - ] - } + } + }, + { + "id": 3, + "type": { + "def": { + "primitive": "bool" + } + } + }, + { + "id": 4, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 5 }, - "params": [ - { - "name": "T", - "type": 8 - }, - { - "name": "E", - "type": 9 - } - ], - "path": [ - "Result" - ] + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "tuple": [] } - }, - { - "id": 8, - "type": { - "def": { - "tuple": [] + } + }, + { + "id": 6, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 7, + "type": { + "def": { + "sequence": { + "type": 2 } } - }, - { - "id": 9, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "OnlyAdmin" - } - ] - } + } + }, + { + "id": 8, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 3 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 3 }, - "path": [ - "node_provider_auth_white_list", - "node_provider_auth_white_list", - "Error" - ] + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 9, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 10 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 10 + }, + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 10, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 11 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 5 + }, + { + "name": "E", + "type": 11 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "OnlyAdmin" + } + ] + } + }, + "path": [ + "node_provider_auth_white_list", + "node_provider_auth_white_list", + "Error" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 0 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 0 + }, + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 13, + "type": { + "def": { + "primitive": "u128" } } - ] - } + }, + { + "id": 14, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 1, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 15, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 16, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 17, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" } \ No newline at end of file diff --git a/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm b/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm index 39d305f7f..5c3394495 100644 Binary files a/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm and b/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm differ diff --git a/pallets/ddc-clusters/src/testing_utils.rs b/pallets/ddc-clusters/src/testing_utils.rs index a09688a1f..3a2e6c738 100644 --- a/pallets/ddc-clusters/src/testing_utils.rs +++ b/pallets/ddc-clusters/src/testing_utils.rs @@ -1,7 +1,7 @@ //! DdcStaking pallet benchmarking. use ddc_primitives::{ - ClusterGovParams, ClusterId, ClusterParams, NodeParams, NodePubKey, StorageNodeMode, + ClusterId, ClusterParams, ClusterProtocolParams, NodeParams, NodePubKey, StorageNodeMode, StorageNodeParams, }; pub use frame_benchmarking::{ @@ -26,26 +26,26 @@ where erasure_coding_total: 6, replication_total: 3, }; - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { - treasury_share: Perquintill::default(), - validators_share: Perquintill::default(), - cluster_reserve_share: Perquintill::default(), - storage_bond_size: 100u32.into(), - storage_chill_delay: 50u32.into(), - storage_unbonding_delay: 50u32.into(), - unit_per_mb_stored: 10, - unit_per_mb_streamed: 10, - unit_per_put_request: 10, - unit_per_get_request: 10, - }; + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = + ClusterProtocolParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; let _ = DdcClusters::::create_cluster( - RawOrigin::Root.into(), + RawOrigin::Signed(user.clone()).into(), cluster_id, - user.clone(), user, cluster_params, - cluster_gov_params, + cluster_protocol_params, ); } @@ -73,26 +73,26 @@ where p2p_port: 15000u16, }; - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { - treasury_share: Perquintill::default(), - validators_share: Perquintill::default(), - cluster_reserve_share: Perquintill::default(), - storage_bond_size: 100u32.into(), - storage_chill_delay: 50u32.into(), - storage_unbonding_delay: 50u32.into(), - unit_per_mb_stored: 10, - unit_per_mb_streamed: 10, - unit_per_put_request: 10, - unit_per_get_request: 10, - }; + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = + ClusterProtocolParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; let _ = DdcClusters::::create_cluster( - RawOrigin::Root.into(), + RawOrigin::Signed(user.clone()).into(), cluster_id, user.clone(), - user.clone(), cluster_params, - cluster_gov_params, + cluster_protocol_params, ); if let Ok(new_node) = Node::::new( @@ -112,6 +112,10 @@ where ) .unwrap(); + as ClusterProtocol>>::bond_cluster(&cluster_id).unwrap(); + as ClusterProtocol>>::activate_cluster_protocol(&cluster_id) + .unwrap(); + let mut auth_contract = NodeProviderAuthContract::::new(user.clone(), user.clone()); auth_contract = auth_contract.deploy_contract(user.clone())?; auth_contract.authorize_node(node_pub_key)?; diff --git a/pallets/ddc-clusters/src/tests.rs b/pallets/ddc-clusters/src/tests.rs index 1f8b50956..56c9d8237 100644 --- a/pallets/ddc-clusters/src/tests.rs +++ b/pallets/ddc-clusters/src/tests.rs @@ -6,7 +6,7 @@ use ddc_primitives::{ ClusterParams, ClusterPricingParams, NodeParams, NodePubKey, StorageNodeMode, StorageNodeParams, }; -use frame_support::{assert_noop, assert_ok, error::BadOrigin}; +use frame_support::{assert_noop, assert_ok}; use frame_system::Config; use hex_literal::hex; use sp_runtime::{traits::Hash, Perquintill}; @@ -23,7 +23,7 @@ fn create_cluster_works() { let cluster_reserve_id = AccountId::from([2; 32]); let auth_contract = AccountId::from([3; 32]); - let cluster_gov_params = ClusterGovParams { + let cluster_protocol_params = ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -36,29 +36,10 @@ fn create_cluster_works() { unit_per_get_request: 10, }; - // Creating cluster not with root signature should fail - assert_noop!( - DdcClusters::create_cluster( - RuntimeOrigin::signed(AccountId::from([1; 32])), - cluster_id, - cluster_manager_id.clone(), - cluster_reserve_id.clone(), - ClusterParams { - node_provider_auth_contract: Some(auth_contract.clone()), - erasure_coding_required: 4, - erasure_coding_total: 6, - replication_total: 3 - }, - cluster_gov_params.clone() - ), - BadOrigin - ); - // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - cluster_manager_id.clone(), cluster_reserve_id.clone(), ClusterParams { node_provider_auth_contract: Some(auth_contract.clone()), @@ -66,7 +47,7 @@ fn create_cluster_works() { erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params.clone() + cluster_protocol_params.clone() )); let created_cluster = DdcClusters::clusters(cluster_id).unwrap(); @@ -75,51 +56,54 @@ fn create_cluster_works() { assert_eq!(created_cluster.reserve_id, cluster_reserve_id); assert_eq!(created_cluster.props.node_provider_auth_contract, Some(auth_contract.clone())); - let created_cluster_gov_params = DdcClusters::clusters_gov_params(cluster_id).unwrap(); - assert_eq!(created_cluster_gov_params.treasury_share, cluster_gov_params.treasury_share); + let created_cluster_protocol_params = + DdcClusters::clusters_protocol_params(cluster_id).unwrap(); assert_eq!( - created_cluster_gov_params.validators_share, - cluster_gov_params.validators_share + created_cluster_protocol_params.treasury_share, + cluster_protocol_params.treasury_share ); assert_eq!( - created_cluster_gov_params.cluster_reserve_share, - cluster_gov_params.cluster_reserve_share + created_cluster_protocol_params.validators_share, + cluster_protocol_params.validators_share ); assert_eq!( - created_cluster_gov_params.storage_bond_size, - cluster_gov_params.storage_bond_size + created_cluster_protocol_params.cluster_reserve_share, + cluster_protocol_params.cluster_reserve_share ); assert_eq!( - created_cluster_gov_params.storage_chill_delay, - cluster_gov_params.storage_chill_delay + created_cluster_protocol_params.storage_bond_size, + cluster_protocol_params.storage_bond_size ); assert_eq!( - created_cluster_gov_params.storage_unbonding_delay, - cluster_gov_params.storage_unbonding_delay + created_cluster_protocol_params.storage_chill_delay, + cluster_protocol_params.storage_chill_delay ); assert_eq!( - created_cluster_gov_params.unit_per_mb_stored, - cluster_gov_params.unit_per_mb_stored + created_cluster_protocol_params.storage_unbonding_delay, + cluster_protocol_params.storage_unbonding_delay ); assert_eq!( - created_cluster_gov_params.unit_per_mb_streamed, - cluster_gov_params.unit_per_mb_streamed + created_cluster_protocol_params.unit_per_mb_stored, + cluster_protocol_params.unit_per_mb_stored ); assert_eq!( - created_cluster_gov_params.unit_per_put_request, - cluster_gov_params.unit_per_put_request + created_cluster_protocol_params.unit_per_mb_streamed, + cluster_protocol_params.unit_per_mb_streamed ); assert_eq!( - created_cluster_gov_params.unit_per_get_request, - cluster_gov_params.unit_per_get_request + created_cluster_protocol_params.unit_per_put_request, + cluster_protocol_params.unit_per_put_request + ); + assert_eq!( + created_cluster_protocol_params.unit_per_get_request, + cluster_protocol_params.unit_per_get_request ); // Creating cluster with same id should fail assert_noop!( DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id), cluster_id, - cluster_manager_id, cluster_reserve_id, ClusterParams { node_provider_auth_contract: Some(auth_contract), @@ -127,7 +111,7 @@ fn create_cluster_works() { erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params + cluster_protocol_params ), Error::::ClusterAlreadyExists ); @@ -135,13 +119,14 @@ fn create_cluster_works() { // Checking storage assert!(Clusters::::contains_key(cluster_id)); // Checking that event was emitted - assert_eq!(System::events().len(), 1); - System::assert_last_event(Event::ClusterCreated { cluster_id }.into()) + assert_eq!(System::events().len(), 2); + System::assert_has_event(Event::ClusterCreated { cluster_id }.into()); + System::assert_last_event(Event::ClusterProtocolParamsSet { cluster_id }.into()); }) } #[test] -fn add_and_delete_node_works() { +fn add_join_and_delete_node_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); @@ -149,6 +134,7 @@ fn add_and_delete_node_works() { let cluster_manager_id = AccountId::from([1; 32]); let cluster_reserve_id = AccountId::from([2; 32]); let node_pub_key = AccountId::from([3; 32]); + let node_pub_key2 = AccountId::from([4; 32]); let contract_id = deploy_contract(); @@ -158,23 +144,31 @@ fn add_and_delete_node_works() { RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), + ClusterNodeKind::Genesis + ), + Error::::ClusterDoesNotExist + ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), ), Error::::ClusterDoesNotExist ); // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - cluster_manager_id.clone(), cluster_reserve_id.clone(), ClusterParams { - node_provider_auth_contract: Some(cluster_manager_id.clone()), + node_provider_auth_contract: None, erasure_coding_required: 4, erasure_coding_total: 6, replication_total: 3 }, - ClusterGovParams { + ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -188,12 +182,35 @@ fn add_and_delete_node_works() { } )); + // Cluster has no auth smart contract + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + ), + Error::::NodeIsNotAuthorized + ); + + // Set an incorrect address for auth contract + assert_ok!(DdcClusters::set_cluster_params( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + ClusterParams { + node_provider_auth_contract: Some(cluster_manager_id.clone()), + erasure_coding_required: 4, + erasure_coding_total: 6, + replication_total: 3 + }, + )); + // Not Cluster Manager assert_noop!( DdcClusters::add_node( RuntimeOrigin::signed(cluster_reserve_id), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), + ClusterNodeKind::Genesis ), Error::::OnlyClusterManager ); @@ -204,6 +221,15 @@ fn add_and_delete_node_works() { RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), + ClusterNodeKind::Genesis + ), + Error::::AttemptToAddNonExistentNode + ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), ), Error::::AttemptToAddNonExistentNode ); @@ -222,17 +248,17 @@ fn add_and_delete_node_works() { assert_ok!(DdcNodes::create_node( RuntimeOrigin::signed(cluster_manager_id.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), - NodeParams::StorageParams(storage_node_params) + NodeParams::StorageParams(storage_node_params.clone()), )); - // Node doesn't exist + // Not node provider assert_noop!( - DdcClusters::add_node( - RuntimeOrigin::signed(cluster_manager_id.clone()), + DdcClusters::join_cluster( + RuntimeOrigin::signed(node_pub_key.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), ), - Error::::NodeAuthContractCallFailed + Error::::OnlyNodeProvider ); // Set the correct address for auth contract @@ -247,16 +273,35 @@ fn add_and_delete_node_works() { }, )); - // Node added succesfully + assert_ok!(DdcClusters::bond_cluster(&cluster_id)); + + // Node is not authorized to join + assert_ok!(DdcNodes::create_node( + RuntimeOrigin::signed(cluster_manager_id.clone()), + NodePubKey::StoragePubKey(node_pub_key2.clone()), + NodeParams::StorageParams(storage_node_params.clone()), + )); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key2.clone()), + ), + Error::::NodeIsNotAuthorized + ); + + // Node added successfully assert_ok!(DdcClusters::add_node( RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), + ClusterNodeKind::Genesis )); assert!(>::contains_node( &cluster_id, - &NodePubKey::StoragePubKey(node_pub_key.clone()) + &NodePubKey::StoragePubKey(node_pub_key.clone()), + None )); // Node already assigned @@ -265,6 +310,15 @@ fn add_and_delete_node_works() { RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), + ClusterNodeKind::Genesis + ), + Error::::AttemptToAddAlreadyAssignedNode + ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), ), Error::::AttemptToAddAlreadyAssignedNode ); @@ -297,13 +351,35 @@ fn add_and_delete_node_works() { // Remove node should fail assert_noop!( DdcClusters::remove_node( - RuntimeOrigin::signed(cluster_manager_id), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - NodePubKey::StoragePubKey(node_pub_key), + NodePubKey::StoragePubKey(node_pub_key.clone()), ), Error::::AttemptToRemoveNotAssignedNode ); + // Node joined successfully + assert_ok!(DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + )); + + assert!(>::contains_node( + &cluster_id, + &NodePubKey::StoragePubKey(node_pub_key.clone()), + None + )); + + // Checking that event was emitted + System::assert_last_event( + Event::ClusterNodeAdded { + cluster_id, + node_pub_key: NodePubKey::StoragePubKey(node_pub_key.clone()), + } + .into(), + ); + pub const CTOR_SELECTOR: [u8; 4] = hex!("9bae9d5e"); fn encode_constructor() -> Vec { @@ -399,9 +475,8 @@ fn set_cluster_params_works() { // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - cluster_manager_id.clone(), cluster_reserve_id.clone(), ClusterParams { node_provider_auth_contract: Some(auth_contract_1), @@ -409,7 +484,7 @@ fn set_cluster_params_works() { erasure_coding_total: 6, replication_total: 3 }, - ClusterGovParams { + ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -497,128 +572,61 @@ fn set_cluster_params_works() { assert_eq!(updated_cluster.props.replication_total, 3); // Checking that event was emitted - assert_eq!(System::events().len(), 2); + assert_eq!(System::events().len(), 3); System::assert_last_event(Event::ClusterParamsSet { cluster_id }.into()) }) } #[test] -fn set_cluster_gov_params_works() { +fn set_last_validated_era_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); let cluster_id = ClusterId::from([1; 20]); let cluster_manager_id = AccountId::from([1; 32]); let cluster_reserve_id = AccountId::from([2; 32]); - let auth_contract = AccountId::from([3; 32]); - - let cluster_gov_params = ClusterGovParams { - treasury_share: Perquintill::from_float(0.05), - validators_share: Perquintill::from_float(0.01), - cluster_reserve_share: Perquintill::from_float(0.02), - storage_bond_size: 100, - storage_chill_delay: 50, - storage_unbonding_delay: 50, - unit_per_mb_stored: 10, - unit_per_mb_streamed: 10, - unit_per_put_request: 10, - unit_per_get_request: 10, - }; + let auth_contract_1 = AccountId::from([3; 32]); + let era_id: DdcEra = 22; // Cluster doesn't exist assert_noop!( - DdcClusters::set_cluster_gov_params( - RuntimeOrigin::root(), - cluster_id, - cluster_gov_params.clone() - ), + DdcClusters::set_last_validated_era(&cluster_id, era_id), Error::::ClusterDoesNotExist ); + // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - cluster_manager_id.clone(), - cluster_reserve_id, + cluster_reserve_id.clone(), ClusterParams { - node_provider_auth_contract: Some(auth_contract), + node_provider_auth_contract: Some(auth_contract_1), erasure_coding_required: 4, erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params.clone() + ClusterProtocolParams { + treasury_share: Perquintill::from_float(0.05), + validators_share: Perquintill::from_float(0.01), + cluster_reserve_share: Perquintill::from_float(0.02), + storage_bond_size: 100, + storage_chill_delay: 50, + storage_unbonding_delay: 50, + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + } )); - assert_noop!( - DdcClusters::set_cluster_gov_params( - RuntimeOrigin::signed(cluster_manager_id), - cluster_id, - cluster_gov_params - ), - BadOrigin - ); - - let updated_gov_params = ClusterGovParams { - treasury_share: Perquintill::from_float(0.06), - validators_share: Perquintill::from_float(0.02), - cluster_reserve_share: Perquintill::from_float(0.03), - storage_bond_size: 1000, - storage_chill_delay: 500, - storage_unbonding_delay: 500, - unit_per_mb_stored: 100, - unit_per_mb_streamed: 100, - unit_per_put_request: 100, - unit_per_get_request: 100, - }; + assert_ok!(DdcClusters::set_last_validated_era(&cluster_id, era_id)); - assert_ok!(DdcClusters::set_cluster_gov_params( - RuntimeOrigin::root(), - cluster_id, - updated_gov_params.clone() - )); - - let updated_cluster_gov_params = DdcClusters::clusters_gov_params(cluster_id).unwrap(); - assert_eq!(updated_cluster_gov_params.treasury_share, updated_gov_params.treasury_share); - assert_eq!( - updated_cluster_gov_params.validators_share, - updated_gov_params.validators_share - ); - assert_eq!( - updated_cluster_gov_params.cluster_reserve_share, - updated_gov_params.cluster_reserve_share - ); - assert_eq!( - updated_cluster_gov_params.storage_bond_size, - updated_gov_params.storage_bond_size - ); - assert_eq!( - updated_cluster_gov_params.storage_chill_delay, - updated_gov_params.storage_chill_delay - ); - assert_eq!( - updated_cluster_gov_params.storage_unbonding_delay, - updated_gov_params.storage_unbonding_delay - ); - assert_eq!( - updated_cluster_gov_params.unit_per_mb_stored, - updated_gov_params.unit_per_mb_stored - ); - assert_eq!( - updated_cluster_gov_params.unit_per_mb_streamed, - updated_gov_params.unit_per_mb_streamed - ); - assert_eq!( - updated_cluster_gov_params.unit_per_put_request, - updated_gov_params.unit_per_put_request - ); - assert_eq!( - updated_cluster_gov_params.unit_per_get_request, - updated_gov_params.unit_per_get_request - ); + let updated_cluster = DdcClusters::clusters(cluster_id).unwrap(); + assert_eq!(updated_cluster.last_validated_era_id, era_id); // Checking that event was emitted - assert_eq!(System::events().len(), 2); - System::assert_last_event(Event::ClusterGovParamsSet { cluster_id }.into()) + assert_eq!(System::events().len(), 3); + System::assert_last_event(Event::ClusterEraValidated { cluster_id, era_id }.into()) }) } @@ -632,7 +640,7 @@ fn cluster_visitor_works() { let cluster_reserve_id = AccountId::from([2; 32]); let auth_contract = AccountId::from([3; 32]); - let cluster_gov_params = ClusterGovParams { + let cluster_protocol_params = ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -647,9 +655,8 @@ fn cluster_visitor_works() { // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( - RuntimeOrigin::root(), + RuntimeOrigin::signed(cluster_manager_id), cluster_id, - cluster_manager_id, cluster_reserve_id.clone(), ClusterParams { node_provider_auth_contract: Some(auth_contract), @@ -657,24 +664,33 @@ fn cluster_visitor_works() { erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params + cluster_protocol_params )); - assert_ok!(>::ensure_cluster(&cluster_id)); + assert!(>::cluster_exists(&cluster_id)); assert_eq!( - >::get_bond_size(&cluster_id, NodeType::Storage) - .unwrap(), + >>::get_bond_size( + &cluster_id, + NodeType::Storage + ) + .unwrap(), 100u128 ); assert_eq!( - >::get_bond_size(&cluster_id, NodeType::Storage) - .unwrap(), + >>::get_bond_size( + &cluster_id, + NodeType::Storage + ) + .unwrap(), 100u128 ); assert_eq!( - >::get_pricing_params(&cluster_id).unwrap(), + >>::get_pricing_params( + &cluster_id + ) + .unwrap(), ClusterPricingParams { unit_per_mb_stored: 10, unit_per_mb_streamed: 10, @@ -684,7 +700,8 @@ fn cluster_visitor_works() { ); assert_eq!( - >::get_fees_params(&cluster_id).unwrap(), + >>::get_fees_params(&cluster_id) + .unwrap(), ClusterFeesParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), @@ -693,23 +710,32 @@ fn cluster_visitor_works() { ); assert_eq!( - >::get_reserve_account_id(&cluster_id).unwrap(), + >>::get_reserve_account_id( + &cluster_id + ) + .unwrap(), cluster_reserve_id ); assert_eq!( - >::get_chill_delay(&cluster_id, NodeType::Storage) - .unwrap(), + >>::get_chill_delay( + &cluster_id, + NodeType::Storage + ) + .unwrap(), 50 ); assert_eq!( - >::get_chill_delay(&cluster_id, NodeType::Storage) - .unwrap(), + >>::get_chill_delay( + &cluster_id, + NodeType::Storage + ) + .unwrap(), 50 ); assert_eq!( - >::get_unbonding_delay( + >>::get_unbonding_delay( &cluster_id, NodeType::Storage ) @@ -717,7 +743,7 @@ fn cluster_visitor_works() { 50 ); assert_eq!( - >::get_unbonding_delay( + >>::get_unbonding_delay( &cluster_id, NodeType::Storage ) @@ -726,7 +752,10 @@ fn cluster_visitor_works() { ); assert_eq!( - >::get_bonding_params(&cluster_id).unwrap(), + >>::get_bonding_params( + &cluster_id + ) + .unwrap(), ClusterBondingParams::> { storage_bond_size: 100, storage_chill_delay: 50, @@ -746,7 +775,7 @@ fn cluster_creator_works() { let cluster_reserve_id = AccountId::from([2; 32]); let auth_contract = AccountId::from([3; 32]); - let cluster_gov_params = ClusterGovParams { + let cluster_protocol_params = ClusterProtocolParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), cluster_reserve_share: Perquintill::from_float(0.02), @@ -759,7 +788,7 @@ fn cluster_creator_works() { unit_per_get_request: 10, }; - assert_ok!(>>::create_new_cluster( + assert_ok!(>>::create_cluster( cluster_id, cluster_manager_id, cluster_reserve_id, @@ -769,7 +798,7 @@ fn cluster_creator_works() { erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params + cluster_protocol_params, )); assert!(Clusters::::contains_key(cluster_id)); diff --git a/pallets/ddc-clusters/src/weights.rs b/pallets/ddc-clusters/src/weights.rs index a7f94f2e8..83ce438dc 100644 --- a/pallets/ddc-clusters/src/weights.rs +++ b/pallets/ddc-clusters/src/weights.rs @@ -1,9 +1,9 @@ //! Autogenerated weights for pallet_ddc_clusters //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `Yahors-MacBook-Pro.local`, CPU: `` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2024-10-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/cere @@ -11,12 +11,13 @@ // pallet // --chain=dev // --execution=wasm -// --pallet=pallet-ddc-clusters +// --wasm-execution=compiled +// --pallet=pallet_ddc_clusters // --extrinsic=* // --steps=50 // --repeat=20 // --template=./.maintain/frame-weight-template.hbs -// --output=pallets/ddc-clusters/src/weights.rs +// --output=pallets/ddc-clusters/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -29,110 +30,221 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create_cluster() -> Weight; fn add_node() -> Weight; + fn join_cluster() -> Weight; fn remove_node() -> Weight; fn set_cluster_params() -> Weight; - fn set_cluster_gov_params() -> Weight; + fn validate_node() -> Weight; } /// Weights for pallet_ddc_clusters using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: DdcClusters Clusters (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:0 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:0 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_cluster() -> Weight { - Weight::from_parts(15_000_000_u64, 0) + Weight::from_parts(35_617_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcStaking Nodes (r:1 w:0) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking Bonded (r:1 w:0) - // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: DdcClusters ClustersNodes (r:0 w:1) - // Storage: unknown [0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3] (r:1 w:0) - // Storage: unknown [0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957] (r:1 w:0) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_node() -> Weight { - Weight::from_parts(599_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(14_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + Weight::from_parts(82_055_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Contracts::MigrationInProgress` (r:1 w:0) + // Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:1 w:0) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Contracts::ContractInfoOf` (r:1 w:1) + // Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) + // Storage: `Contracts::CodeInfoOf` (r:1 w:0) + // Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + // Storage: `Contracts::PristineCode` (r:1 w:0) + // Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `MaxEncodedLen`) + // Storage: `Timestamp::Now` (r:1 w:0) + // Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + // Storage: `System::EventTopics` (r:2 w:2) + // Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Storage: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + fn join_cluster() -> Weight { + Weight::from_parts(656_290_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(17_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: DdcClusters ClustersNodes (r:0 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_node() -> Weight { - Weight::from_parts(24_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + Weight::from_parts(55_664_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_cluster_params() -> Weight { - Weight::from_parts(16_000_000_u64, 0) + Weight::from_parts(23_734_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcClusters ClustersGovParams (r:0 w:1) - fn set_cluster_gov_params() -> Weight { - Weight::from_parts(17_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn validate_node() -> Weight { + Weight::from_parts(42_951_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: DdcClusters Clusters (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:0 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:0 w:1) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_cluster() -> Weight { - Weight::from_parts(15_000_000_u64, 0) + Weight::from_parts(35_617_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcStaking Nodes (r:1 w:0) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking Bonded (r:1 w:0) - // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: System Account (r:1 w:0) - // Storage: Contracts ContractInfoOf (r:1 w:1) - // Storage: Contracts CodeStorage (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: System EventTopics (r:2 w:2) - // Storage: DdcClusters ClustersNodes (r:0 w:1) - // Storage: unknown [0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3] (r:1 w:0) - // Storage: unknown [0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957] (r:1 w:0) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_node() -> Weight { - Weight::from_parts(599_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(14_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + Weight::from_parts(82_055_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: DdcClusters ClustersNodes (r:0 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Contracts::MigrationInProgress` (r:1 w:0) + // Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) + // Storage: `System::Account` (r:1 w:0) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `Contracts::ContractInfoOf` (r:1 w:1) + // Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) + // Storage: `Contracts::CodeInfoOf` (r:1 w:0) + // Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + // Storage: `Contracts::PristineCode` (r:1 w:0) + // Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `MaxEncodedLen`) + // Storage: `Timestamp::Now` (r:1 w:0) + // Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + // Storage: `System::EventTopics` (r:2 w:2) + // Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Storage: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + fn join_cluster() -> Weight { + Weight::from_parts(656_290_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(17_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_node() -> Weight { - Weight::from_parts(24_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + Weight::from_parts(55_664_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:1) + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_cluster_params() -> Weight { - Weight::from_parts(16_000_000_u64, 0) + Weight::from_parts(23_734_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcClusters ClustersGovParams (r:0 w:1) - fn set_cluster_gov_params() -> Weight { - Weight::from_parts(17_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn validate_node() -> Weight { + Weight::from_parts(42_951_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } -} \ No newline at end of file +} diff --git a/pallets/ddc-customers/src/benchmarking.rs b/pallets/ddc-customers/src/benchmarking.rs index 60ec90c82..04b1b1525 100644 --- a/pallets/ddc-customers/src/benchmarking.rs +++ b/pallets/ddc-customers/src/benchmarking.rs @@ -1,7 +1,7 @@ //! DdcStaking pallet benchmarking. #![cfg(feature = "runtime-benchmarks")] -use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams}; +use ddc_primitives::{ClusterId, ClusterParams, ClusterProtocolParams}; use frame_benchmarking::{account, benchmarks, whitelist_account}; use frame_support::traits::Currency; use sp_runtime::Perquintill; @@ -21,7 +21,7 @@ benchmarks! { create_bucket { let cluster_id = ClusterId::from([1; 20]); let user = account::("user", USER_SEED, 0u32); - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = ClusterProtocolParams { treasury_share: Perquintill::default(), validators_share: Perquintill::default(), cluster_reserve_share: Perquintill::default(), @@ -34,7 +34,7 @@ benchmarks! { unit_per_get_request: 10, }; - let _ = ::ClusterCreator::create_new_cluster( + let _ = ::ClusterCreator::create_cluster( ClusterId::from([1; 20]), user.clone(), user.clone(), @@ -44,7 +44,7 @@ benchmarks! { erasure_coding_total: 6, replication_total: 3 }, - cluster_gov_params + cluster_protocol_params ); let bucket_params = BucketParams { @@ -162,6 +162,7 @@ benchmarks! { cluster_id, is_public: false, is_removed: false, + total_customers_usage: None, }; >::set(bucket_id); @@ -190,6 +191,7 @@ benchmarks! { cluster_id, is_public: false, is_removed: false, + total_customers_usage: None, }; >::set(bucket_id); diff --git a/pallets/ddc-customers/src/lib.rs b/pallets/ddc-customers/src/lib.rs index 41b15a364..c2531a3e9 100644 --- a/pallets/ddc-customers/src/lib.rs +++ b/pallets/ddc-customers/src/lib.rs @@ -15,10 +15,11 @@ mod tests; use codec::{Decode, Encode}; use ddc_primitives::{ traits::{ - cluster::{ClusterCreator, ClusterVisitor}, - customer::{CustomerCharger, CustomerDepositor}, + bucket::{BucketManager, BucketVisitor}, + cluster::{ClusterCreator, ClusterProtocol, ClusterQuery}, + customer::{CustomerCharger, CustomerDepositor, CustomerVisitor}, }, - BucketId, ClusterId, + BucketId, BucketVisitorError, ClusterId, CustomerUsage, }; use frame_support::{ parameter_types, @@ -66,6 +67,7 @@ pub struct Bucket { cluster_id: ClusterId, is_public: bool, is_removed: bool, + total_customers_usage: Option, } #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] @@ -135,7 +137,7 @@ pub mod pallet { /// The current storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = - frame_support::traits::StorageVersion::new(1); + frame_support::traits::StorageVersion::new(2); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -152,7 +154,7 @@ pub mod pallet { /// Number of eras that staked funds must remain locked for. #[pallet::constant] type UnlockingDelay: Get>; - type ClusterVisitor: ClusterVisitor; + type ClusterProtocol: ClusterProtocol>; type ClusterCreator: ClusterCreator>; type WeightInfo: WeightInfo; } @@ -194,9 +196,27 @@ pub mod pallet { /// The account has been charged for the usage Charged { owner_id: T::AccountId, charged: BalanceOf, expected_to_charge: BalanceOf }, /// Bucket with specific id created - BucketCreated { bucket_id: BucketId }, + BucketCreated { cluster_id: ClusterId, bucket_id: BucketId }, /// Bucket with specific id updated - BucketUpdated { bucket_id: BucketId }, + BucketUpdated { cluster_id: ClusterId, bucket_id: BucketId }, + /// Bucket nodes usage with specific id updated + BucketTotalNodesUsageUpdated { + cluster_id: ClusterId, + bucket_id: BucketId, + transferred_bytes: u64, + stored_bytes: i64, + number_of_puts: u64, + number_of_gets: u64, + }, + /// Bucket customers usage with specific id updated + BucketTotalCustomersUsageUpdated { + cluster_id: ClusterId, + bucket_id: BucketId, + transferred_bytes: u64, + stored_bytes: i64, + number_of_puts: u64, + number_of_gets: u64, + }, /// Bucket with specific id marked as removed BucketRemoved { bucket_id: BucketId }, } @@ -305,8 +325,10 @@ pub mod pallet { let cur_bucket_id = Self::buckets_count().checked_add(1).ok_or(Error::::ArithmeticOverflow)?; - ::ClusterVisitor::ensure_cluster(&cluster_id) - .map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!( + >::cluster_exists(&cluster_id), + Error::::ClusterDoesNotExist + ); let bucket = Bucket { bucket_id: cur_bucket_id, @@ -314,12 +336,13 @@ pub mod pallet { cluster_id, is_public: bucket_params.is_public, is_removed: false, + total_customers_usage: None, }; >::set(cur_bucket_id); >::insert(cur_bucket_id, bucket); - Self::deposit_event(Event::::BucketCreated { bucket_id: cur_bucket_id }); + Self::deposit_event(Event::::BucketCreated { cluster_id, bucket_id: cur_bucket_id }); Ok(()) } @@ -509,8 +532,9 @@ pub mod pallet { ensure!(bucket.owner_id == owner, Error::::NotBucketOwner); bucket.is_public = bucket_params.is_public; + let cluster_id = bucket.cluster_id; >::insert(bucket_id, bucket); - Self::deposit_event(Event::::BucketUpdated { bucket_id }); + Self::deposit_event(Event::::BucketUpdated { cluster_id, bucket_id }); Ok(()) } @@ -521,6 +545,7 @@ pub mod pallet { #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::remove_bucket())] pub fn remove_bucket(origin: OriginFor, bucket_id: BucketId) -> DispatchResult { + // todo! can we set total_usage to None and save bytes let owner = ensure_signed(origin)?; >::try_mutate(bucket_id, |maybe_bucket| -> DispatchResult { @@ -622,10 +647,68 @@ pub mod pallet { } } + impl BucketVisitor for Pallet { + fn get_total_customer_usage( + cluster_id: &ClusterId, + bucket_id: BucketId, + content_owner: &T::AccountId, + ) -> Result, BucketVisitorError> { + let bucket = Self::buckets(bucket_id).ok_or(BucketVisitorError::NoBucketWithId)?; + ensure!(bucket.owner_id == *content_owner, BucketVisitorError::NotBucketOwner); + ensure!(bucket.cluster_id == *cluster_id, BucketVisitorError::IncorrectClusterId); + + Ok(bucket.total_customers_usage) + } + } + + impl BucketManager for Pallet { + fn inc_total_customer_usage( + cluster_id: &ClusterId, + bucket_id: BucketId, + content_owner: T::AccountId, + customer_usage: &CustomerUsage, + ) -> DispatchResult { + let mut bucket = Self::buckets(bucket_id).ok_or(Error::::NoBucketWithId)?; + ensure!(bucket.owner_id == content_owner, Error::::NotBucketOwner); + + // Update or initialize total_customers_usage + match &mut bucket.total_customers_usage { + Some(total_customers_usage) => { + total_customers_usage.transferred_bytes += customer_usage.transferred_bytes; + total_customers_usage.stored_bytes += customer_usage.stored_bytes; + total_customers_usage.number_of_puts += customer_usage.number_of_puts; + total_customers_usage.number_of_gets += customer_usage.number_of_gets; + }, + None => { + bucket.total_customers_usage = Some(CustomerUsage { + transferred_bytes: customer_usage.transferred_bytes, + stored_bytes: customer_usage.stored_bytes, + number_of_puts: customer_usage.number_of_puts, + number_of_gets: customer_usage.number_of_gets, + }); + }, + } + + Self::deposit_event(Event::::BucketTotalCustomersUsageUpdated { + cluster_id: *cluster_id, + bucket_id, + transferred_bytes: customer_usage.transferred_bytes, + stored_bytes: customer_usage.stored_bytes, + number_of_puts: customer_usage.number_of_puts, + number_of_gets: customer_usage.number_of_gets, + }); + + Ok(()) + } + } + impl CustomerCharger for Pallet { fn charge_content_owner( + cluster_id: &ClusterId, + bucket_id: BucketId, content_owner: T::AccountId, billing_vault: T::AccountId, + customer_usage: &CustomerUsage, amount: u128, ) -> Result { let actually_charged: BalanceOf; @@ -660,6 +743,13 @@ pub mod pallet { actually_charged.checked_add(&charged).ok_or(Error::::ArithmeticUnderflow)?; } + Self::inc_total_customer_usage( + cluster_id, + bucket_id, + content_owner.clone(), + customer_usage, + )?; + ::Currency::transfer( &Self::account_id(), &billing_vault, @@ -733,4 +823,11 @@ pub mod pallet { Ok(()) } } + + impl CustomerVisitor for Pallet { + fn get_bucket_owner(bucket_id: &BucketId) -> Result { + let bucket = Self::buckets(bucket_id).ok_or(Error::::NoBucketWithId)?; + Ok(bucket.owner_id) + } + } } diff --git a/pallets/ddc-customers/src/migration.rs b/pallets/ddc-customers/src/migration.rs index b95225a3e..576988f78 100644 --- a/pallets/ddc-customers/src/migration.rs +++ b/pallets/ddc-customers/src/migration.rs @@ -1,13 +1,5 @@ -#[cfg(feature = "try-runtime")] -use frame_support::ensure; -use frame_support::{ - storage_alias, - traits::{Get, GetStorageVersion, OnRuntimeUpgrade, StorageVersion}, - weights::Weight, -}; +use frame_support::{storage_alias, traits::OnRuntimeUpgrade}; use log::info; -#[cfg(feature = "try-runtime")] -use sp_runtime::DispatchError; use super::*; @@ -39,92 +31,241 @@ pub mod v0 { >; } -// Migrate to removable buckets -pub fn migrate_to_v1() -> Weight { - let on_chain_version = Pallet::::on_chain_storage_version(); - if on_chain_version == 0 { - let count = v0::BucketsCount::::get(); - info!( - target: LOG_TARGET, - " >>> Updating DDC Customers storage. Migrating {} buckets...", count - ); - - Buckets::::translate::, _>( - |bucket_id: BucketId, bucket: v0::Bucket| { - info!(target: LOG_TARGET, " Migrating bucket for bucket ID {:?}...", bucket_id); - - Some(Bucket { - bucket_id: bucket.bucket_id, - owner_id: bucket.owner_id, - cluster_id: bucket.cluster_id, - is_public: bucket.is_public, - is_removed: false, - }) - }, - ); - - // Update storage version. - StorageVersion::new(1).put::>(); - let count = v0::BucketsCount::::get(); - info!( - target: LOG_TARGET, - " <<< DDC Customers storage updated! Migrated {} buckets ✅", count - ); - - T::DbWeight::get().reads_writes(count + 2, count + 1) - } else { - info!(target: LOG_TARGET, " >>> Unused migration!"); - T::DbWeight::get().reads(1) +pub mod v1 { + use frame_support::pallet_prelude::*; + + use super::*; + + #[storage_alias] + pub(super) type BucketsCount = StorageValue, BucketId, ValueQuery>; + + #[storage_alias] + pub(super) type Buckets = StorageMap< + crate::Pallet, + Twox64Concat, + BucketId, + Bucket<::AccountId>, + OptionQuery, + >; + + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + pub struct Bucket { + pub bucket_id: BucketId, + pub owner_id: AccountId, + pub cluster_id: ClusterId, + pub is_public: bool, + pub is_removed: bool, // new field } -} -pub struct MigrateToV1(sp_std::marker::PhantomData); -impl OnRuntimeUpgrade for MigrateToV1 { - fn on_runtime_upgrade() -> Weight { - migrate_to_v1::() + // Migrate to removable buckets + pub fn migrate_to_v1() -> Weight { + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 0 { + let count = v0::BucketsCount::::get(); + info!( + target: LOG_TARGET, + " >>> Updating DDC Customers storage. Migrating {} buckets...", count + ); + + v1::Buckets::::translate::, _>( + |bucket_id: BucketId, bucket: v0::Bucket| { + info!(target: LOG_TARGET, " Migrating bucket for bucket ID {:?}...", bucket_id); + + Some(v1::Bucket { + bucket_id: bucket.bucket_id, + owner_id: bucket.owner_id, + cluster_id: bucket.cluster_id, + is_public: bucket.is_public, + is_removed: false, + }) + }, + ); + + // Update storage version. + StorageVersion::new(1).put::>(); + let count = v0::BucketsCount::::get(); + info!( + target: LOG_TARGET, + " <<< DDC Customers storage updated! Migrated {} buckets ✅", count + ); + + T::DbWeight::get().reads_writes(count + 2, count + 1) + } else { + info!(target: LOG_TARGET, " >>> Unused migration!"); + T::DbWeight::get().reads(1) + } } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, DispatchError> { - let prev_bucket_id = v0::BucketsCount::::get(); - let prev_count = v0::Buckets::::iter().count(); + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v1::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + let prev_bucket_id = v0::BucketsCount::::get(); + let prev_count = v0::Buckets::::iter().count(); + + Ok((prev_bucket_id, prev_count as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_state: Vec) -> Result<(), DispatchError> { + let (prev_bucket_id, prev_count): (u64, u64) = Decode::decode(&mut &prev_state[..]) + .expect("pre_upgrade provides a valid state; qed"); + + let post_bucket_id = Pallet::::buckets_count(); + ensure!( + prev_bucket_id == post_bucket_id, + "the last bucket ID before and after the migration should be the same" + ); + + let post_count = Buckets::::iter().count() as u64; + ensure!( + prev_count == post_count, + "the bucket count before and after the migration should be the same" + ); + + let current_version = Pallet::::current_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + + frame_support::ensure!(current_version == 1, "must_upgrade"); + ensure!( + current_version == on_chain_version, + "after migration, the current_version and on_chain_version should be the same" + ); - Ok((prev_bucket_id, prev_count as u64).encode()) + Buckets::::iter().try_for_each(|(_id, bucket)| -> Result<(), &'static str> { + ensure!( + !bucket.is_removed, + "At this point all the bucket should have is_removed set to false" + ); + Ok(()) + })?; + Ok(()) + } } +} + +pub mod v2 { - #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_state: Vec) -> Result<(), DispatchError> { - let (prev_bucket_id, prev_count): (u64, u64) = - Decode::decode(&mut &prev_state[..]).expect("pre_upgrade provides a valid state; qed"); + use frame_support::pallet_prelude::*; - let post_bucket_id = Pallet::::buckets_count(); - ensure!( - prev_bucket_id == post_bucket_id, - "the last bucket ID before and after the migration should be the same" - ); + use super::*; + + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + #[scale_info(skip_type_params(T))] + pub struct Bucket { + bucket_id: BucketId, + owner_id: AccountId, + cluster_id: ClusterId, + is_public: bool, + is_removed: bool, + total_customers_usage: Option, // new field + } - let post_count = Buckets::::iter().count() as u64; - ensure!( - prev_count == post_count, - "the bucket count before and after the migration should be the same" - ); + #[storage_alias] + pub(super) type BucketsCount = StorageValue, BucketId, ValueQuery>; + + #[storage_alias] + pub(super) type Buckets = StorageMap< + crate::Pallet, + Twox64Concat, + BucketId, + Bucket<::AccountId>, + OptionQuery, + >; - let current_version = Pallet::::current_storage_version(); + // New migration to add total_customers_usage field + pub fn migrate_to_v2() -> Weight { let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 1 { + let count = v1::BucketsCount::::get(); + info!( + target: LOG_TARGET, + " >>> Updating DDC Customers storage to v2. Migrating {} buckets...", count + ); + + v2::Buckets::::translate::, _>( + |bucket_id: BucketId, bucket: v1::Bucket| { + info!(target: LOG_TARGET, " Migrating bucket for bucket ID {:?}...", bucket_id); + + Some(v2::Bucket { + bucket_id: bucket.bucket_id, + owner_id: bucket.owner_id, + cluster_id: bucket.cluster_id, + is_public: bucket.is_public, + is_removed: bucket.is_removed, + total_customers_usage: None, + }) + }, + ); + + // Update storage version. + StorageVersion::new(2).put::>(); + let count = v1::BucketsCount::::get(); + info!( + target: LOG_TARGET, + " <<< DDC Customers storage updated to v2! Migrated {} buckets ✅", count + ); + + T::DbWeight::get().reads_writes(count + 2, count + 1) + } else { + info!(target: LOG_TARGET, " >>> Unused migration to v2!"); + T::DbWeight::get().reads(1) + } + } + + pub struct MigrateToV2(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV2 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v2::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + let prev_bucket_id = v1::BucketsCount::::get(); + let prev_count = v1::Buckets::::iter().count(); - frame_support::ensure!(current_version == 1, "must_upgrade"); - ensure!( - current_version == on_chain_version, - "after migration, the current_version and on_chain_version should be the same" - ); + Ok((prev_bucket_id, prev_count as u64).encode()) + } - Buckets::::iter().try_for_each(|(_id, bucket)| -> Result<(), &'static str> { + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_state: Vec) -> Result<(), DispatchError> { + let (prev_bucket_id, prev_count): (u64, u64) = Decode::decode(&mut &prev_state[..]) + .expect("pre_upgrade provides a valid state; qed"); + + let post_bucket_id = v2::BucketsCount::::get(); + ensure!( + prev_bucket_id == post_bucket_id, + "the last bucket ID before and after the migration should be the same" + ); + + let post_count = v2::Buckets::::iter().count() as u64; + ensure!( + prev_count == post_count, + "the bucket count before and after the migration should be the same" + ); + + let current_version = Pallet::::current_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + + frame_support::ensure!(current_version == 2, "must_upgrade"); ensure!( - !bucket.is_removed, - "At this point all the bucket should have is_removed set to false" + current_version == on_chain_version, + "after migration, the current_version and on_chain_version should be the same" ); + + v2::Buckets::::iter().try_for_each(|(_id, bucket)| -> Result<(), &'static str> { + ensure!( + bucket.total_customers_usage.is_none(), + "At this point all the bucket should have total_customers_usage set to None" + ); + Ok(()) + })?; + Ok(()) - })?; - Ok(()) + } } } diff --git a/pallets/ddc-customers/src/mock.rs b/pallets/ddc-customers/src/mock.rs index 451519b81..1fa0b6d5a 100644 --- a/pallets/ddc-customers/src/mock.rs +++ b/pallets/ddc-customers/src/mock.rs @@ -1,9 +1,10 @@ //! Test utilities use ddc_primitives::{ - traits::cluster::{ClusterManager, ClusterManagerError, ClusterVisitorError}, - ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterParams, ClusterPricingParams, - NodePubKey, NodeType, + traits::cluster::{ClusterCreator, ClusterManager, ClusterProtocol, ClusterQuery}, + ClusterBondingParams, ClusterFeesParams, ClusterId, ClusterNodeKind, ClusterNodeState, + ClusterNodeStatus, ClusterNodesStats, ClusterParams, ClusterPricingParams, + ClusterProtocolParams, ClusterStatus, NodePubKey, NodeType, }; use frame_support::{ construct_runtime, parameter_types, @@ -15,7 +16,7 @@ use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, DispatchResult, Perquintill, + BuildStorage, DispatchError, DispatchResult, Perquintill, }; use crate::{self as pallet_ddc_customers, *}; @@ -78,6 +79,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = (); type RuntimeHoldReason = (); @@ -100,38 +102,48 @@ impl crate::pallet::Config for Test { type Currency = Balances; type PalletId = DdcCustomersPalletId; type RuntimeEvent = RuntimeEvent; - type ClusterVisitor = TestClusterVisitor; + type ClusterProtocol = TestClusterProtocol; type ClusterCreator = TestClusterCreator; type WeightInfo = (); } -pub struct TestClusterVisitor; -impl ClusterVisitor for TestClusterVisitor { - fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { - Ok(()) +pub struct TestClusterProtocol; +impl ClusterQuery for TestClusterProtocol { + fn cluster_exists(_cluster_id: &ClusterId) -> bool { + true + } + + fn get_cluster_status(_cluster_id: &ClusterId) -> Result { + unimplemented!() } - fn get_bond_size( + + fn get_manager_and_reserve_id( _cluster_id: &ClusterId, - _node_type: NodeType, - ) -> Result { + ) -> Result<(T::AccountId, T::AccountId), DispatchError> { + unimplemented!() + } +} + +impl ClusterProtocol> for TestClusterProtocol { + fn get_bond_size(_cluster_id: &ClusterId, _node_type: NodeType) -> Result { Ok(10) } + fn get_chill_delay( _cluster_id: &ClusterId, _node_type: NodeType, - ) -> Result, ClusterVisitorError> { + ) -> Result, DispatchError> { Ok(BlockNumberFor::::from(10u32)) } + fn get_unbonding_delay( _cluster_id: &ClusterId, _node_type: NodeType, - ) -> Result, ClusterVisitorError> { + ) -> Result, DispatchError> { Ok(BlockNumberFor::::from(10u32)) } - fn get_pricing_params( - _cluster_id: &ClusterId, - ) -> Result { + fn get_pricing_params(_cluster_id: &ClusterId) -> Result { Ok(ClusterPricingParams { unit_per_mb_stored: 1, unit_per_mb_streamed: 2, @@ -140,7 +152,7 @@ impl ClusterVisitor for TestClusterVisitor { }) } - fn get_fees_params(_cluster_id: &ClusterId) -> Result { + fn get_fees_params(_cluster_id: &ClusterId) -> Result { Ok(ClusterFeesParams { treasury_share: Perquintill::from_percent(1), validators_share: Perquintill::from_percent(10), @@ -148,64 +160,136 @@ impl ClusterVisitor for TestClusterVisitor { }) } - fn get_reserve_account_id( - _cluster_id: &ClusterId, - ) -> Result { - Err(ClusterVisitorError::ClusterDoesNotExist) - } - fn get_bonding_params( cluster_id: &ClusterId, - ) -> Result>, ClusterVisitorError> { + ) -> Result>, DispatchError> { Ok(ClusterBondingParams { - storage_bond_size: >::get_bond_size( - cluster_id, - NodeType::Storage, - ) - .unwrap_or_default(), - storage_chill_delay: >::get_chill_delay( - cluster_id, - NodeType::Storage, - ) - .unwrap_or_default(), + storage_bond_size: + >>::get_bond_size( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + storage_chill_delay: + >>::get_chill_delay( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), storage_unbonding_delay: - >::get_unbonding_delay( + >>::get_unbonding_delay( cluster_id, NodeType::Storage, ) .unwrap_or_default(), }) } + + fn get_reserve_account_id(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn activate_cluster_protocol(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn update_cluster_protocol( + _cluster_id: &ClusterId, + _cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + unimplemented!() + } + + fn bond_cluster(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn start_unbond_cluster(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn end_unbond_cluster(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } } pub struct TestClusterManager; +impl ClusterQuery for TestClusterManager { + fn cluster_exists(_cluster_id: &ClusterId) -> bool { + true + } + + fn get_cluster_status(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn get_manager_and_reserve_id( + _cluster_id: &ClusterId, + ) -> Result<(T::AccountId, T::AccountId), DispatchError> { + unimplemented!() + } +} + impl ClusterManager for TestClusterManager { - fn contains_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { + fn get_manager_account_id(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn get_nodes(_cluster_id: &ClusterId) -> Result, DispatchError> { + unimplemented!() + } + + fn contains_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + _validation_status: Option, + ) -> bool { true } + fn add_node( _cluster_id: &ClusterId, _node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { + _node_kind: &ClusterNodeKind, + ) -> Result<(), DispatchError> { Ok(()) } fn remove_node( _cluster_id: &ClusterId, _node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { + ) -> Result<(), DispatchError> { Ok(()) } + + fn get_node_state( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result>, DispatchError> { + unimplemented!() + } + + fn get_nodes_stats(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn validate_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + _succeeded: bool, + ) -> Result<(), DispatchError> { + unimplemented!() + } } pub struct TestClusterCreator; impl ClusterCreator for TestClusterCreator { - fn create_new_cluster( + fn create_cluster( _cluster_id: ClusterId, _cluster_manager_id: T::AccountId, _cluster_reserve_id: T::AccountId, _cluster_params: ClusterParams, - _cluster_gov_params: ClusterGovParams>, + _cluster_protocol_params: ClusterProtocolParams>, ) -> DispatchResult { Ok(()) } diff --git a/pallets/ddc-customers/src/tests.rs b/pallets/ddc-customers/src/tests.rs index 86d1e0bf9..9f31edcf5 100644 --- a/pallets/ddc-customers/src/tests.rs +++ b/pallets/ddc-customers/src/tests.rs @@ -30,12 +30,13 @@ fn create_bucket_works() { cluster_id, is_public: bucket_params.is_public, is_removed: false, + total_customers_usage: None, }) ); // Checking that event was emitted assert_eq!(System::events().len(), 1); - System::assert_last_event(Event::BucketCreated { bucket_id: 1u64 }.into()) + System::assert_last_event(Event::BucketCreated { cluster_id, bucket_id: 1u64 }.into()) }) } @@ -56,14 +57,14 @@ fn create_two_buckets_works() { bucket_1_params.clone() )); assert_eq!(System::events().len(), 1); - System::assert_last_event(Event::BucketCreated { bucket_id: 1u64 }.into()); + System::assert_last_event(Event::BucketCreated { cluster_id, bucket_id: 1u64 }.into()); assert_ok!(DdcCustomers::create_bucket( RuntimeOrigin::signed(account_1), cluster_id, bucket_2_params.clone() )); assert_eq!(System::events().len(), 2); - System::assert_last_event(Event::BucketCreated { bucket_id: 2u64 }.into()); + System::assert_last_event(Event::BucketCreated { cluster_id, bucket_id: 2u64 }.into()); // Check storage assert_eq!(DdcCustomers::buckets_count(), 2); @@ -75,6 +76,7 @@ fn create_two_buckets_works() { cluster_id, is_public: bucket_1_params.is_public, is_removed: false, + total_customers_usage: None, }) ); assert_eq!( @@ -85,6 +87,7 @@ fn create_two_buckets_works() { cluster_id, is_public: bucket_2_params.is_public, is_removed: false, + total_customers_usage: None, }) ); }) @@ -175,10 +178,27 @@ fn charge_content_owner_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let cluster_id = ClusterId::from([1; 20]); + let bucket_1_params = BucketParams { is_public: false }; + let customer_usage = CustomerUsage { + transferred_bytes: 1, + stored_bytes: 2, + number_of_puts: 3, + number_of_gets: 4, + }; + let account_2: u128 = 2; let account_3: u128 = 3; let vault: u128 = 4; let deposit = 100_u128; + assert_ok!(DdcCustomers::create_bucket( + RuntimeOrigin::signed(account_3), + cluster_id, + bucket_1_params.clone() + )); + let balance_before_deposit = Balances::free_balance(account_3); // Deposited assert_ok!(DdcCustomers::deposit(RuntimeOrigin::signed(account_3), deposit)); @@ -204,9 +224,29 @@ fn charge_content_owner_works() { // successful transfer let charge1 = 10; - let charged = DdcCustomers::charge_content_owner(account_3, vault, charge1).unwrap(); + let charged = DdcCustomers::charge_content_owner( + &cluster_id, + bucket_id1, + account_3, + vault, + &customer_usage, + charge1, + ) + .unwrap(); assert_eq!(charge1, charged); + System::assert_has_event( + Event::BucketTotalCustomersUsageUpdated { + cluster_id, + bucket_id: bucket_id1, + transferred_bytes: customer_usage.transferred_bytes, + stored_bytes: customer_usage.stored_bytes, + number_of_puts: customer_usage.number_of_puts, + number_of_gets: customer_usage.number_of_gets, + } + .into(), + ); + let vault_balance = Balances::free_balance(vault); assert_eq!(charged, vault_balance); @@ -234,7 +274,15 @@ fn charge_content_owner_works() { // failed transfer let charge2 = 100u128; - let charge_result = DdcCustomers::charge_content_owner(account_3, vault, charge2).unwrap(); + let charge_result = DdcCustomers::charge_content_owner( + &cluster_id, + bucket_id1, + account_3, + vault, + &customer_usage, + charge2, + ) + .unwrap(); assert_eq!( DdcCustomers::ledger(account_3), Some(AccountsLedger { @@ -276,6 +324,31 @@ fn charge_content_owner_works() { deposit, Balances::free_balance(DdcCustomers::account_id()) - Balances::minimum_balance() ); + + assert_ok!(DdcCustomers::deposit(RuntimeOrigin::signed(account_2), 50_u128)); + assert_noop!( + DdcCustomers::charge_content_owner( + &cluster_id, + bucket_id1, + account_2, + vault, + &customer_usage, + charge1, + ), + Error::::NotBucketOwner + ); + + assert_noop!( + DdcCustomers::charge_content_owner( + &cluster_id, + bucket_id2, + account_3, + vault, + &customer_usage, + charge1, + ), + Error::::NoBucketWithId + ); }) } @@ -363,7 +436,7 @@ fn set_bucket_params_works() { // Checking that event was emitted assert_eq!(System::events().len(), 1); - System::assert_last_event(Event::BucketCreated { bucket_id: 1u64 }.into()); + System::assert_last_event(Event::BucketCreated { cluster_id, bucket_id: 1u64 }.into()); let bucket_id = 1; let update_bucket_params = BucketParams { is_public: true }; @@ -382,12 +455,13 @@ fn set_bucket_params_works() { cluster_id, is_public: update_bucket_params.is_public, is_removed: false, + total_customers_usage: None, }) ); // Checking that event was emitted assert_eq!(System::events().len(), 2); - System::assert_last_event(Event::BucketUpdated { bucket_id }.into()); + System::assert_last_event(Event::BucketUpdated { cluster_id, bucket_id }.into()); }) } @@ -409,7 +483,7 @@ fn set_bucket_params_checks_work() { // Checking that event was emitted assert_eq!(System::events().len(), 1); - System::assert_last_event(Event::BucketCreated { bucket_id: 1u64 }.into()); + System::assert_last_event(Event::BucketCreated { cluster_id, bucket_id: 1u64 }.into()); let bucket_id = 1; let non_existent_bucket_id = 2; @@ -475,6 +549,7 @@ fn remove_bucket_works() { cluster_id, is_public: bucket_params.is_public, is_removed: false, + total_customers_usage: None, }) ); @@ -491,6 +566,7 @@ fn remove_bucket_works() { cluster_id, is_public: bucket_params.is_public, is_removed: true, + total_customers_usage: None, }) ); @@ -565,6 +641,7 @@ fn remove_bucket_checks_with_multiple_buckets_works() { cluster_id, is_public: private_bucket_params.is_public, is_removed: true, + total_customers_usage: None, }) ); @@ -576,6 +653,7 @@ fn remove_bucket_checks_with_multiple_buckets_works() { cluster_id, is_public: public_bucket_params.is_public, is_removed: false, + total_customers_usage: None, }) ); diff --git a/pallets/ddc-customers/src/weights.rs b/pallets/ddc-customers/src/weights.rs index 017c2d998..893b116d8 100644 --- a/pallets/ddc-customers/src/weights.rs +++ b/pallets/ddc-customers/src/weights.rs @@ -1,9 +1,9 @@ //! Autogenerated weights for pallet_ddc_customers //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-02-18, STEPS: `50`, REPEAT: 50, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bench`, CPU: `DO-Premium-AMD` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/cere @@ -11,12 +11,13 @@ // pallet // --chain=dev // --execution=wasm -// --pallet=pallet-ddc-customers +// --wasm-execution=compiled +// --pallet=pallet_ddc_customers // --extrinsic=* // --steps=50 -// --repeat=50 +// --repeat=20 // --template=./.maintain/frame-weight-template.hbs -// --output=pallets/ddc-customers/src/weights.rs +// --output=pallets/ddc-customers/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -40,71 +41,71 @@ pub trait WeightInfo { /// Weights for pallet_ddc_customers using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: DdcCustomers BucketsCount (r:1 w:1) - // Proof Skipped: DdcCustomers BucketsCount (max_values: Some(1), max_size: None, mode: Measured) - // Storage: DdcClusters Clusters (r:1 w:0) - // Proof Skipped: DdcClusters Clusters (max_values: None, max_size: None, mode: Measured) - // Storage: DdcCustomers Buckets (r:0 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::BucketsCount` (r:1 w:1) + // Proof: `DdcCustomers::BucketsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcCustomers::Buckets` (r:0 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_bucket() -> Weight { - Weight::from_parts(39_599_000_u64, 0) + Weight::from_parts(27_061_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn deposit() -> Weight { - Weight::from_parts(124_067_000_u64, 0) + Weight::from_parts(79_879_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn deposit_extra() -> Weight { - Weight::from_parts(125_998_000_u64, 0) + Weight::from_parts(80_612_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unlock_deposit() -> Weight { - Weight::from_parts(37_109_000_u64, 0) + Weight::from_parts(24_205_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn withdraw_unlocked_deposit_update() -> Weight { - Weight::from_parts(130_058_000_u64, 0) + Weight::from_parts(80_711_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn withdraw_unlocked_deposit_kill() -> Weight { - Weight::from_parts(131_827_000_u64, 0) + Weight::from_parts(84_608_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Buckets (r:1 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Buckets` (r:1 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_bucket_params() -> Weight { - Weight::from_parts(31_820_000_u64, 0) + Weight::from_parts(21_910_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcCustomers Buckets (r:1 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Buckets` (r:1 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_bucket() -> Weight { - Weight::from_parts(34_070_000_u64, 0) + Weight::from_parts(20_589_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -112,72 +113,72 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: DdcCustomers BucketsCount (r:1 w:1) - // Proof Skipped: DdcCustomers BucketsCount (max_values: Some(1), max_size: None, mode: Measured) - // Storage: DdcClusters Clusters (r:1 w:0) - // Proof Skipped: DdcClusters Clusters (max_values: None, max_size: None, mode: Measured) - // Storage: DdcCustomers Buckets (r:0 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::BucketsCount` (r:1 w:1) + // Proof: `DdcCustomers::BucketsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcCustomers::Buckets` (r:0 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_bucket() -> Weight { - Weight::from_parts(39_599_000_u64, 0) + Weight::from_parts(27_061_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn deposit() -> Weight { - Weight::from_parts(124_067_000_u64, 0) + Weight::from_parts(79_879_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn deposit_extra() -> Weight { - Weight::from_parts(125_998_000_u64, 0) + Weight::from_parts(80_612_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unlock_deposit() -> Weight { - Weight::from_parts(37_109_000_u64, 0) + Weight::from_parts(24_205_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn withdraw_unlocked_deposit_update() -> Weight { - Weight::from_parts(130_058_000_u64, 0) + Weight::from_parts(80_711_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Ledger (r:1 w:1) - // Proof Skipped: DdcCustomers Ledger (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `DdcCustomers::Ledger` (r:1 w:1) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn withdraw_unlocked_deposit_kill() -> Weight { - Weight::from_parts(131_827_000_u64, 0) + Weight::from_parts(84_608_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcCustomers Buckets (r:1 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Buckets` (r:1 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_bucket_params() -> Weight { - Weight::from_parts(31_820_000_u64, 0) + Weight::from_parts(21_910_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcCustomers Buckets (r:1 w:1) - // Proof Skipped: DdcCustomers Buckets (max_values: None, max_size: None, mode: Measured) + // Storage: `DdcCustomers::Buckets` (r:1 w:1) + // Proof: `DdcCustomers::Buckets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_bucket() -> Weight { - Weight::from_parts(34_070_000_u64, 0) + Weight::from_parts(20_589_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } -} \ No newline at end of file +} diff --git a/pallets/ddc-nodes/Cargo.toml b/pallets/ddc-nodes/Cargo.toml index 20551c5dc..f8e6e47c6 100644 --- a/pallets/ddc-nodes/Cargo.toml +++ b/pallets/ddc-nodes/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true [dependencies] # 3rd-party dependencies codec = { workspace = true } +log = { workspace = true } scale-info = { workspace = true } serde = { workspace = true } @@ -18,6 +19,7 @@ serde = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } +hex = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } @@ -35,6 +37,7 @@ substrate-test-utils = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "hex/std", "codec/std", "ddc-primitives/std", "frame-benchmarking/std", diff --git a/pallets/ddc-nodes/src/lib.rs b/pallets/ddc-nodes/src/lib.rs index 4f0e8aba0..2a0e2fdc8 100644 --- a/pallets/ddc-nodes/src/lib.rs +++ b/pallets/ddc-nodes/src/lib.rs @@ -29,15 +29,16 @@ pub mod testing_utils; use ddc_primitives::{ traits::{ - node::{NodeCreator, NodeVisitor, NodeVisitorError}, + node::{NodeCreator, NodeVisitor}, staking::StakingVisitor, }, - ClusterId, NodeParams, NodePubKey, StorageNodePubKey, + ClusterId, NodeParams, NodePubKey, NodeUsage, StorageNodeParams, StorageNodePubKey, }; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; pub use pallet::*; use sp_std::prelude::*; +pub mod migrations; mod node; mod storage_node; @@ -48,11 +49,12 @@ pub use crate::{ #[frame_support::pallet] pub mod pallet { + use self::node::NodeProps; use super::*; /// The current storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = - frame_support::traits::StorageVersion::new(0); + frame_support::traits::StorageVersion::new(1); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -225,17 +227,46 @@ pub mod pallet { } impl NodeVisitor for Pallet { - fn get_cluster_id( - node_pub_key: &NodePubKey, - ) -> Result, NodeVisitorError> { - let node = - Self::get(node_pub_key.clone()).map_err(|_| NodeVisitorError::NodeDoesNotExist)?; + fn get_cluster_id(node_pub_key: &NodePubKey) -> Result, DispatchError> { + let node = Self::get(node_pub_key.clone()).map_err(|_| Error::::NodeDoesNotExist)?; Ok(*node.get_cluster_id()) } fn exists(node_pub_key: &NodePubKey) -> bool { Self::get(node_pub_key.clone()).is_ok() } + + fn get_node_provider_id(node_pub_key: &NodePubKey) -> Result { + let node = Self::get(node_pub_key.clone()).map_err(|_| Error::::NodeDoesNotExist)?; + Ok(node.get_provider_id().clone()) + } + + fn get_node_params(node_pub_key: &NodePubKey) -> Result { + let node = Self::get(node_pub_key.clone()).map_err(|_| Error::::NodeDoesNotExist)?; + let node_props = node.get_props().clone(); + + match node_pub_key { + NodePubKey::StoragePubKey(_) => match node_props { + NodeProps::StorageProps(node_props) => + Ok(ddc_primitives::NodeParams::StorageParams(StorageNodeParams { + mode: node_props.mode, + host: node_props.host.into(), + domain: node_props.domain.into(), + ssl: node_props.ssl, + http_port: node_props.http_port, + grpc_port: node_props.grpc_port, + p2p_port: node_props.p2p_port, + })), + }, + } + } + + fn get_total_usage(node_pub_key: &NodePubKey) -> Result, DispatchError> { + let node = Self::get(node_pub_key.clone()).map_err(|_| Error::::NodeDoesNotExist)?; + let total_usage = node.get_total_usage().clone(); + + Ok(total_usage) + } } impl NodeCreator for Pallet { diff --git a/pallets/ddc-nodes/src/migrations.rs b/pallets/ddc-nodes/src/migrations.rs new file mode 100644 index 000000000..57857b0dd --- /dev/null +++ b/pallets/ddc-nodes/src/migrations.rs @@ -0,0 +1,224 @@ +#[cfg(feature = "try-runtime")] +use ddc_primitives::StorageNodePubKey; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; +use frame_support::{ + storage_alias, + traits::{Get, GetStorageVersion, OnRuntimeUpgrade, StorageVersion}, + weights::Weight, +}; +use log::info; +use serde::{Deserialize, Serialize}; +use sp_runtime::Saturating; + +use super::*; +use crate::{storage_node::StorageNodeProps, ClusterId}; + +const LOG_TARGET: &str = "ddc-customers"; + +pub mod v0 { + use frame_support::pallet_prelude::*; + + use super::*; + + // Define the old storage node structure + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + #[scale_info(skip_type_params(T))] + pub struct StorageNode { + pub pub_key: StorageNodePubKey, + pub provider_id: ::AccountId, + pub cluster_id: Option, + pub props: StorageNodeProps, + } + + #[storage_alias] + pub type StorageNodes = + StorageMap, Blake2_128Concat, StorageNodePubKey, StorageNode>; +} + +pub mod v1 { + use super::*; + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] + #[scale_info(skip_type_params(T))] + pub struct StorageNode { + pub pub_key: StorageNodePubKey, + pub provider_id: T::AccountId, + pub cluster_id: Option, + pub props: StorageNodeProps, + pub total_usage: Option, // new field + } + + #[storage_alias] + pub type StorageNodes = + StorageMap, Blake2_128Concat, StorageNodePubKey, StorageNode>; + + pub fn migrate_to_v1() -> Weight { + let on_chain_version = Pallet::::on_chain_storage_version(); + let current_version = Pallet::::current_storage_version(); + + info!( + target: LOG_TARGET, + "Running migration with current storage version {:?} / onchain {:?}", + current_version, + on_chain_version + ); + + if on_chain_version == 0 && current_version == 1 { + let weight = T::DbWeight::get().reads(1); + + let mut translated = 0u64; + let count = v0::StorageNodes::::iter().count(); + info!( + target: LOG_TARGET, + " >>> Updating DDC Storage Nodes. Migrating {} nodes...", count + ); + v1::StorageNodes::::translate::, _>( + |_, old: v0::StorageNode| { + let node_pub_key_ref: &[u8; 32] = old.pub_key.as_ref(); + let node_pub_key_string = hex::encode(node_pub_key_ref); + info!(target: LOG_TARGET, " Migrating node for node ID {:?}...", node_pub_key_string); + translated.saturating_inc(); + + Some(StorageNode { + pub_key: old.pub_key, + provider_id: old.provider_id, + cluster_id: old.cluster_id, + props: old.props, + total_usage: None, // Default value for the new field + }) + }, + ); + + // Update storage version. + StorageVersion::new(1).put::>(); + let count = v1::StorageNodes::::iter().count(); + info!( + target: LOG_TARGET, + "Upgraded {} records, storage to version {:?}", + count, + current_version + ); + + weight.saturating_add(T::DbWeight::get().reads_writes(translated + 1, translated + 1)) + } else { + info!(target: LOG_TARGET, " >>> Unused migration!"); + T::DbWeight::get().reads(1) + } + } + + pub struct MigrateToV1(PhantomData); + + impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + migrate_to_v1::() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + let prev_count = v0::StorageNodes::::iter().count(); + + Ok((prev_count as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev_state: Vec) -> Result<(), sp_runtime::DispatchError> { + let prev_count: u64 = Decode::decode(&mut &prev_state[..]) + .expect("pre_upgrade provides a valid state; qed"); + + let post_count = v1::StorageNodes::::iter().count() as u64; + ensure!( + prev_count == post_count, + "the storage node count before and after the migration should be the same" + ); + + let current_version = Pallet::::current_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + + ensure!(current_version == 1, "must_upgrade"); + ensure!( + current_version == on_chain_version, + "after migration, the current_version and on_chain_version should be the same" + ); + + // Ensure all nodes have total_usage set to None + for (_key, node) in v1::StorageNodes::::iter() { + ensure!(node.total_usage.is_none(), "total_usage should be None"); + } + + Ok(()) + } + } +} + +#[cfg(test)] +#[cfg(feature = "try-runtime")] +mod test { + use ddc_primitives::StorageNodeMode; + use frame_support::pallet_prelude::StorageVersion; + + use super::*; + use crate::mock::{Test as T, *}; + + #[test] + fn storage_node_migration_works() { + ExtBuilder.build_and_execute(|| { + let node_pub_key0 = StorageNodePubKey::from([0; 32]); + let node_pub_key1 = StorageNodePubKey::from([1; 32]); + let node_pub_key2 = StorageNodePubKey::from([2; 32]); + let provider_id = AccountId::from([1; 32]); + let cluster_id = Some(ClusterId::from([1; 20])); + + assert_eq!(StorageVersion::get::>(), 0); + + let node1 = v0::StorageNode { + pub_key: node_pub_key1.clone(), + provider_id: provider_id.clone(), + cluster_id, + props: StorageNodeProps { + mode: StorageNodeMode::Storage, + host: vec![3u8; 255].try_into().unwrap(), + domain: vec![4u8; 255].try_into().unwrap(), + ssl: true, + http_port: 45000u16, + grpc_port: 55000u16, + p2p_port: 65000u16, + }, + }; + + v0::StorageNodes::::insert(node_pub_key1.clone(), node1); + let node2 = v0::StorageNode { + pub_key: node_pub_key2.clone(), + provider_id: provider_id.clone(), + cluster_id, + props: StorageNodeProps { + mode: StorageNodeMode::Storage, + host: vec![3u8; 255].try_into().unwrap(), + domain: vec![4u8; 255].try_into().unwrap(), + ssl: true, + http_port: 45000u16, + grpc_port: 55000u16, + p2p_port: 65000u16, + }, + }; + + v0::StorageNodes::::insert(node_pub_key2.clone(), node2); + let node_count = v0::StorageNodes::::iter_values().count() as u32; + + assert_eq!(node_count, 2); + let state = v1::MigrateToV1::::pre_upgrade().unwrap(); + let _weight = v1::MigrateToV1::::on_runtime_upgrade(); + v1::MigrateToV1::::post_upgrade(state).unwrap(); + + let node_count_after_upgrade = v1::StorageNodes::::iter_values().count() as u32; + + assert_eq!(StorageVersion::get::>(), 1); + assert_eq!(node_count_after_upgrade, 2); + assert_eq!(StorageNodes::::get(node_pub_key0), None); + assert!(StorageNodes::::get(node_pub_key1.clone()).is_some()); + assert!(StorageNodes::::get(node_pub_key2.clone()).is_some()); + assert_eq!(StorageNodes::::get(node_pub_key1).unwrap().total_usage, None); + assert_eq!(StorageNodes::::get(node_pub_key2).unwrap().total_usage, None); + }); + } +} diff --git a/pallets/ddc-nodes/src/mock.rs b/pallets/ddc-nodes/src/mock.rs index ab5fb34f7..501b6b555 100644 --- a/pallets/ddc-nodes/src/mock.rs +++ b/pallets/ddc-nodes/src/mock.rs @@ -12,17 +12,18 @@ use frame_system::mocking::{MockBlock, MockUncheckedExtrinsic}; use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, }; use crate::{self as pallet_ddc_nodes, *}; /// The AccountId alias in this test module. -pub(crate) type AccountId = u64; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u128; +pub type Signature = MultiSignature; type UncheckedExtrinsic = MockUncheckedExtrinsic; type Block = MockBlock; @@ -78,6 +79,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = (); type RuntimeHoldReason = (); @@ -110,6 +112,10 @@ impl StakingVisitor for TestStakingVisitor { fn has_chilling_attempt(_node_pub_key: &NodePubKey) -> Result { Ok(false) } + + fn stash_by_ctrl(_controller: &T::AccountId) -> Result { + todo!() + } } pub(crate) type TestRuntimeCall = ::RuntimeCall; @@ -121,9 +127,12 @@ impl ExtBuilder { sp_tracing::try_init_simple(); let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - let _ = pallet_balances::GenesisConfig:: { balances: vec![(1, 100), (2, 100)] } - .assimilate_storage(&mut t); + let account_id1 = AccountId::from([1; 32]); + let account_id2 = AccountId::from([2; 32]); + let _ = pallet_balances::GenesisConfig:: { + balances: vec![(account_id1, 100), (account_id2, 100)], + } + .assimilate_storage(&mut t); TestExternalities::new(t) } diff --git a/pallets/ddc-nodes/src/node.rs b/pallets/ddc-nodes/src/node.rs index 617d9f501..b738d0667 100644 --- a/pallets/ddc-nodes/src/node.rs +++ b/pallets/ddc-nodes/src/node.rs @@ -1,7 +1,7 @@ #![allow(clippy::needless_lifetimes)] // ToDo use codec::{Decode, Encode}; -use ddc_primitives::{NodeParams, NodePubKey, NodeType}; +use ddc_primitives::{NodeParams, NodePubKey, NodeType, NodeUsage}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; @@ -31,6 +31,7 @@ pub trait NodeTrait { fn get_cluster_id(&self) -> &Option; fn set_cluster_id(&mut self, cluster_id: Option); fn get_type(&self) -> NodeType; + fn get_total_usage(&self) -> &Option; } impl Node { @@ -77,6 +78,11 @@ impl NodeTrait for Node { Node::Storage(node) => node.get_cluster_id(), } } + fn get_total_usage(&self) -> &Option { + match &self { + Node::Storage(node) => node.get_total_usage(), + } + } fn set_cluster_id(&mut self, cluster_id: Option) { match self { Node::Storage(node) => node.set_cluster_id(cluster_id), diff --git a/pallets/ddc-nodes/src/storage_node.rs b/pallets/ddc-nodes/src/storage_node.rs index 43d4d06fd..3831bcf17 100644 --- a/pallets/ddc-nodes/src/storage_node.rs +++ b/pallets/ddc-nodes/src/storage_node.rs @@ -1,6 +1,6 @@ use codec::{Decode, Encode}; use ddc_primitives::{ - ClusterId, NodeParams, NodePubKey, NodeType, StorageNodeMode, StorageNodePubKey, + ClusterId, NodeParams, NodePubKey, NodeType, NodeUsage, StorageNodeMode, StorageNodePubKey, }; use frame_support::{parameter_types, BoundedVec}; use scale_info::TypeInfo; @@ -21,6 +21,7 @@ pub struct StorageNode { pub provider_id: T::AccountId, pub cluster_id: Option, pub props: StorageNodeProps, + pub total_usage: Option, } #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] @@ -61,6 +62,7 @@ impl StorageNode { grpc_port: node_params.grpc_port, p2p_port: node_params.p2p_port, }, + total_usage: None, }), }, } @@ -106,6 +108,9 @@ impl NodeTrait for StorageNode { fn get_cluster_id(&self) -> &Option { &self.cluster_id } + fn get_total_usage(&self) -> &Option { + &self.total_usage + } fn set_cluster_id(&mut self, cluster_id: Option) { self.cluster_id = cluster_id; } diff --git a/pallets/ddc-nodes/src/tests.rs b/pallets/ddc-nodes/src/tests.rs index 618a01c8e..eb3de5d31 100644 --- a/pallets/ddc-nodes/src/tests.rs +++ b/pallets/ddc-nodes/src/tests.rs @@ -23,10 +23,12 @@ fn create_storage_node_works() { p2p_port: 15000u16, }; + let account_id1 = AccountId::from([1; 32]); + // Host length exceeds limit assert_noop!( DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, @@ -44,7 +46,7 @@ fn create_storage_node_works() { // Host length exceeds limit assert_noop!( DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, @@ -61,7 +63,7 @@ fn create_storage_node_works() { // Node created assert_ok!(DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params.clone()) )); @@ -73,7 +75,7 @@ fn create_storage_node_works() { storage_node_params.clone().domain.try_into().unwrap(); assert_eq!(created_storage_node.pub_key, node_pub_key); - assert_eq!(created_storage_node.provider_id, 1); + assert_eq!(created_storage_node.provider_id, account_id1); assert_eq!(created_storage_node.cluster_id, None); assert_eq!(created_storage_node.props.host, expected_host); assert_eq!(created_storage_node.props.domain, expected_domain); @@ -97,7 +99,7 @@ fn create_storage_node_works() { // Node already exists assert_noop!( DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params) ), @@ -127,11 +129,12 @@ fn create_storage_node_with_node_creator() { grpc_port: 25000u16, p2p_port: 15000u16, }; + let account_id1 = AccountId::from([1; 32]); // Node created assert_ok!(>::create_node( NodePubKey::StoragePubKey(node_pub_key.clone()), - 1u64, + account_id1, NodeParams::StorageParams(storage_node_params) )); @@ -164,10 +167,13 @@ fn set_storage_node_params_works() { p2p_port: 15000u16, }; + let account_id1 = AccountId::from([1; 32]); + let account_id2 = AccountId::from([2; 32]); + // Node doesn't exist assert_noop!( DdcNodes::set_node_params( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params.clone()) ), @@ -176,7 +182,7 @@ fn set_storage_node_params_works() { // Node created assert_ok!(DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params.clone()) )); @@ -193,7 +199,7 @@ fn set_storage_node_params_works() { // Set node params assert_ok!(DdcNodes::set_node_params( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(updated_params.clone()) )); @@ -204,7 +210,7 @@ fn set_storage_node_params_works() { updated_params.domain.try_into().unwrap(); assert_eq!(updated_storage_node.pub_key, node_pub_key); - assert_eq!(updated_storage_node.provider_id, 1); + assert_eq!(updated_storage_node.provider_id, account_id1.clone()); assert_eq!(updated_storage_node.cluster_id, None); assert_eq!(updated_storage_node.props.host, expected_host); assert_eq!(updated_storage_node.props.domain, expected_domain); @@ -217,7 +223,7 @@ fn set_storage_node_params_works() { // Only node provider can set params assert_noop!( DdcNodes::set_node_params( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account_id2.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params.clone()) ), @@ -228,7 +234,7 @@ fn set_storage_node_params_works() { let node_pub_key_2 = AccountId32::from(bytes_2); let node = Node::::new( NodePubKey::StoragePubKey(node_pub_key_2), - 2u64, + account_id2, NodeParams::StorageParams(storage_node_params), ) .unwrap(); @@ -242,7 +248,7 @@ fn set_storage_node_params_works() { // Storage host length exceeds limit assert_noop!( DdcNodes::set_node_params( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, @@ -260,7 +266,7 @@ fn set_storage_node_params_works() { // Storage domain length exceeds limit assert_noop!( DdcNodes::set_node_params( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, @@ -299,11 +305,13 @@ fn delete_storage_node_works() { grpc_port: 25000u16, p2p_port: 15000u16, }; + let account_id1 = AccountId::from([1; 32]); + let account_id2 = AccountId::from([2; 32]); // Node doesn't exist assert_noop!( DdcNodes::delete_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()) ), Error::::NodeDoesNotExist @@ -311,7 +319,7 @@ fn delete_storage_node_works() { // Create node assert_ok!(DdcNodes::create_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), NodeParams::StorageParams(storage_node_params) )); @@ -319,7 +327,7 @@ fn delete_storage_node_works() { // Only node provider can delete assert_noop!( DdcNodes::delete_node( - RuntimeOrigin::signed(2), + RuntimeOrigin::signed(account_id2), NodePubKey::StoragePubKey(node_pub_key.clone()) ), Error::::OnlyNodeProvider @@ -327,7 +335,7 @@ fn delete_storage_node_works() { // Delete node assert_ok!(DdcNodes::delete_node( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(account_id1), NodePubKey::StoragePubKey(node_pub_key.clone()), )); diff --git a/pallets/ddc-nodes/src/weights.rs b/pallets/ddc-nodes/src/weights.rs index b1c3a3228..917b5c671 100644 --- a/pallets/ddc-nodes/src/weights.rs +++ b/pallets/ddc-nodes/src/weights.rs @@ -1,9 +1,9 @@ //! Autogenerated weights for pallet_ddc_nodes //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `Yahors-MacBook-Pro.local`, CPU: `` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/cere @@ -11,12 +11,13 @@ // pallet // --chain=dev // --execution=wasm -// --pallet=pallet-ddc-nodes +// --wasm-execution=compiled +// --pallet=pallet_ddc_nodes // --extrinsic=* // --steps=50 // --repeat=20 // --template=./.maintain/frame-weight-template.hbs -// --output=pallets/ddc-nodes/src/weights.rs +// --output=pallets/ddc-nodes/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -35,22 +36,26 @@ pub trait WeightInfo { /// Weights for pallet_ddc_nodes using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: DdcNodes StorageNodes (r:1 w:1) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_node() -> Weight { - Weight::from_parts(12_000_000_u64, 0) + Weight::from_parts(21_089_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: DdcStaking Nodes (r:1 w:0) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn delete_node() -> Weight { - Weight::from_parts(16_000_000_u64, 0) + Weight::from_parts(28_053_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcNodes StorageNodes (r:1 w:1) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_node_params() -> Weight { - Weight::from_parts(15_000_000_u64, 0) + Weight::from_parts(26_469_000_u64, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -58,22 +63,26 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: DdcNodes StorageNodes (r:1 w:1) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_node() -> Weight { - Weight::from_parts(12_000_000_u64, 0) + Weight::from_parts(21_089_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcNodes StorageNodes (r:1 w:1) - // Storage: DdcStaking Nodes (r:1 w:0) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn delete_node() -> Weight { - Weight::from_parts(16_000_000_u64, 0) + Weight::from_parts(28_053_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcNodes StorageNodes (r:1 w:1) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_node_params() -> Weight { - Weight::from_parts(15_000_000_u64, 0) + Weight::from_parts(26_469_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/pallets/ddc-payouts/Cargo.toml b/pallets/ddc-payouts/Cargo.toml index e26ad53d2..02eab6699 100644 --- a/pallets/ddc-payouts/Cargo.toml +++ b/pallets/ddc-payouts/Cargo.toml @@ -20,6 +20,7 @@ frame-benchmarking = { workspace = true, optional = true } frame-election-provider-support = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } +hex = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } @@ -39,6 +40,7 @@ substrate-test-utils = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "hex/std", "codec/std", "ddc-primitives/std", "frame-support/std", diff --git a/pallets/ddc-payouts/src/benchmarking.rs b/pallets/ddc-payouts/src/benchmarking.rs index d92f86693..6481dd71e 100644 --- a/pallets/ddc-payouts/src/benchmarking.rs +++ b/pallets/ddc-payouts/src/benchmarking.rs @@ -1,6 +1,6 @@ //! DdcPayouts pallet benchmarking. -use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams}; +use ddc_primitives::{traits::ValidatorVisitor, ClusterId, ClusterParams, ClusterProtocolParams}; pub use frame_benchmarking::{account, benchmarks, whitelist_account}; use frame_system::RawOrigin; use sp_runtime::Perquintill; @@ -14,6 +14,7 @@ const CERE: u128 = 10000000000; fn create_dac_account() -> T::AccountId { let dac_account = create_account::("dac_account", 0, 0); authorize_account::(dac_account.clone()); + T::ValidatorVisitor::setup_validators(vec![dac_account.clone()]); dac_account } @@ -45,14 +46,14 @@ fn create_cluster( cluster_manager_id: T::AccountId, cluster_reserve_id: T::AccountId, cluster_params: ClusterParams, - cluster_gov_params: ClusterGovParams, BlockNumberFor>, + cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, ) { - T::ClusterCreator::create_new_cluster( + T::ClusterCreator::create_cluster( cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params, - cluster_gov_params, + cluster_protocol_params, ) .expect("Cluster is not created"); } @@ -66,30 +67,31 @@ fn create_default_cluster(cluster_id: ClusterId) { erasure_coding_total: 6, replication_total: 3, }; - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { - treasury_share: Perquintill::from_percent(5), - validators_share: Perquintill::from_percent(10), - cluster_reserve_share: Perquintill::from_percent(15), - unit_per_mb_stored: CERE, - unit_per_mb_streamed: CERE, - unit_per_put_request: CERE, - unit_per_get_request: CERE, - ..Default::default() - }; + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = + ClusterProtocolParams { + treasury_share: Perquintill::from_percent(5), + validators_share: Perquintill::from_percent(10), + cluster_reserve_share: Perquintill::from_percent(15), + unit_per_mb_stored: CERE, + unit_per_mb_streamed: CERE, + unit_per_put_request: CERE, + unit_per_get_request: CERE, + ..Default::default() + }; create_cluster::( cluster_id, cluster_manager, cluster_reserve, cluster_params, - cluster_gov_params, + cluster_protocol_params, ); } struct BillingReportParams { cluster_id: ClusterId, era: DdcEra, - state: State, + state: PayoutState, total_customer_charge: CustomerCharge, total_distributed_reward: u128, total_node_usage: NodeUsage, @@ -147,13 +149,13 @@ benchmarks! { verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::Initialized); + assert_eq!(billing_report.state, PayoutState::Initialized); } begin_charging_customers { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::Initialized; + let state = PayoutState::Initialized; let total_customer_charge = CustomerCharge::default(); let total_distributed_reward : u128= 0; let total_node_usage = NodeUsage::default(); @@ -185,7 +187,7 @@ benchmarks! { verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::ChargingCustomers); + assert_eq!(billing_report.state, PayoutState::ChargingCustomers); assert_eq!(billing_report.charging_max_batch_index, max_batch_index); } @@ -194,7 +196,7 @@ benchmarks! { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::ChargingCustomers; + let state = PayoutState::ChargingCustomers; let total_customer_charge = CustomerCharge::default(); let total_distributed_reward : u128 = 0; let total_node_usage = NodeUsage::default(); @@ -221,7 +223,7 @@ benchmarks! { }); let batch_index: BatchIndex = 0; - let payers: Vec<(T::AccountId, CustomerUsage)> = (0..b).map(|i| { + let payers: Vec<(T::AccountId, BucketId, CustomerUsage)> = (0..b).map(|i| { let customer = create_account::("customer", i, i); if b % 2 == 0 { @@ -238,22 +240,23 @@ benchmarks! { number_of_gets: 10, // 10 gets number_of_puts: 5, // 5 puts }; + let bucket_id: BucketId = 1; - (customer, customer_usage) + (customer, bucket_id, customer_usage) }).collect(); - }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payers) + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payers, MMRProof::default()) verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::ChargingCustomers); + assert_eq!(billing_report.state, PayoutState::ChargingCustomers); assert!(billing_report.charging_processed_batches.contains(&batch_index)); } end_charging_customers { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::ChargingCustomers; + let state = PayoutState::ChargingCustomers; let total_customer_charge = CustomerCharge { transfer: 200 * CERE, // price for 200 mb storage: 100 * CERE, // price for 100 mb @@ -292,14 +295,14 @@ benchmarks! { }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era) verify { let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::CustomersChargedWithFees); + assert_eq!(billing_report.state, PayoutState::CustomersChargedWithFees); assert!(billing_report.charging_processed_batches.contains(&charging_max_batch_index)); } begin_rewarding_providers { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::CustomersChargedWithFees; + let state = PayoutState::CustomersChargedWithFees; let total_customer_charge = CustomerCharge { transfer: 200 * CERE, // price for 200 mb storage: 100 * CERE, // price for 100 mb @@ -342,7 +345,7 @@ benchmarks! { }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, max_batch_index, total_node_usage) verify { let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::RewardingProviders); + assert_eq!(billing_report.state, PayoutState::RewardingProviders); assert_eq!(billing_report.rewarding_max_batch_index, max_batch_index); } @@ -351,7 +354,7 @@ benchmarks! { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::RewardingProviders; + let state = PayoutState::RewardingProviders; let total_customer_charge = CustomerCharge { transfer: (200 * CERE).saturating_mul(b.into()), // price for 200 mb per customer storage: (100 * CERE).saturating_mul(b.into()), // price for 100 mb per customer @@ -361,7 +364,7 @@ benchmarks! { let total_distributed_reward : u128 = 0; let total_node_usage = NodeUsage { transferred_bytes: 200000000u64.saturating_mul(b.into()), // 200 mb per provider - stored_bytes: 100000000u64.saturating_mul(b.into()), // 100 mb per provider + stored_bytes: 100000000i64.saturating_mul(b.into()), // 100 mb per provider number_of_gets: 10u64.saturating_mul(b.into()), // 10 gets per provider number_of_puts: 10u64.saturating_mul(b.into()), // 5 puts per provider }; @@ -405,18 +408,18 @@ benchmarks! { (provider, node_usage) }).collect(); - }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payees) + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payees, MMRProof::default()) verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::RewardingProviders); + assert_eq!(billing_report.state, PayoutState::RewardingProviders); assert!(billing_report.rewarding_processed_batches.contains(&batch_index)); } end_rewarding_providers { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::RewardingProviders; + let state = PayoutState::RewardingProviders; let total_customer_charge = CustomerCharge { transfer: 200 * CERE, // price for 200 mb storage: 100 * CERE, // price for 100 mb @@ -458,13 +461,13 @@ benchmarks! { verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::ProvidersRewarded); + assert_eq!(billing_report.state, PayoutState::ProvidersRewarded); } end_billing_report { let cluster_id = ClusterId::from([1; 20]); let era : DdcEra = 1; - let state = State::ProvidersRewarded; + let state = PayoutState::ProvidersRewarded; let total_customer_charge = CustomerCharge { transfer: 200 * CERE, // price for 200 mb storage: 100 * CERE, // price for 100 mb @@ -506,7 +509,7 @@ benchmarks! { verify { assert!(ActiveBillingReports::::contains_key(cluster_id, era)); let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); - assert_eq!(billing_report.state, State::Finalized); + assert_eq!(billing_report.state, PayoutState::Finalized); } } diff --git a/pallets/ddc-payouts/src/lib.rs b/pallets/ddc-payouts/src/lib.rs index 35b58a6c1..e7ffc16d0 100644 --- a/pallets/ddc-payouts/src/lib.rs +++ b/pallets/ddc-payouts/src/lib.rs @@ -15,6 +15,7 @@ #![recursion_limit = "256"] pub mod weights; + use crate::weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -27,13 +28,17 @@ mod tests; use ddc_primitives::{ traits::{ - cluster::{ClusterCreator as ClusterCreatorType, ClusterVisitor as ClusterVisitorType}, + bucket::BucketVisitor as BucketVisitorType, + cluster::{ClusterCreator as ClusterCreatorType, ClusterProtocol as ClusterProtocolType}, customer::{ CustomerCharger as CustomerChargerType, CustomerDepositor as CustomerDepositorType, }, + node::NodeVisitor as NodeVisitorType, pallet::PalletVisitor as PalletVisitorType, + payout::PayoutVisitor, }, - ClusterId, DdcEra, MILLICENTS, + BatchIndex, BucketId, BucketVisitorError, ClusterId, CustomerUsage, DdcEra, MMRProof, + NodeUsage, PayoutError, PayoutState, MAX_PAYOUT_BATCH_COUNT, MAX_PAYOUT_BATCH_SIZE, MILLICENTS, }; use frame_election_provider_support::SortedListProvider; use frame_support::{ @@ -45,29 +50,9 @@ use frame_support::{ }; use frame_system::pallet_prelude::*; pub use pallet::*; -use sp_runtime::{traits::Convert, PerThing, Perquintill}; +use scale_info::prelude::string::String; +use sp_runtime::{traits::Convert, AccountId32, PerThing, Perquintill}; use sp_std::prelude::*; - -type BatchIndex = u16; - -/// Stores usage of customers -#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, Default, Clone)] -pub struct CustomerUsage { - pub transferred_bytes: u64, - pub stored_bytes: u64, - pub number_of_puts: u64, - pub number_of_gets: u64, -} - -/// Stores usage of node provider -#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, Default, Clone)] -pub struct NodeUsage { - pub transferred_bytes: u64, - pub stored_bytes: u64, - pub number_of_puts: u64, - pub number_of_gets: u64, -} - /// Stores reward in tokens(units) of node provider as per NodeUsage #[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, Default, Clone)] pub struct NodeReward { @@ -104,13 +89,14 @@ pub type VoteScoreOf = >>::Score; parameter_types! { - pub MaxBatchesCount: u16 = 1000; + pub MaxBatchesCount: u16 = MAX_PAYOUT_BATCH_COUNT; pub MaxDust: u128 = MILLICENTS; - pub MaxBatchSize: u16 = 1000; + pub MaxBatchSize: u16 = MAX_PAYOUT_BATCH_SIZE; } #[frame_support::pallet] pub mod pallet { + use ddc_primitives::traits::ValidatorVisitor; use frame_support::PalletId; use sp_io::hashing::blake2_128; use sp_runtime::traits::{AccountIdConversion, Zero}; @@ -133,13 +119,17 @@ pub mod pallet { type PalletId: Get; type Currency: LockableCurrency>; type CustomerCharger: CustomerChargerType; + type BucketVisitor: BucketVisitorType; + type NodeVisitor: NodeVisitorType; type CustomerDepositor: CustomerDepositorType; type TreasuryVisitor: PalletVisitorType; - type ClusterVisitor: ClusterVisitorType; + type ClusterProtocol: ClusterProtocolType>; type NominatorsAndValidatorsList: SortedListProvider; type ClusterCreator: ClusterCreatorType>; type WeightInfo: WeightInfo; type VoteScoreToU64: Convert, u64>; + type ValidatorVisitor: ValidatorVisitor; + type AccountIdConverter: From + Into; } #[pallet::event] @@ -158,6 +148,7 @@ pub mod pallet { era: DdcEra, batch_index: BatchIndex, customer_id: T::AccountId, + bucket_id: BucketId, amount: u128, }, ChargeFailed { @@ -165,6 +156,7 @@ pub mod pallet { era: DdcEra, batch_index: BatchIndex, customer_id: T::AccountId, + bucket_id: BucketId, charged: u128, expected_to_charge: u128, }, @@ -173,6 +165,7 @@ pub mod pallet { era: DdcEra, batch_index: BatchIndex, customer_id: T::AccountId, + bucket_id: BucketId, amount: u128, }, ChargingFinished { @@ -206,6 +199,12 @@ pub mod pallet { rewarded: u128, expected_to_reward: u128, }, + ValidatorRewarded { + cluster_id: ClusterId, + era: DdcEra, + validator_id: T::AccountId, + amount: u128, + }, NotDistributedReward { cluster_id: ClusterId, era: DdcEra, @@ -254,9 +253,16 @@ pub mod pallet { BoundedVecOverflow, ArithmeticOverflow, NotExpectedClusterState, + NotExpectedBucketState, BatchSizeIsOutOfBounds, ScoreRetrievalError, BadRequest, + BatchValidationFailed, + NoBucketWithId, + NotBucketOwner, + IncorrectClusterId, + ClusterProtocolParamsNotSet, + TotalStoredBytesLessThanZero, } #[pallet::storage] @@ -287,7 +293,7 @@ pub mod pallet { #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] #[scale_info(skip_type_params(T))] pub struct BillingReport { - pub state: State, + pub state: PayoutState, pub vault: T::AccountId, pub start_era: i64, pub end_era: i64, @@ -305,7 +311,7 @@ pub mod pallet { impl Default for BillingReport { fn default() -> Self { Self { - state: State::default(), + state: PayoutState::default(), vault: T::PalletId::get().into_account_truncating(), start_era: Zero::zero(), end_era: Zero::zero(), @@ -336,6 +342,8 @@ pub mod pallet { #[pallet::call] impl Pallet { + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::set_authorised_caller())] pub fn set_authorised_caller( @@ -361,7 +369,7 @@ pub mod pallet { end_era: i64, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); ensure!( ActiveBillingReports::::try_get(cluster_id, era).is_err(), @@ -372,7 +380,7 @@ pub mod pallet { let billing_report = BillingReport:: { vault: Self::account_id(), - state: State::Initialized, + state: PayoutState::Initialized, start_era, end_era, ..Default::default() @@ -384,6 +392,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::begin_charging_customers())] pub fn begin_charging_customers( @@ -393,17 +403,18 @@ pub mod pallet { max_batch_index: BatchIndex, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); // + // todo! need to refactor this ensure!(max_batch_index < MaxBatchesCount::get(), Error::::BatchIndexOverflow); let mut billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; - ensure!(billing_report.state == State::Initialized, Error::::NotExpectedState); + ensure!(billing_report.state == PayoutState::Initialized, Error::::NotExpectedState); billing_report.charging_max_batch_index = max_batch_index; - billing_report.state = State::ChargingCustomers; + billing_report.state = PayoutState::ChargingCustomers; ActiveBillingReports::::insert(cluster_id, era, billing_report); Self::deposit_event(Event::::ChargingStarted { cluster_id, era }); @@ -411,6 +422,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // + pass values by reference PayoutProcessor trait #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::send_charging_customers_batch(payers.len().saturated_into()))] pub fn send_charging_customers_batch( @@ -418,10 +431,11 @@ pub mod pallet { cluster_id: ClusterId, era: DdcEra, batch_index: BatchIndex, - payers: Vec<(T::AccountId, CustomerUsage)>, + payers: Vec<(T::AccountId, BucketId, CustomerUsage)>, + batch_proof: MMRProof, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); ensure!( !payers.is_empty() && payers.len() <= MaxBatchSize::get() as usize, @@ -431,21 +445,40 @@ pub mod pallet { let billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; - ensure!(billing_report.state == State::ChargingCustomers, Error::::NotExpectedState); + ensure!( + billing_report.state == PayoutState::ChargingCustomers, + Error::::NotExpectedState + ); + ensure!( billing_report.charging_max_batch_index >= batch_index, Error::::BatchIndexIsOutOfRange ); + ensure!( !billing_report.charging_processed_batches.contains(&batch_index), Error::::BatchIndexAlreadyProcessed ); + ensure!( + T::ValidatorVisitor::is_customers_batch_valid( + cluster_id, + era, + batch_index, + &payers, + &batch_proof + ), + Error::::BatchValidationFailed + ); + let mut updated_billing_report = billing_report; - for payer in payers { + for (customer_id, bucket_id, customer_usage) in payers { + log::info!("🏭send_charging_customers_batch get_customer_charge customer_id: {:?} - bucket_id: {:?} - era:{:?} - cluster-id:{:?}", Self::get_account_id_string(customer_id.clone()), bucket_id, era, cluster_id); let mut customer_charge = get_customer_charge::( - cluster_id, - &payer.1, + &cluster_id, + &customer_usage, + bucket_id, + &customer_id, updated_billing_report.start_era, updated_billing_report.end_era, )?; @@ -458,10 +491,12 @@ pub mod pallet { })() .ok_or(Error::::ArithmeticOverflow)?; - let customer_id = payer.0.clone(); let amount_actually_charged = match T::CustomerCharger::charge_content_owner( + &cluster_id, + bucket_id, customer_id.clone(), updated_billing_report.vault.clone(), + &customer_usage, total_customer_charge, ) { Ok(actually_charged) => actually_charged, @@ -498,6 +533,7 @@ pub mod pallet { era, batch_index, customer_id: customer_id.clone(), + bucket_id, amount: debt, }); @@ -506,6 +542,7 @@ pub mod pallet { era, batch_index, customer_id, + bucket_id, charged: amount_actually_charged, expected_to_charge: total_customer_charge, }); @@ -525,6 +562,7 @@ pub mod pallet { era, batch_index, customer_id, + bucket_id, amount: total_customer_charge, }); } @@ -564,6 +602,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::end_charging_customers())] pub fn end_charging_customers( @@ -572,13 +612,16 @@ pub mod pallet { era: DdcEra, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); let mut billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; - ensure!(billing_report.state == State::ChargingCustomers, Error::::NotExpectedState); - validate_batches::( + ensure!( + billing_report.state == PayoutState::ChargingCustomers, + Error::::NotExpectedState + ); + Self::validate_batches( &billing_report.charging_processed_batches, &billing_report.charging_max_batch_index, )?; @@ -586,8 +629,8 @@ pub mod pallet { Self::deposit_event(Event::::ChargingFinished { cluster_id, era }); // deduct fees - let fees = T::ClusterVisitor::get_fees_params(&cluster_id) - .map_err(|_| Error::::NotExpectedClusterState)?; + let fees = T::ClusterProtocol::get_fees_params(&cluster_id) + .map_err(|_| Error::::ClusterProtocolParamsNotSet)?; let total_customer_charge = (|| -> Option { billing_report @@ -621,7 +664,7 @@ pub mod pallet { charge_cluster_reserve_fees::( cluster_reserve_fee, &billing_report.vault, - &T::ClusterVisitor::get_reserve_account_id(&cluster_id) + &T::ClusterProtocol::get_reserve_account_id(&cluster_id) .map_err(|_| Error::::NotExpectedClusterState)?, )?; Self::deposit_event(Event::::ClusterReserveFeesCollected { @@ -632,7 +675,7 @@ pub mod pallet { } if validators_fee > 0 { - charge_validator_fees::(validators_fee, &billing_report.vault)?; + charge_validator_fees::(validators_fee, &billing_report.vault, cluster_id, era)?; Self::deposit_event(Event::::ValidatorFeesCollected { cluster_id, era, @@ -657,12 +700,14 @@ pub mod pallet { total_left_from_one * billing_report.total_customer_charge.gets; } - billing_report.state = State::CustomersChargedWithFees; + billing_report.state = PayoutState::CustomersChargedWithFees; ActiveBillingReports::::insert(cluster_id, era, billing_report); Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::begin_rewarding_providers())] pub fn begin_rewarding_providers( @@ -673,7 +718,7 @@ pub mod pallet { total_node_usage: NodeUsage, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); ensure!(max_batch_index < MaxBatchesCount::get(), Error::::BatchIndexOverflow); @@ -681,13 +726,13 @@ pub mod pallet { .map_err(|_| Error::::BillingReportDoesNotExist)?; ensure!( - billing_report.state == State::CustomersChargedWithFees, + billing_report.state == PayoutState::CustomersChargedWithFees, Error::::NotExpectedState ); billing_report.total_node_usage = total_node_usage; billing_report.rewarding_max_batch_index = max_batch_index; - billing_report.state = State::RewardingProviders; + billing_report.state = PayoutState::RewardingProviders; ActiveBillingReports::::insert(cluster_id, era, billing_report); Self::deposit_event(Event::::RewardingStarted { cluster_id, era }); @@ -695,6 +740,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // + pass values by reference PayoutProcessor trait #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::send_rewarding_providers_batch(payees.len().saturated_into()))] pub fn send_rewarding_providers_batch( @@ -702,10 +749,12 @@ pub mod pallet { cluster_id: ClusterId, era: DdcEra, batch_index: BatchIndex, - payees: Vec<(T::AccountId, NodeUsage)>, + payees: Vec<(T::AccountId, NodeUsage)>, /* todo! we need to pass NodePubKey inside + * NodeUsage and more provider_id */ + batch_proof: MMRProof, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); ensure!( !payees.is_empty() && payees.len() <= MaxBatchSize::get() as usize, @@ -716,7 +765,7 @@ pub mod pallet { .map_err(|_| Error::::BillingReportDoesNotExist)?; ensure!( - billing_report.state == State::RewardingProviders, + billing_report.state == PayoutState::RewardingProviders, Error::::NotExpectedState ); ensure!( @@ -728,11 +777,38 @@ pub mod pallet { Error::::BatchIndexAlreadyProcessed ); + ensure!( + T::ValidatorVisitor::is_providers_batch_valid( + cluster_id, + era, + batch_index, + &payees, + &batch_proof + ), + Error::::BatchValidationFailed + ); + let max_dust = MaxDust::get().saturated_into::>(); let mut updated_billing_report = billing_report.clone(); - for payee in payees { + for (node_provider_id, delta_node_usage) in payees { + // todo! deduce node_provider_id from delta_node_usage.node_id + // todo! get T::NodeVisitor::get_total_usage(delta_node_usage.node_id).stored_bytes + let mut total_node_stored_bytes: i64 = 0; + + total_node_stored_bytes = total_node_stored_bytes + .checked_add(delta_node_usage.stored_bytes) + .ok_or(Error::::ArithmeticOverflow)? + .max(0); + + let node_usage = NodeUsage { + stored_bytes: total_node_stored_bytes, + transferred_bytes: delta_node_usage.transferred_bytes, + number_of_puts: delta_node_usage.number_of_puts, + number_of_gets: delta_node_usage.number_of_gets, + }; + let node_reward = get_node_reward( - &payee.1, + &node_usage, &billing_report.total_node_usage, &billing_report.total_customer_charge, ) @@ -746,7 +822,6 @@ pub mod pallet { })() .ok_or(Error::::ArithmeticOverflow)?; - let node_provider_id = payee.0; let mut reward_ = amount_to_reward; let mut reward: BalanceOf = amount_to_reward.saturated_into::>(); if amount_to_reward > 0 { @@ -777,6 +852,8 @@ pub mod pallet { ExistenceRequirement::AllowDeath, )?; + // todo! update total usage of node with NodeManager + reward_ = reward.saturated_into::(); updated_billing_report.total_distributed_reward = updated_billing_report @@ -805,6 +882,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::end_rewarding_providers())] pub fn end_rewarding_providers( @@ -813,17 +892,17 @@ pub mod pallet { era: DdcEra, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); let mut billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; ensure!( - billing_report.state == State::RewardingProviders, + billing_report.state == PayoutState::RewardingProviders, Error::::NotExpectedState ); - validate_batches::( + Self::validate_batches( &billing_report.rewarding_processed_batches, &billing_report.rewarding_max_batch_index, )?; @@ -848,7 +927,7 @@ pub mod pallet { }); } - billing_report.state = State::ProvidersRewarded; + billing_report.state = PayoutState::ProvidersRewarded; ActiveBillingReports::::insert(cluster_id, era, billing_report); Self::deposit_event(Event::::RewardingFinished { cluster_id, era }); @@ -856,6 +935,8 @@ pub mod pallet { Ok(()) } + // todo! remove extrensics from payout pallet and factor the extrensics implementation into + // PayoutProcessor trait #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::end_billing_report())] pub fn end_billing_report( @@ -864,16 +945,19 @@ pub mod pallet { era: DdcEra, ) -> DispatchResult { let caller = ensure_signed(origin)?; - ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!(T::ValidatorVisitor::is_ocw_validator(caller), Error::::Unauthorised); let mut billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; - ensure!(billing_report.state == State::ProvidersRewarded, Error::::NotExpectedState); + ensure!( + billing_report.state == PayoutState::ProvidersRewarded, + Error::::NotExpectedState + ); billing_report.charging_processed_batches.clear(); billing_report.rewarding_processed_batches.clear(); - billing_report.state = State::Finalized; + billing_report.state = PayoutState::Finalized; ActiveBillingReports::::insert(cluster_id, era, billing_report); Self::deposit_event(Event::::BillingReportFinalized { cluster_id, era }); @@ -935,6 +1019,8 @@ pub mod pallet { fn charge_validator_fees( validators_fee: u128, vault: &T::AccountId, + cluster_id: ClusterId, + era: DdcEra, ) -> DispatchResult { let stakers = get_current_exposure_ratios::()?; @@ -947,6 +1033,13 @@ pub mod pallet { amount_to_deduct.saturated_into::>(), ExistenceRequirement::AllowDeath, )?; + + pallet::Pallet::deposit_event(Event::::ValidatorRewarded { + cluster_id, + era, + validator_id: staker_id.clone(), + amount: amount_to_deduct, + }); } Ok(()) @@ -985,14 +1078,16 @@ pub mod pallet { } fn get_customer_charge( - cluster_id: ClusterId, + cluster_id: &ClusterId, usage: &CustomerUsage, + bucket_id: BucketId, + customer_id: &T::AccountId, start_era: i64, end_era: i64, ) -> Result> { let mut total = CustomerCharge::default(); - let pricing = T::ClusterVisitor::get_pricing_params(&cluster_id) + let pricing = T::ClusterProtocol::get_pricing_params(cluster_id) .map_err(|_| Error::::NotExpectedClusterState)?; total.transfer = (|| -> Option { @@ -1008,9 +1103,19 @@ pub mod pallet { let fraction_of_month = Perquintill::from_rational(duration_seconds as u64, seconds_in_month as u64); + let mut total_stored_bytes: i64 = + T::BucketVisitor::get_total_customer_usage(cluster_id, bucket_id, customer_id) + .map_err(Into::>::into)? + .map_or(0, |customer_usage| customer_usage.stored_bytes); + + total_stored_bytes = total_stored_bytes + .checked_add(usage.stored_bytes) + .ok_or(Error::::ArithmeticOverflow)? + .max(0); + total.storage = fraction_of_month * (|| -> Option { - (usage.stored_bytes as u128) + (total_stored_bytes as u128) .checked_mul(pricing.unit_per_mb_stored)? .checked_div(byte_unit::MEBIBYTE) })() @@ -1027,20 +1132,14 @@ pub mod pallet { Ok(total) } - fn validate_batches( - batches: &BoundedBTreeSet, - max_batch_index: &BatchIndex, - ) -> DispatchResult { - // Check if the Vec contains all integers between 1 and rewarding_max_batch_index - ensure!(!batches.is_empty(), Error::::BatchesMissed); - - ensure!((*max_batch_index + 1) as usize == batches.len(), Error::::BatchesMissed); - - for index in 0..*max_batch_index + 1 { - ensure!(batches.contains(&index), Error::::BatchesMissed); + impl From for Error { + fn from(error: BucketVisitorError) -> Self { + match error { + BucketVisitorError::NoBucketWithId => Error::::NoBucketWithId, + BucketVisitorError::NotBucketOwner => Error::::NotBucketOwner, + BucketVisitorError::IncorrectClusterId => Error::::IncorrectClusterId, + } } - - Ok(()) } #[pallet::genesis_config] @@ -1086,11 +1185,223 @@ pub mod pallet { } } + impl PayoutVisitor for Pallet { + fn begin_billing_report( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + start_era: i64, + end_era: i64, + ) -> DispatchResult { + log::info!( + "🏭begin_billing_report called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::begin_billing_report(origin, cluster_id, era_id, start_era, end_era) + } + + fn begin_charging_customers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + ) -> DispatchResult { + log::info!( + "🏭begin_charging_customers called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::begin_charging_customers(origin, cluster_id, era_id, max_batch_index) + } + + fn send_charging_customers_batch( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payers: &[(T::AccountId, BucketId, CustomerUsage)], + batch_proof: MMRProof, + ) -> DispatchResult { + log::info!( + "🏭send_charging_customers_batch called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::send_charging_customers_batch( + origin, + cluster_id, + era_id, + batch_index, + (*payers).to_vec(), + batch_proof, + ) + } + + fn end_charging_customers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + log::info!( + "🏭end_charging_customers called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::end_charging_customers(origin, cluster_id, era_id) + } + + fn begin_rewarding_providers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + total_node_usage: NodeUsage, + ) -> DispatchResult { + log::info!( + "🏭begin_rewarding_providers called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::begin_rewarding_providers( + origin, + cluster_id, + era_id, + max_batch_index, + total_node_usage, + ) + } + + fn send_rewarding_providers_batch( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payees: &[(T::AccountId, NodeUsage)], + batch_proof: MMRProof, + ) -> DispatchResult { + log::info!( + "🏭send_rewarding_providers_batch called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::send_rewarding_providers_batch( + origin, + cluster_id, + era_id, + batch_index, + (*payees).to_vec(), + batch_proof, + ) + } + + fn end_rewarding_providers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + log::info!( + "🏭end_rewarding_providers called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::end_rewarding_providers(origin, cluster_id, era_id) + } + + fn end_billing_report( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + log::info!( + "🏭end_billing_report called by: {:?}", + Self::get_account_id_string(origin.clone()) + ); + let origin = frame_system::RawOrigin::Signed(origin).into(); + Self::end_billing_report(origin, cluster_id, era_id) + } + + fn get_billing_report_status(cluster_id: &ClusterId, era: DdcEra) -> PayoutState { + let billing_report = ActiveBillingReports::::get(cluster_id, era); + + match billing_report { + Some(report) => report.state, + None => PayoutState::NotInitialized, // Return NotInitialized if entry doesn't exist + } + } + + fn all_customer_batches_processed(cluster_id: &ClusterId, era_id: DdcEra) -> bool { + let billing_report = match ActiveBillingReports::::try_get(cluster_id, era_id) { + Ok(report) => report, + Err(_) => return false, /* Return false if there's any error (e.g., + * BillingReportDoesNotExist) */ + }; + + Self::validate_batches( + &billing_report.charging_processed_batches, + &billing_report.charging_max_batch_index, + ) + .is_ok() + } + + fn all_provider_batches_processed(cluster_id: &ClusterId, era_id: DdcEra) -> bool { + let billing_report = match ActiveBillingReports::::try_get(cluster_id, era_id) { + Ok(report) => report, + Err(_) => return false, /* Return false if there's any error (e.g., + * BillingReportDoesNotExist) */ + }; + + Self::validate_batches( + &billing_report.rewarding_processed_batches, + &billing_report.rewarding_max_batch_index, + ) + .is_ok() + } + + fn get_next_customer_batch_for_payment( + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Result, PayoutError> { + let billing_report = ActiveBillingReports::::try_get(cluster_id, era_id) + .map_err(|_| PayoutError::BillingReportDoesNotExist)?; + + for batch_index in 0..=billing_report.charging_max_batch_index { + if !billing_report.charging_processed_batches.contains(&batch_index) { + return Ok(Some(batch_index)); + } + } + + Ok(None) + } + + fn get_next_provider_batch_for_payment( + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Result, PayoutError> { + let billing_report = ActiveBillingReports::::try_get(cluster_id, era_id) + .map_err(|_| PayoutError::BillingReportDoesNotExist)?; + + for batch_index in 0..=billing_report.rewarding_max_batch_index { + if !billing_report.rewarding_processed_batches.contains(&batch_index) { + return Ok(Some(batch_index)); + } + } + + Ok(None) + } + } + impl Pallet { pub fn account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } + pub fn get_account_id_string(caller: T::AccountId) -> String { + let account_id: T::AccountIdConverter = caller.into(); + let account_id_32: AccountId32 = account_id.into(); + let account_ref: &[u8; 32] = account_id_32.as_ref(); + hex::encode(account_ref) + } pub fn sub_account_id(cluster_id: ClusterId, era: DdcEra) -> T::AccountId { let mut bytes = Vec::new(); bytes.extend_from_slice(&cluster_id[..]); @@ -1102,5 +1413,21 @@ pub mod pallet { // be fulfilled with trailing zeros. T::PalletId::get().into_sub_account_truncating(hash) } + + pub(crate) fn validate_batches( + batches: &BoundedBTreeSet, + max_batch_index: &BatchIndex, + ) -> DispatchResult { + // Check if the Vec contains all integers between 1 and rewarding_max_batch_index + ensure!(!batches.is_empty(), Error::::BatchesMissed); + + ensure!((*max_batch_index + 1) as usize == batches.len(), Error::::BatchesMissed); + + for index in 0..*max_batch_index + 1 { + ensure!(batches.contains(&index), Error::::BatchesMissed); + } + + Ok(()) + } } } diff --git a/pallets/ddc-payouts/src/mock.rs b/pallets/ddc-payouts/src/mock.rs index 4ea47bbc0..e309b5cb4 100644 --- a/pallets/ddc-payouts/src/mock.rs +++ b/pallets/ddc-payouts/src/mock.rs @@ -4,33 +4,40 @@ use ddc_primitives::{ traits::{ - cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + bucket::BucketVisitor, + cluster::{ClusterCreator, ClusterProtocol}, customer::{CustomerCharger, CustomerDepositor}, + node::NodeVisitor, pallet::PalletVisitor, + ClusterQuery, ValidatorVisitor, }, - ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterParams, ClusterPricingParams, - NodeType, DOLLARS, + BucketVisitorError, ClusterBondingParams, ClusterFeesParams, ClusterParams, + ClusterPricingParams, ClusterProtocolParams, ClusterStatus, NodeParams, NodePubKey, NodeType, + DOLLARS, }; use frame_election_provider_support::SortedListProvider; use frame_support::{ construct_runtime, parameter_types, - traits::{ConstU32, ConstU64, Everything, Randomness}, + traits::{ConstU32, ConstU64, Everything, ExistenceRequirement, Randomness}, weights::constants::RocksDbWeight, PalletId, }; use frame_system::mocking::{MockBlock, MockUncheckedExtrinsic}; use sp_core::H256; use sp_io::TestExternalities; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; use sp_runtime::{ - traits::{BlakeTwo256, Identity, IdentityLookup}, - BuildStorage, DispatchError, Perquintill, TryRuntimeError, + traits::{BlakeTwo256, IdentifyAccount, Identity, IdentityLookup, Verify}, + BuildStorage, DispatchError, MultiSignature, Perquintill, }; use sp_std::prelude::*; use crate::{self as pallet_ddc_payouts, *}; +pub type Signature = MultiSignature; /// The AccountId alias in this test module. -pub type AccountId = u128; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u128; @@ -107,6 +114,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = (); type RuntimeHoldReason = (); @@ -121,33 +129,111 @@ impl crate::pallet::Config for Test { type PalletId = PayoutsPalletId; type Currency = Balances; type CustomerCharger = TestCustomerCharger; + type BucketVisitor = TestBucketVisitor; type CustomerDepositor = TestCustomerDepositor; - type ClusterVisitor = TestClusterVisitor; + type ClusterProtocol = TestClusterProtocol; type TreasuryVisitor = TestTreasuryVisitor; type NominatorsAndValidatorsList = TestValidatorVisitor; type ClusterCreator = TestClusterCreator; type VoteScoreToU64 = Identity; type WeightInfo = (); + type ValidatorVisitor = MockValidatorVisitor; + type NodeVisitor = MockNodeVisitor; + type AccountIdConverter = AccountId; +} + +pub struct MockNodeVisitor; +impl NodeVisitor for MockNodeVisitor +where + ::AccountId: From, +{ + fn get_total_usage(_node_pub_key: &NodePubKey) -> Result, DispatchError> { + unimplemented!() + } + fn get_cluster_id(_node_pub_key: &NodePubKey) -> Result, DispatchError> { + unimplemented!() + } + fn exists(_node_pub_key: &NodePubKey) -> bool { + unimplemented!() + } + fn get_node_provider_id(_node_pub_key: &NodePubKey) -> Result { + unimplemented!() + } + fn get_node_params(_node_pub_key: &NodePubKey) -> Result { + unimplemented!() + } +} + +pub struct MockValidatorVisitor; +impl ValidatorVisitor for MockValidatorVisitor +where + ::AccountId: From, +{ + fn setup_validators(_validators: Vec) { + unimplemented!() + } + fn is_ocw_validator(caller: T::AccountId) -> bool { + let account_id: [u8; 32] = [123; 32]; + let dac: [u8; 32] = DAC_ACCOUNT_ID; + let validators = [ + T::AccountId::decode(&mut &dac[..]).unwrap(), + T::AccountId::decode(&mut &account_id[..]).unwrap(), + ]; + validators.contains(&caller) + } + fn is_customers_batch_valid( + _cluster_id: ClusterId, + _era: DdcEra, + _batch_index: BatchIndex, + _payers: &[(T::AccountId, BucketId, CustomerUsage)], + _batch_proof: &MMRProof, + ) -> bool { + true + } + + fn is_providers_batch_valid( + _cluster_id: ClusterId, + _era: DdcEra, + _batch_index: BatchIndex, + _payees: &[(T::AccountId, NodeUsage)], + _batch_proof: &MMRProof, + ) -> bool { + true + } +} + +pub struct TestBucketVisitor; +impl BucketVisitor for TestBucketVisitor { + fn get_total_customer_usage( + _cluster_id: &ClusterId, + _bucket_id: BucketId, + _content_owner: &T::AccountId, + ) -> Result, BucketVisitorError> { + Ok(None) + } } pub struct TestCustomerCharger; impl CustomerCharger for TestCustomerCharger { fn charge_content_owner( + _cluster_id: &ClusterId, + _bucket_id: BucketId, content_owner: T::AccountId, billing_vault: T::AccountId, + _customer_usage: &CustomerUsage, amount: u128, ) -> Result { let mut amount_to_charge = amount; - let mut temp = ACCOUNT_ID_1.to_ne_bytes(); + let mut temp: [u8; 32] = ACCOUNT_ID_1; let account_1 = T::AccountId::decode(&mut &temp[..]).unwrap(); - temp = ACCOUNT_ID_2.to_ne_bytes(); + temp = ACCOUNT_ID_2; let account_2 = T::AccountId::decode(&mut &temp[..]).unwrap(); - temp = ACCOUNT_ID_3.to_ne_bytes(); + temp = ACCOUNT_ID_3; let account_3 = T::AccountId::decode(&mut &temp[..]).unwrap(); - temp = ACCOUNT_ID_4.to_ne_bytes(); + temp = ACCOUNT_ID_4; let account_4 = T::AccountId::decode(&mut &temp[..]).unwrap(); - temp = ACCOUNT_ID_5.to_ne_bytes(); + temp = ACCOUNT_ID_5; let account_5 = T::AccountId::decode(&mut &temp[..]).unwrap(); if content_owner == account_1 || @@ -179,21 +265,21 @@ impl CustomerCharger for TestCustomerCharger { } } -pub const ACCOUNT_ID_1: AccountId = 1; -pub const ACCOUNT_ID_2: AccountId = 2; -pub const ACCOUNT_ID_3: AccountId = 3; -pub const ACCOUNT_ID_4: AccountId = 4; -pub const ACCOUNT_ID_5: AccountId = 5; -pub const ACCOUNT_ID_6: AccountId = 6; -pub const ACCOUNT_ID_7: AccountId = 7; +pub const ACCOUNT_ID_1: [u8; 32] = [1; 32]; +pub const ACCOUNT_ID_2: [u8; 32] = [2; 32]; +pub const ACCOUNT_ID_3: [u8; 32] = [3; 32]; +pub const ACCOUNT_ID_4: [u8; 32] = [4; 32]; +pub const ACCOUNT_ID_5: [u8; 32] = [5; 32]; +pub const ACCOUNT_ID_6: [u8; 32] = [6; 32]; +pub const ACCOUNT_ID_7: [u8; 32] = [7; 32]; pub struct TestClusterCreator; impl ClusterCreator for TestClusterCreator { - fn create_new_cluster( + fn create_cluster( _cluster_id: ClusterId, _cluster_manager_id: T::AccountId, _cluster_reserve_id: T::AccountId, _cluster_params: ClusterParams, - _cluster_gov_params: ClusterGovParams>, + _cluster_protocol_params: ClusterProtocolParams>, ) -> DispatchResult { Ok(()) } @@ -209,11 +295,12 @@ impl CustomerDepositor for TestCustomerDepositor { } } -pub const RESERVE_ACCOUNT_ID: AccountId = 999; -pub const TREASURY_ACCOUNT_ID: AccountId = 888; -pub const VALIDATOR1_ACCOUNT_ID: AccountId = 111; -pub const VALIDATOR2_ACCOUNT_ID: AccountId = 222; -pub const VALIDATOR3_ACCOUNT_ID: AccountId = 333; +pub const DAC_ACCOUNT_ID: [u8; 32] = [2; 32]; +pub const RESERVE_ACCOUNT_ID: [u8; 32] = [9; 32]; +pub const TREASURY_ACCOUNT_ID: [u8; 32] = [8; 32]; +pub const VALIDATOR1_ACCOUNT_ID: [u8; 32] = [111; 32]; +pub const VALIDATOR2_ACCOUNT_ID: [u8; 32] = [222; 32]; +pub const VALIDATOR3_ACCOUNT_ID: [u8; 32] = [250; 32]; pub const VALIDATOR1_SCORE: u64 = 30; pub const VALIDATOR2_SCORE: u64 = 45; @@ -303,14 +390,13 @@ pub const PRICING_FEES_ZERO: ClusterFeesParams = ClusterFeesParams { pub struct TestTreasuryVisitor; impl PalletVisitor for TestTreasuryVisitor { fn get_account_id() -> T::AccountId { - let reserve_account = TREASURY_ACCOUNT_ID.to_ne_bytes(); + let reserve_account: [u8; 32] = TREASURY_ACCOUNT_ID; T::AccountId::decode(&mut &reserve_account[..]).unwrap() } } -fn create_account_id_from_u128(id: u128) -> T::AccountId { - let bytes = id.to_ne_bytes(); - T::AccountId::decode(&mut &bytes[..]).unwrap() +fn create_account_id_from_u128(id: [u8; 32]) -> T::AccountId { + T::AccountId::decode(&mut &id[..]).unwrap() } pub struct TestValidatorVisitor(sp_std::marker::PhantomData); @@ -415,50 +501,81 @@ pub fn get_pricing(cluster_id: &ClusterId) -> ClusterPricingParams { } } -pub struct TestClusterVisitor; -impl ClusterVisitor for TestClusterVisitor { - fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { - Ok(()) +pub struct TestClusterProtocol; +impl ClusterQuery for TestClusterProtocol { + fn cluster_exists(_cluster_id: &ClusterId) -> bool { + true } - fn get_bond_size( + + fn get_cluster_status(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn get_manager_and_reserve_id( _cluster_id: &ClusterId, - _node_type: NodeType, - ) -> Result { + ) -> Result<(T::AccountId, T::AccountId), DispatchError> { + unimplemented!() + } +} + +impl ClusterProtocol> for TestClusterProtocol { + fn get_bond_size(_cluster_id: &ClusterId, _node_type: NodeType) -> Result { Ok(10) } + fn get_chill_delay( _cluster_id: &ClusterId, _node_type: NodeType, - ) -> Result, ClusterVisitorError> { + ) -> Result, DispatchError> { Ok(BlockNumberFor::::from(10u32)) } + fn get_unbonding_delay( _cluster_id: &ClusterId, _node_type: NodeType, - ) -> Result, ClusterVisitorError> { + ) -> Result, DispatchError> { Ok(BlockNumberFor::::from(10u32)) } - fn get_pricing_params( - cluster_id: &ClusterId, - ) -> Result { + fn get_pricing_params(cluster_id: &ClusterId) -> Result { Ok(get_pricing(cluster_id)) } - fn get_fees_params(cluster_id: &ClusterId) -> Result { + fn get_fees_params(cluster_id: &ClusterId) -> Result { Ok(get_fees(cluster_id)) } - fn get_reserve_account_id( + fn get_bonding_params( _cluster_id: &ClusterId, - ) -> Result { - let reserve_account = RESERVE_ACCOUNT_ID.to_ne_bytes(); + ) -> Result>, DispatchError> { + unimplemented!() + } + + fn get_reserve_account_id(_cluster_id: &ClusterId) -> Result { + let reserve_account: [u8; 32] = RESERVE_ACCOUNT_ID; Ok(T::AccountId::decode(&mut &reserve_account[..]).unwrap()) } - fn get_bonding_params( + fn activate_cluster_protocol(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn update_cluster_protocol( _cluster_id: &ClusterId, - ) -> Result>, ClusterVisitorError> { + _cluster_protocol_params: ClusterProtocolParams, BlockNumberFor>, + ) -> DispatchResult { + unimplemented!() + } + + fn bond_cluster(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn start_unbond_cluster(_cluster_id: &ClusterId) -> DispatchResult { + unimplemented!() + } + + fn end_unbond_cluster(_cluster_id: &ClusterId) -> DispatchResult { unimplemented!() } } @@ -475,13 +592,13 @@ impl ExtBuilder { let _balance_genesis = pallet_balances::GenesisConfig:: { balances: vec![ - (1, 10000000000000000000000000000), - (2, USER2_BALANCE), // < PARTIAL_CHARGE - (3, USER3_BALANCE), // > PARTIAL_CHARGE - (4, 1000000000000000000000000), - (5, 1000000000000000000000000), - (6, 1000000000000000000000000), - (7, 1000000000000000000000000), + ([1; 32].into(), 10000000000000000000000000000), + ([2; 32].into(), USER2_BALANCE), // < PARTIAL_CHARGE + ([3; 32].into(), USER3_BALANCE), // > PARTIAL_CHARGE + ([4; 32].into(), 1000000000000000000000000), + ([5; 32].into(), 1000000000000000000000000), + ([6; 32].into(), 1000000000000000000000000), + ([7; 32].into(), 1000000000000000000000000), ], } .assimilate_storage(&mut storage); diff --git a/pallets/ddc-payouts/src/tests.rs b/pallets/ddc-payouts/src/tests.rs index af8fe2d30..3a67aecb5 100644 --- a/pallets/ddc-payouts/src/tests.rs +++ b/pallets/ddc-payouts/src/tests.rs @@ -13,18 +13,21 @@ fn set_authorised_caller_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let root_account = 1u128; - let dac_account = 2u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); assert_noop!( - DdcPayouts::set_authorised_caller(RuntimeOrigin::signed(root_account), dac_account), + DdcPayouts::set_authorised_caller( + RuntimeOrigin::signed(root_account), + dac_account.clone() + ), BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); System::assert_last_event( - Event::AuthorisedCaller { authorised_caller: dac_account }.into(), + Event::AuthorisedCaller { authorised_caller: dac_account.clone() }.into(), ); assert_eq!(DdcPayouts::authorised_caller().unwrap(), dac_account); @@ -34,8 +37,8 @@ fn set_authorised_caller_works() { #[test] fn begin_billing_report_fails_for_unauthorised() { ExtBuilder.build_and_execute(|| { - let root_account = 1u128; - let dac_account = 2u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); let cluster_id = ClusterId::from([1; 20]); let era = 100; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -49,7 +52,7 @@ fn begin_billing_report_fails_for_unauthorised() { assert_noop!( DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account + 1), + RuntimeOrigin::signed(AccountId::from([3; 32])), cluster_id, era, start_era, @@ -76,7 +79,7 @@ fn begin_billing_report_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 2u128; + let dac_account = AccountId::from([2; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -86,7 +89,7 @@ fn begin_billing_report_works() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( RuntimeOrigin::signed(dac_account), @@ -99,7 +102,7 @@ fn begin_billing_report_works() { System::assert_last_event(Event::BillingReportInitialized { cluster_id, era }.into()); let report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::Initialized); + assert_eq!(report.state, PayoutState::Initialized); assert_eq!(report.start_era, start_era); assert_eq!(report.end_era, end_era); }) @@ -108,7 +111,7 @@ fn begin_billing_report_works() { #[test] fn begin_charging_customers_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let dac_account = 2u128; + let dac_account = AccountId::from([3; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 2; @@ -133,11 +136,11 @@ fn begin_charging_customers_fails_uninitialised() { BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), DAC_ACCOUNT_ID.into())); assert_noop!( DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(DAC_ACCOUNT_ID.into()), cluster_id, era, max_batch_index, @@ -152,7 +155,7 @@ fn begin_charging_customers_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 2u128; + let dac_account = AccountId::from([2; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 2; @@ -163,10 +166,10 @@ fn begin_charging_customers_works() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -183,7 +186,7 @@ fn begin_charging_customers_works() { System::assert_last_event(Event::ChargingStarted { cluster_id, era }.into()); let report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); assert_eq!(report.charging_max_batch_index, max_batch_index); }) } @@ -191,16 +194,24 @@ fn begin_charging_customers_works() { #[test] fn send_charging_customers_batch_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let root_account = 1u128; - let dac_account = 2u128; - let user1 = 3u128; - let user2 = 4u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); + let user2 = AccountId::from([4; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 2; let batch_index = 1; - let payers1 = vec![(user1, CustomerUsage::default())]; - let payers2 = vec![(user2, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let customer_usage = CustomerUsage { + transferred_bytes: 100, + stored_bytes: -800, + number_of_gets: 100, + number_of_puts: 200, + }; + let payers1 = vec![(user1, bucket_id1, customer_usage)]; + let payers2 = vec![(user2.clone(), bucket_id2, CustomerUsage::default())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight @@ -215,6 +226,7 @@ fn send_charging_customers_batch_fails_uninitialised() { era, batch_index, payers1.clone(), + MMRProof::default(), ), Error::::Unauthorised ); @@ -226,25 +238,27 @@ fn send_charging_customers_batch_fails_uninitialised() { era, batch_index, payers1.clone(), + MMRProof::default(), ), BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1.clone(), + MMRProof::default(), ), Error::::BillingReportDoesNotExist ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -253,37 +267,41 @@ fn send_charging_customers_batch_fails_uninitialised() { assert_noop!( DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1.clone(), + MMRProof::default(), ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); + let payers1 = vec![(user2, bucket_id2, CustomerUsage::default())]; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1.clone(), + MMRProof::default(), )); assert_noop!( DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), ), Error::::BatchIndexAlreadyProcessed ); @@ -295,6 +313,7 @@ fn send_charging_customers_batch_fails_uninitialised() { era, batch_index, payers2, + MMRProof::default(), ), Error::::BatchIndexAlreadyProcessed ); @@ -392,15 +411,20 @@ fn send_charging_customers_batch_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -429,18 +453,21 @@ fn send_charging_customers_batch_works() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -448,7 +475,7 @@ fn send_charging_customers_batch_works() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -457,15 +484,16 @@ fn send_charging_customers_batch_works() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_month(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_month(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -488,7 +516,8 @@ fn send_charging_customers_batch_works() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -501,6 +530,7 @@ fn send_charging_customers_batch_works() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -511,6 +541,7 @@ fn send_charging_customers_batch_works() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -523,11 +554,12 @@ fn send_charging_customers_batch_works() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -535,7 +567,8 @@ fn send_charging_customers_batch_works() { cluster_id, era, batch_index, - customer_id: user1, + bucket_id: bucket_id1, + customer_id: user1.clone(), amount: calculate_charge_for_month(cluster_id, usage1.clone()), } .into(), @@ -560,7 +593,7 @@ fn send_charging_customers_batch_works() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -575,6 +608,7 @@ fn send_charging_customers_batch_works() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_month(cluster_id, usage3.clone()); @@ -601,7 +635,7 @@ fn send_charging_customers_batch_works() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -609,7 +643,8 @@ fn send_charging_customers_batch_works() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -622,6 +657,7 @@ fn send_charging_customers_batch_works() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -635,13 +671,16 @@ fn end_charging_customers_works_small_usage_1_hour() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user6 = 6u128; - let user7 = 7u128; + let dac_account = AccountId::from([123; 32]); + let user6 = AccountId::from([6; 32]); + let user7 = AccountId::from([7; 32]); let cluster_id = HIGH_FEES_CLUSTER_ID; let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id6: BucketId = 6; + let bucket_id7: BucketId = 7; + let usage6 = CustomerUsage { transferred_bytes: 0, stored_bytes: 474_957, @@ -654,16 +693,19 @@ fn end_charging_customers_works_small_usage_1_hour() { number_of_puts: 0, number_of_gets: 0, }; - let payers1 = vec![(user6, usage6.clone()), (user7, usage7.clone())]; + let payers1 = vec![ + (user6.clone(), bucket_id6, usage6.clone()), + (user7.clone(), bucket_id7, usage7.clone()), + ]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 1.0 * 3600.0) as i64; // 1 hour - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -671,7 +713,7 @@ fn end_charging_customers_works_small_usage_1_hour() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -680,11 +722,12 @@ fn end_charging_customers_works_small_usage_1_hour() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); @@ -705,6 +748,7 @@ fn end_charging_customers_works_small_usage_1_hour() { cluster_id, era, customer_id: user6, + bucket_id: bucket_id6, batch_index, amount: usage6_charge, } @@ -716,6 +760,7 @@ fn end_charging_customers_works_small_usage_1_hour() { cluster_id, era, customer_id: user7, + bucket_id: bucket_id7, batch_index, amount: usage7_charge, } @@ -726,19 +771,19 @@ fn end_charging_customers_works_small_usage_1_hour() { let charge = usage7_charge + usage6_charge; assert_eq!(balance - Balances::minimum_balance(), charge); - balance = Balances::free_balance(TREASURY_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(TREASURY_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(RESERVE_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(RESERVE_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR1_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR1_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR2_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR2_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR3_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR3_ACCOUNT_ID)); assert_eq!(balance, 0); assert_ok!(DdcPayouts::end_charging_customers( @@ -749,7 +794,7 @@ fn end_charging_customers_works_small_usage_1_hour() { System::assert_has_event(Event::ChargingFinished { cluster_id, era }.into()); let report_after = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report_after.state, State::CustomersChargedWithFees); + assert_eq!(report_after.state, PayoutState::CustomersChargedWithFees); let fees = get_fees(&cluster_id); let total_left_from_one = @@ -767,15 +812,15 @@ fn end_charging_customers_works_small_usage_1_hour() { assert_eq!(fees.validators_share, PRICING_FEES_HIGH.validators_share); assert_eq!(fees.cluster_reserve_share, PRICING_FEES_HIGH.cluster_reserve_share); - balance = Balances::free_balance(TREASURY_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(TREASURY_ACCOUNT_ID)); assert_eq!(balance, get_fees(&cluster_id).treasury_share * charge); assert!(balance > 0); - balance = Balances::free_balance(RESERVE_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(RESERVE_ACCOUNT_ID)); assert_eq!(balance, get_fees(&cluster_id).cluster_reserve_share * charge); assert!(balance > 0); - balance = Balances::free_balance(VALIDATOR1_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR1_ACCOUNT_ID)); let mut ratio = Perquintill::from_rational( VALIDATOR1_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, @@ -783,7 +828,7 @@ fn end_charging_customers_works_small_usage_1_hour() { assert_eq!(balance, get_fees(&cluster_id).validators_share * ratio * charge); assert!(balance > 0); - balance = Balances::free_balance(VALIDATOR2_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR2_ACCOUNT_ID)); ratio = Perquintill::from_rational( VALIDATOR2_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, @@ -791,7 +836,7 @@ fn end_charging_customers_works_small_usage_1_hour() { assert_eq!(balance, get_fees(&cluster_id).validators_share * ratio * charge); assert!(balance > 0); - balance = Balances::free_balance(VALIDATOR3_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR3_ACCOUNT_ID)); ratio = Perquintill::from_rational( VALIDATOR3_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, @@ -825,15 +870,20 @@ fn send_charging_customers_batch_works_for_day() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -862,18 +912,21 @@ fn send_charging_customers_batch_works_for_day() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -881,7 +934,7 @@ fn send_charging_customers_batch_works_for_day() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -890,15 +943,16 @@ fn send_charging_customers_batch_works_for_day() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -921,7 +975,8 @@ fn send_charging_customers_batch_works_for_day() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -934,6 +989,7 @@ fn send_charging_customers_batch_works_for_day() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -944,6 +1000,7 @@ fn send_charging_customers_batch_works_for_day() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -956,11 +1013,12 @@ fn send_charging_customers_batch_works_for_day() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -968,7 +1026,8 @@ fn send_charging_customers_batch_works_for_day() { cluster_id, era, batch_index, - customer_id: user1, + customer_id: user1.clone(), + bucket_id: bucket_id1, amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -993,7 +1052,7 @@ fn send_charging_customers_batch_works_for_day() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -1008,6 +1067,7 @@ fn send_charging_customers_batch_works_for_day() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -1034,7 +1094,7 @@ fn send_charging_customers_batch_works_for_day() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -1042,7 +1102,8 @@ fn send_charging_customers_batch_works_for_day() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -1055,6 +1116,7 @@ fn send_charging_customers_batch_works_for_day() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -1068,15 +1130,20 @@ fn send_charging_customers_batch_works_for_day_free_storage() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = STORAGE_ZERO_CLUSTER_ID; let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -1105,18 +1172,21 @@ fn send_charging_customers_batch_works_for_day_free_storage() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -1124,7 +1194,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -1133,15 +1203,16 @@ fn send_charging_customers_batch_works_for_day_free_storage() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -1164,7 +1235,8 @@ fn send_charging_customers_batch_works_for_day_free_storage() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -1177,6 +1249,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -1187,6 +1260,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -1199,11 +1273,12 @@ fn send_charging_customers_batch_works_for_day_free_storage() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -1211,7 +1286,8 @@ fn send_charging_customers_batch_works_for_day_free_storage() { cluster_id, era, batch_index, - customer_id: user1, + customer_id: user1.clone(), + bucket_id: bucket_id1, amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -1236,7 +1312,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -1251,6 +1327,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -1277,7 +1354,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -1285,7 +1362,8 @@ fn send_charging_customers_batch_works_for_day_free_storage() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -1298,6 +1376,7 @@ fn send_charging_customers_batch_works_for_day_free_storage() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -1311,15 +1390,20 @@ fn send_charging_customers_batch_works_for_day_free_stream() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = STREAM_ZERO_CLUSTER_ID; let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -1348,18 +1432,21 @@ fn send_charging_customers_batch_works_for_day_free_stream() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -1367,7 +1454,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -1376,15 +1463,16 @@ fn send_charging_customers_batch_works_for_day_free_stream() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -1407,7 +1495,8 @@ fn send_charging_customers_batch_works_for_day_free_stream() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -1420,6 +1509,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -1430,6 +1520,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -1442,11 +1533,12 @@ fn send_charging_customers_batch_works_for_day_free_stream() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -1454,7 +1546,8 @@ fn send_charging_customers_batch_works_for_day_free_stream() { cluster_id, era, batch_index, - customer_id: user1, + customer_id: user1.clone(), + bucket_id: bucket_id1, amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -1479,7 +1572,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -1494,6 +1587,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -1520,7 +1614,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -1528,7 +1622,8 @@ fn send_charging_customers_batch_works_for_day_free_stream() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -1541,6 +1636,7 @@ fn send_charging_customers_batch_works_for_day_free_stream() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -1554,15 +1650,20 @@ fn send_charging_customers_batch_works_for_day_free_get() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = GET_ZERO_CLUSTER_ID; let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -1591,18 +1692,21 @@ fn send_charging_customers_batch_works_for_day_free_get() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -1610,7 +1714,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -1619,15 +1723,16 @@ fn send_charging_customers_batch_works_for_day_free_get() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -1650,7 +1755,8 @@ fn send_charging_customers_batch_works_for_day_free_get() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -1662,7 +1768,8 @@ fn send_charging_customers_batch_works_for_day_free_get() { Event::Indebted { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -1673,6 +1780,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -1685,11 +1793,12 @@ fn send_charging_customers_batch_works_for_day_free_get() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -1697,7 +1806,8 @@ fn send_charging_customers_batch_works_for_day_free_get() { cluster_id, era, batch_index, - customer_id: user1, + customer_id: user1.clone(), + bucket_id: bucket_id1, amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -1722,7 +1832,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -1737,6 +1847,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -1763,7 +1874,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -1771,7 +1882,8 @@ fn send_charging_customers_batch_works_for_day_free_get() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -1784,6 +1896,7 @@ fn send_charging_customers_batch_works_for_day_free_get() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -1797,15 +1910,20 @@ fn send_charging_customers_batch_works_for_day_free_put() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = PUT_ZERO_CLUSTER_ID; let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -1834,18 +1952,21 @@ fn send_charging_customers_batch_works_for_day_free_put() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -1853,7 +1974,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -1862,15 +1983,16 @@ fn send_charging_customers_batch_works_for_day_free_put() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -1893,7 +2015,8 @@ fn send_charging_customers_batch_works_for_day_free_put() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -1906,6 +2029,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -1916,6 +2040,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -1928,11 +2053,12 @@ fn send_charging_customers_batch_works_for_day_free_put() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -1940,7 +2066,8 @@ fn send_charging_customers_batch_works_for_day_free_put() { cluster_id, era, batch_index, - customer_id: user1, + bucket_id: bucket_id1, + customer_id: user1.clone(), amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -1965,7 +2092,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -1980,6 +2107,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -2006,7 +2134,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -2014,7 +2142,8 @@ fn send_charging_customers_batch_works_for_day_free_put() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -2027,6 +2156,7 @@ fn send_charging_customers_batch_works_for_day_free_put() { era, batch_index, customer_id: user3_debtor, + bucket_id: bucket_id3, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, } @@ -2040,15 +2170,20 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let user2_debtor = 2u128; - let user3_debtor = 3u128; - let user4 = 4u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let user2_debtor = AccountId::from([2; 32]); + let user3_debtor = AccountId::from([3; 32]); + let user4 = AccountId::from([4; 32]); let cluster_id = STORAGE_STREAM_ZERO_CLUSTER_ID; let era = 100; let max_batch_index = 3; let mut batch_index = 0; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let bucket_id3: BucketId = 3; + let bucket_id4: BucketId = 4; + let usage1 = CustomerUsage { // should pass without debt transferred_bytes: 23452345, @@ -2077,18 +2212,21 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { number_of_puts: 3456345, number_of_gets: 242334563456423, }; - let payers1 = vec![(user2_debtor, usage2.clone()), (user4, usage4.clone())]; - let payers2 = vec![(user1, usage1.clone())]; - let payers3 = vec![(user3_debtor, usage3.clone())]; + let payers1 = vec![ + (user2_debtor.clone(), bucket_id2, usage2.clone()), + (user4.clone(), bucket_id4, usage4.clone()), + ]; + let payers2 = vec![(user1.clone(), bucket_id1, usage1.clone())]; + let payers3 = vec![(user3_debtor.clone(), bucket_id3, usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (1.0 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2096,7 +2234,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2105,15 +2243,16 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { // batch 1 assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); let usage4_charge = calculate_charge_for_day(cluster_id, usage4.clone()); - let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor).unwrap(); + let user2_debt = DdcPayouts::debtor_customers(cluster_id, user2_debtor.clone()).unwrap(); let expected_charge2 = calculate_charge_for_day(cluster_id, usage2.clone()); let mut debt = expected_charge2 - USER2_BALANCE; assert_eq!(user2_debt, debt); @@ -2136,7 +2275,8 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { Event::ChargeFailed { cluster_id, era, - customer_id: user2_debtor, + customer_id: user2_debtor.clone(), + bucket_id: bucket_id2, batch_index, charged: USER2_BALANCE, expected_to_charge: expected_charge2, @@ -2149,6 +2289,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { cluster_id, era, customer_id: user2_debtor, + bucket_id: bucket_id2, batch_index, amount: debt, } @@ -2159,6 +2300,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { cluster_id, era, customer_id: user4, + bucket_id: bucket_id4, batch_index, amount: usage4_charge, } @@ -2171,11 +2313,12 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { let mut before_total_customer_charge = report.total_customer_charge; batch_index += 1; assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers2, + MMRProof::default(), )); System::assert_last_event( @@ -2183,7 +2326,8 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { cluster_id, era, batch_index, - customer_id: user1, + bucket_id: bucket_id1, + customer_id: user1.clone(), amount: calculate_charge_for_day(cluster_id, usage1.clone()), } .into(), @@ -2208,7 +2352,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { report.total_customer_charge.transfer ); - assert_eq!(report.state, State::ChargingCustomers); + assert_eq!(report.state, PayoutState::ChargingCustomers); let user1_debt = DdcPayouts::debtor_customers(cluster_id, user1); assert_eq!(user1_debt, None); @@ -2223,6 +2367,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { era, batch_index, payers3, + MMRProof::default(), )); let user3_charge = calculate_charge_for_day(cluster_id, usage3.clone()); @@ -2249,7 +2394,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { let balance = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance, balance_before + PARTIAL_CHARGE); - let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor).unwrap(); + let user3_debt = DdcPayouts::debtor_customers(cluster_id, user3_debtor.clone()).unwrap(); debt = user3_charge - PARTIAL_CHARGE; assert_eq!(user3_debt, debt); @@ -2257,7 +2402,8 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { Event::Indebted { cluster_id, era, - customer_id: user3_debtor, + customer_id: user3_debtor.clone(), + bucket_id: bucket_id3, batch_index, amount: user3_debt, } @@ -2269,6 +2415,7 @@ fn send_charging_customers_batch_works_for_day_free_storage_stream() { cluster_id, era, batch_index, + bucket_id: bucket_id3, customer_id: user3_debtor, charged: PARTIAL_CHARGE, expected_to_charge: user3_charge, @@ -2283,12 +2430,13 @@ fn send_charging_customers_batch_works_zero_fees() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user5 = 5u128; + let dac_account = AccountId::from([123; 32]); + let user5 = AccountId::from([5; 32]); let cluster_id = ONE_CLUSTER_ID; let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id5: BucketId = 5; let usage5 = CustomerUsage { // should pass without debt transferred_bytes: 1024, @@ -2296,16 +2444,16 @@ fn send_charging_customers_batch_works_zero_fees() { number_of_puts: 1, number_of_gets: 1, }; - let payers5 = vec![(user5, usage5.clone())]; + let payers5 = vec![(user5, bucket_id5, usage5.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2313,7 +2461,7 @@ fn send_charging_customers_batch_works_zero_fees() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2330,6 +2478,7 @@ fn send_charging_customers_batch_works_zero_fees() { era, batch_index, payers5, + MMRProof::default(), )); let usage5_charge = calculate_charge_for_month(cluster_id, usage5.clone()); @@ -2359,14 +2508,15 @@ fn send_charging_customers_batch_works_zero_fees() { #[test] fn end_charging_customers_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let root_account = 100u128; - let dac_account = 123u128; - let user1 = 1u128; + let root_account = AccountId::from([100; 32]); + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 2; let batch_index = 1; - let payers = vec![(user1, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let payers = vec![(user1, bucket_id1, CustomerUsage::default())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight let start_era: i64 = @@ -2387,15 +2537,19 @@ fn end_charging_customers_fails_uninitialised() { BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( - DdcPayouts::end_charging_customers(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_charging_customers( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::BillingReportDoesNotExist ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2403,28 +2557,37 @@ fn end_charging_customers_fails_uninitialised() { )); assert_noop!( - DdcPayouts::end_charging_customers(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_charging_customers( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_noop!( - DdcPayouts::end_charging_customers(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_charging_customers( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::BatchesMissed ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); assert_noop!( @@ -2439,19 +2602,20 @@ fn end_charging_customers_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id1: BucketId = 1; let usage1 = CustomerUsage { transferred_bytes: 23452345, stored_bytes: 3345234523, number_of_puts: 4456456345234523, number_of_gets: 523423, }; - let payers = vec![(user1, usage1.clone())]; + let payers = vec![(user1.clone(), bucket_id1, usage1.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight @@ -2459,10 +2623,10 @@ fn end_charging_customers_works() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2470,25 +2634,33 @@ fn end_charging_customers_works() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); let charge = calculate_charge_for_month(cluster_id, usage1); System::assert_last_event( - Event::Charged { cluster_id, era, batch_index, customer_id: user1, amount: charge } - .into(), + Event::Charged { + cluster_id, + era, + batch_index, + customer_id: user1, + amount: charge, + bucket_id: bucket_id1, + } + .into(), ); let mut balance = Balances::free_balance(DdcPayouts::account_id()); @@ -2520,42 +2692,74 @@ fn end_charging_customers_works() { ); let transfers = 3 + 3 + 3 * 3; // for Currency::transfer - assert_eq!(System::events().len(), 5 + 1 + 3 + transfers); + assert_eq!(System::events().len(), 8 + 1 + 3 + transfers); let report_after = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report_after.state, State::CustomersChargedWithFees); + assert_eq!(report_after.state, PayoutState::CustomersChargedWithFees); let total_left_from_one = (get_fees(&cluster_id).treasury_share + get_fees(&cluster_id).validators_share + get_fees(&cluster_id).cluster_reserve_share) .left_from_one(); - balance = Balances::free_balance(TREASURY_ACCOUNT_ID); - assert_eq!(balance, get_fees(&cluster_id).treasury_share * charge); + balance = Balances::free_balance(AccountId::from(TREASURY_ACCOUNT_ID)); + let mut expected_fees = get_fees(&cluster_id).treasury_share * charge; + assert_eq!(balance, expected_fees); - balance = Balances::free_balance(RESERVE_ACCOUNT_ID); - assert_eq!(balance, get_fees(&cluster_id).cluster_reserve_share * charge); + balance = Balances::free_balance(AccountId::from(RESERVE_ACCOUNT_ID)); + expected_fees = get_fees(&cluster_id).cluster_reserve_share * charge; + assert_eq!(balance, expected_fees); - balance = Balances::free_balance(VALIDATOR1_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR1_ACCOUNT_ID)); let mut ratio = Perquintill::from_rational( VALIDATOR1_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, ); - assert_eq!(balance, get_fees(&cluster_id).validators_share * ratio * charge); + expected_fees = get_fees(&cluster_id).validators_share * ratio * charge; + assert_eq!(balance, expected_fees); + System::assert_has_event( + Event::ValidatorRewarded { + cluster_id, + era, + validator_id: AccountId::from(VALIDATOR1_ACCOUNT_ID), + amount: expected_fees, + } + .into(), + ); - balance = Balances::free_balance(VALIDATOR2_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR2_ACCOUNT_ID)); ratio = Perquintill::from_rational( VALIDATOR2_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, ); - assert_eq!(balance, get_fees(&cluster_id).validators_share * ratio * charge); + expected_fees = get_fees(&cluster_id).validators_share * ratio * charge; + assert_eq!(balance, expected_fees); + System::assert_has_event( + Event::ValidatorRewarded { + cluster_id, + era, + validator_id: AccountId::from(VALIDATOR2_ACCOUNT_ID), + amount: expected_fees, + } + .into(), + ); - balance = Balances::free_balance(VALIDATOR3_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR3_ACCOUNT_ID)); ratio = Perquintill::from_rational( VALIDATOR3_SCORE, VALIDATOR1_SCORE + VALIDATOR2_SCORE + VALIDATOR3_SCORE, ); - assert_eq!(balance, get_fees(&cluster_id).validators_share * ratio * charge); + expected_fees = get_fees(&cluster_id).validators_share * ratio * charge; + assert_eq!(balance, expected_fees); + System::assert_has_event( + Event::ValidatorRewarded { + cluster_id, + era, + validator_id: AccountId::from(VALIDATOR3_ACCOUNT_ID), + amount: expected_fees, + } + .into(), + ); assert_eq!( report_after.total_customer_charge.transfer, @@ -2581,19 +2785,20 @@ fn end_charging_customers_works_zero_fees() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); let cluster_id = ClusterId::zero(); let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id1: BucketId = 1; let usage1 = CustomerUsage { transferred_bytes: 23452345, stored_bytes: 3345234523, number_of_puts: 1, number_of_gets: 1, }; - let payers = vec![(user1, usage1.clone())]; + let payers = vec![(user1.clone(), bucket_id1, usage1.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight @@ -2601,10 +2806,10 @@ fn end_charging_customers_works_zero_fees() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2612,25 +2817,33 @@ fn end_charging_customers_works_zero_fees() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); let charge = calculate_charge_for_month(cluster_id, usage1); System::assert_last_event( - Event::Charged { cluster_id, era, customer_id: user1, batch_index, amount: charge } - .into(), + Event::Charged { + cluster_id, + era, + customer_id: user1, + bucket_id: bucket_id1, + batch_index, + amount: charge, + } + .into(), ); let mut balance = Balances::free_balance(DdcPayouts::account_id()); @@ -2647,7 +2860,7 @@ fn end_charging_customers_works_zero_fees() { assert_eq!(System::events().len(), 5 + 1); let report_after = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report_after.state, State::CustomersChargedWithFees); + assert_eq!(report_after.state, PayoutState::CustomersChargedWithFees); let fees = get_fees(&cluster_id); @@ -2661,19 +2874,19 @@ fn end_charging_customers_works_zero_fees() { assert_eq!(fees.validators_share, Perquintill::zero()); assert_eq!(fees.cluster_reserve_share, Perquintill::zero()); - balance = Balances::free_balance(TREASURY_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(TREASURY_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(RESERVE_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(RESERVE_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR1_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR1_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR2_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR2_ACCOUNT_ID)); assert_eq!(balance, 0); - balance = Balances::free_balance(VALIDATOR3_ACCOUNT_ID); + balance = Balances::free_balance(AccountId::from(VALIDATOR3_ACCOUNT_ID)); assert_eq!(balance, 0); assert_eq!( @@ -2698,14 +2911,15 @@ fn end_charging_customers_works_zero_fees() { #[test] fn begin_rewarding_providers_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let root_account = 1u128; - let dac_account = 2u128; - let user1 = 3u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 2; let batch_index = 1; - let payers = vec![(user1, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let payers = vec![(user1, bucket_id1, CustomerUsage::default())]; let node_usage = NodeUsage::default(); let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -2736,11 +2950,11 @@ fn begin_rewarding_providers_fails_uninitialised() { BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2750,7 +2964,7 @@ fn begin_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2759,7 +2973,7 @@ fn begin_rewarding_providers_fails_uninitialised() { assert_noop!( DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2769,7 +2983,7 @@ fn begin_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2777,7 +2991,7 @@ fn begin_rewarding_providers_fails_uninitialised() { assert_noop!( DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2787,16 +3001,17 @@ fn begin_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers.clone(), + MMRProof::default(), )); assert_noop!( DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2806,11 +3021,12 @@ fn begin_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index + 1, payers, + MMRProof::default(), )); assert_noop!( @@ -2831,14 +3047,15 @@ fn begin_rewarding_providers_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id1: BucketId = 1; let total_node_usage = NodeUsage::default(); - let payers = vec![(user1, CustomerUsage::default())]; + let payers = vec![(user1, bucket_id1, CustomerUsage::default())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight @@ -2846,10 +3063,10 @@ fn begin_rewarding_providers_works() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2857,25 +3074,26 @@ fn begin_rewarding_providers_works() { )); let mut report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::Initialized); + assert_eq!(report.state, PayoutState::Initialized); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -2891,24 +3109,26 @@ fn begin_rewarding_providers_works() { System::assert_last_event(Event::RewardingStarted { cluster_id, era }.into()); report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::RewardingProviders); + assert_eq!(report.state, PayoutState::RewardingProviders); }) } #[test] fn send_rewarding_providers_batch_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let root_account = 1u128; - let dac_account = 2u128; - let user1 = 3u128; - let user2 = 4u128; - let node1 = 33u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); + let user2 = AccountId::from([4; 32]); + let node1 = AccountId::from([33; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 1; let batch_index = 0; - let payers1 = vec![(user1, CustomerUsage::default())]; - let payers2 = vec![(user2, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let payers1 = vec![(user1, bucket_id1, CustomerUsage::default())]; + let payers2 = vec![(user2, bucket_id2, CustomerUsage::default())]; let payees = vec![(node1, NodeUsage::default())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -2924,6 +3144,7 @@ fn send_rewarding_providers_batch_fails_uninitialised() { era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::Unauthorised ); @@ -2935,25 +3156,27 @@ fn send_rewarding_providers_batch_fails_uninitialised() { era, batch_index, payees.clone(), + MMRProof::default(), ), BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::BillingReportDoesNotExist ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -2962,17 +3185,18 @@ fn send_rewarding_providers_batch_fails_uninitialised() { assert_noop!( DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -2980,55 +3204,60 @@ fn send_rewarding_providers_batch_fails_uninitialised() { assert_noop!( DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); assert_noop!( DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index + 1, payers2, + MMRProof::default(), )); assert_noop!( DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -3040,6 +3269,7 @@ fn send_rewarding_providers_batch_fails_uninitialised() { era, batch_index, payees, + MMRProof::default(), ), Error::::NotExpectedState ); @@ -3051,17 +3281,18 @@ fn send_rewarding_providers_batch_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let dac_account = 123u128; - let user1 = 1u128; - let node1 = 10u128; - let node2 = 11u128; - let node3 = 12u128; + let dac_account = AccountId::from([123; 32]); + let user1 = AccountId::from([1; 32]); + let node1 = AccountId::from([10; 32]); + let node2 = AccountId::from([11; 32]); + let node3 = AccountId::from([12; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 0; let max_node_batch_index = 1; let batch_index = 0; let batch_node_index = 0; + let bucket_id1: BucketId = 1; let usage1 = CustomerUsage { transferred_bytes: 23452345, stored_bytes: 3345234523, @@ -3108,9 +3339,10 @@ fn send_rewarding_providers_batch_works() { node_usage3.number_of_gets, }; - let payers = vec![(user1, usage1)]; - let payees1 = vec![(node1, node_usage1.clone()), (node2, node_usage2.clone())]; - let payees2 = vec![(node3, node_usage3.clone())]; + let payers = vec![(user1, bucket_id1, usage1)]; + let payees1 = + vec![(node1.clone(), node_usage1.clone()), (node2.clone(), node_usage2.clone())]; + let payees2 = vec![(node3.clone(), node_usage3.clone())]; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st let time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); // Midnight @@ -3118,10 +3350,10 @@ fn send_rewarding_providers_batch_works() { DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -3129,23 +3361,24 @@ fn send_rewarding_providers_batch_works() { )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -3174,7 +3407,7 @@ fn send_rewarding_providers_batch_works() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_node_batch_index, @@ -3182,11 +3415,12 @@ fn send_rewarding_providers_batch_works() { )); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index, payees1, + MMRProof::default(), )); let ratio1_transfer = Perquintill::from_rational( @@ -3195,8 +3429,10 @@ fn send_rewarding_providers_batch_works() { ); let mut transfer_charge = ratio1_transfer * report_after.total_customer_charge.transfer; - let ratio1_storage = - Perquintill::from_rational(node_usage1.stored_bytes, total_nodes_usage.stored_bytes); + let ratio1_storage = Perquintill::from_rational( + node_usage1.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, + ); let mut storage_charge = ratio1_storage * report_after.total_customer_charge.storage; let ratio1_puts = Perquintill::from_rational( @@ -3211,7 +3447,7 @@ fn send_rewarding_providers_batch_works() { ); let mut gets_charge = ratio1_gets * report_after.total_customer_charge.gets; - let balance_node1 = Balances::free_balance(node1); + let balance_node1 = Balances::free_balance(node1.clone()); assert_eq!(balance_node1, transfer_charge + storage_charge + puts_charge + gets_charge); let mut report_reward = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); @@ -3233,8 +3469,10 @@ fn send_rewarding_providers_batch_works() { ); transfer_charge = ratio2_transfer * report_after.total_customer_charge.transfer; - let ratio2_storage = - Perquintill::from_rational(node_usage2.stored_bytes, total_nodes_usage.stored_bytes); + let ratio2_storage = Perquintill::from_rational( + node_usage2.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, + ); storage_charge = ratio2_storage * report_after.total_customer_charge.storage; let ratio2_puts = Perquintill::from_rational( @@ -3249,7 +3487,7 @@ fn send_rewarding_providers_batch_works() { ); gets_charge = ratio2_gets * report_after.total_customer_charge.gets; - let balance_node2 = Balances::free_balance(node2); + let balance_node2 = Balances::free_balance(node2.clone()); assert_eq!(balance_node2, transfer_charge + storage_charge + puts_charge + gets_charge); assert_eq!(report_reward.total_distributed_reward, balance_node1 + balance_node2); @@ -3267,11 +3505,12 @@ fn send_rewarding_providers_batch_works() { // batch 2 assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index + 1, payees2, + MMRProof::default(), )); let ratio3_transfer = Perquintill::from_rational( @@ -3280,8 +3519,10 @@ fn send_rewarding_providers_batch_works() { ); transfer_charge = ratio3_transfer * report_after.total_customer_charge.transfer; - let ratio3_storage = - Perquintill::from_rational(node_usage3.stored_bytes, total_nodes_usage.stored_bytes); + let ratio3_storage = Perquintill::from_rational( + node_usage3.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, + ); storage_charge = ratio3_storage * report_after.total_customer_charge.storage; let ratio3_puts = Perquintill::from_rational( @@ -3297,7 +3538,7 @@ fn send_rewarding_providers_batch_works() { gets_charge = ratio3_gets * report_after.total_customer_charge.gets; report_reward = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - let balance_node3 = Balances::free_balance(node3); + let balance_node3 = Balances::free_balance(node3.clone()); assert_eq!(balance_node3, transfer_charge + storage_charge + puts_charge + gets_charge); System::assert_has_event( @@ -3337,14 +3578,15 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); - let num_nodes = 100; + let num_nodes = 10; let num_users = 5; - let dac_account = 123u128; - let bank = 1u128; + let dac_account = AccountId::from([123; 32]); + let bank = AccountId::from([1; 32]); let cluster_id = ONE_CLUSTER_ID; let era = 100; let user_batch_size = 10; let node_batch_size = 10; + let bucketid1: BucketId = 1; let mut batch_user_index = 0; let mut batch_node_index = 0; let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -3384,8 +3626,8 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { number_of_gets: usage1.number_of_gets * 2, }; - let mut payees: Vec> = Vec::new(); - let mut node_batch: Vec<(u128, NodeUsage)> = Vec::new(); + let mut payees: Vec> = Vec::new(); + let mut node_batch: Vec<(AccountId, NodeUsage)> = Vec::new(); let mut total_nodes_usage = NodeUsage::default(); for i in 10..10 + num_nodes { let node_usage = match i % 3 { @@ -3399,7 +3641,7 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { total_nodes_usage.number_of_puts += node_usage.number_of_puts; total_nodes_usage.number_of_gets += node_usage.number_of_gets; - node_batch.push((i, node_usage)); + node_batch.push((AccountId::from([i; 32]), node_usage)); if node_batch.len() == node_batch_size { payees.push(node_batch.clone()); node_batch.clear(); @@ -3410,9 +3652,9 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { } let mut total_charge = 0u128; - let mut payers: Vec> = Vec::new(); - let mut user_batch: Vec<(u128, CustomerUsage)> = Vec::new(); - for user_id in 1000..1000 + num_users { + let mut payers: Vec> = Vec::new(); + let mut user_batch: Vec<(AccountId, BucketId, CustomerUsage)> = Vec::new(); + for user_id in 100u8..100 + num_users { let ratio = match user_id % 5 { 0 => Perquintill::one(), 1 => Perquintill::from_float(0.5), @@ -3424,20 +3666,21 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { let mut user_usage = usage1.clone(); user_usage.transferred_bytes = ratio * user_usage.transferred_bytes; - user_usage.stored_bytes = ratio * user_usage.stored_bytes; + user_usage.stored_bytes = (ratio * user_usage.stored_bytes as u64) as i64; user_usage.number_of_puts = ratio * user_usage.number_of_puts; user_usage.number_of_gets = ratio * user_usage.number_of_gets; let expected_charge = calculate_charge_for_month(cluster_id, user_usage.clone()); Balances::transfer( - RuntimeOrigin::signed(bank), - user_id, + &bank.clone(), + &AccountId::from([user_id; 32]), (expected_charge * 2).max(Balances::minimum_balance()), + ExistenceRequirement::KeepAlive, ) .unwrap(); total_charge += expected_charge; - user_batch.push((user_id, user_usage)); + user_batch.push((AccountId::from([user_id; 32]), bucketid1, user_usage)); if user_batch.len() == user_batch_size { payers.push(user_batch.clone()); user_batch.clear(); @@ -3447,16 +3690,16 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { payers.push(user_batch.clone()); } - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, end_era, )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payers.len() - 1) as u16, @@ -3464,21 +3707,23 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { for batch in payers.iter() { assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_user_index, batch.to_vec(), + MMRProof::default(), )); - for (customer_id, usage) in batch.iter() { + for (customer_id, _bucket_id, usage) in batch.iter() { let charge = calculate_charge_for_month(cluster_id, usage.clone()); System::assert_has_event( Event::Charged { cluster_id, era, - customer_id: *customer_id, + bucket_id: bucketid1, + customer_id: customer_id.clone(), batch_index: batch_user_index, amount: charge, } @@ -3489,14 +3734,14 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { } let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - let balance1 = Balances::free_balance(report_before.vault); + let balance1 = Balances::free_balance(report_before.vault.clone()); let balance2 = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance1, balance2); assert_eq!(report_before.vault, DdcPayouts::account_id()); assert_eq!(balance1 - Balances::minimum_balance(), total_charge); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -3532,7 +3777,7 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payees.len() - 1) as u16, @@ -3542,11 +3787,12 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { for batch in payees.iter() { let before_batch = Balances::free_balance(DdcPayouts::account_id()); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index, batch.to_vec(), + MMRProof::default(), )); let mut batch_charge = 0; @@ -3558,8 +3804,8 @@ fn send_rewarding_providers_batch_100_nodes_small_usage_works() { let transfer_charge = ratio1_transfer * report_after.total_customer_charge.transfer; let ratio1_storage = Perquintill::from_rational( - node_usage1.stored_bytes, - total_nodes_usage.stored_bytes, + node_usage1.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, ); let storage_charge = ratio1_storage * report_after.total_customer_charge.storage; @@ -3603,16 +3849,18 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - let num_nodes = 100; + let num_nodes = 10; let num_users = 5; - let dac_account = 123u128; - let bank = 1u128; + let dac_account = AccountId::from([123; 32]); + let bank = AccountId::from([1; 32]); let cluster_id = ONE_CLUSTER_ID; let era = 100; let user_batch_size = 10; let node_batch_size = 10; + let bucketid1: BucketId = 1; let mut batch_user_index = 0; let mut batch_node_index = 0; + let bucket_id: BucketId = 1; let usage1 = CustomerUsage { transferred_bytes: 1024, stored_bytes: 1024, @@ -3644,10 +3892,10 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { number_of_gets: usage1.number_of_gets * 2, }; - let mut payees: Vec> = Vec::new(); - let mut node_batch: Vec<(u128, NodeUsage)> = Vec::new(); + let mut payees: Vec> = Vec::new(); + let mut node_batch: Vec<(AccountId, NodeUsage)> = Vec::new(); let mut total_nodes_usage = NodeUsage::default(); - for i in 10..10 + num_nodes { + for i in 10u8..10 + num_nodes { let ratio = match i % 5 { 0 => Perquintill::from_float(1_000_000.0), 1 => Perquintill::from_float(10_000_000.0), @@ -3663,7 +3911,7 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { _ => unreachable!(), }; node_usage.transferred_bytes = ratio * node_usage.transferred_bytes; - node_usage.stored_bytes = ratio * node_usage.stored_bytes; + node_usage.stored_bytes = (ratio * node_usage.stored_bytes as u64) as i64; node_usage.number_of_puts = ratio * node_usage.number_of_puts; node_usage.number_of_gets = ratio * node_usage.number_of_gets; @@ -3672,7 +3920,7 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { total_nodes_usage.number_of_puts += node_usage.number_of_puts; total_nodes_usage.number_of_gets += node_usage.number_of_gets; - node_batch.push((i, node_usage)); + node_batch.push((AccountId::from([i; 32]), node_usage)); if node_batch.len() == node_batch_size { payees.push(node_batch.clone()); node_batch.clear(); @@ -3683,9 +3931,9 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { } let mut total_charge = 0u128; - let mut payers: Vec> = Vec::new(); - let mut user_batch: Vec<(u128, CustomerUsage)> = Vec::new(); - for user_id in 1000..1000 + num_users { + let mut payers: Vec> = Vec::new(); + let mut user_batch: Vec<(AccountId, BucketId, CustomerUsage)> = Vec::new(); + for user_id in 100u8..100 + num_users { let ratio = match user_id % 5 { 0 => Perquintill::from_float(1_000_000.0), 1 => Perquintill::from_float(10_000_000.0), @@ -3697,20 +3945,21 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { let mut user_usage = usage1.clone(); user_usage.transferred_bytes = ratio * user_usage.transferred_bytes; - user_usage.stored_bytes = ratio * user_usage.stored_bytes; + user_usage.stored_bytes = (ratio * user_usage.stored_bytes as u64) as i64; user_usage.number_of_puts = ratio * user_usage.number_of_puts; user_usage.number_of_gets = ratio * user_usage.number_of_gets; let expected_charge = calculate_charge_for_month(cluster_id, user_usage.clone()); Balances::transfer( - RuntimeOrigin::signed(bank), - user_id, + &bank.clone(), + &AccountId::from([user_id; 32]), (expected_charge * 2).max(Balances::minimum_balance()), + ExistenceRequirement::KeepAlive, ) .unwrap(); total_charge += expected_charge; - user_batch.push((user_id, user_usage)); + user_batch.push((AccountId::from([user_id; 32]), bucket_id, user_usage)); if user_batch.len() == user_batch_size { payers.push(user_batch.clone()); user_batch.clear(); @@ -3720,16 +3969,16 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { payers.push(user_batch.clone()); } - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, end_era, )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payers.len() - 1) as u16, @@ -3737,21 +3986,23 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { for batch in payers.iter() { assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_user_index, batch.to_vec(), + MMRProof::default(), )); - for (customer_id, usage) in batch.iter() { + for (customer_id, _bucket_id, usage) in batch.iter() { let charge = calculate_charge_for_month(cluster_id, usage.clone()); System::assert_has_event( Event::Charged { cluster_id, era, - customer_id: *customer_id, + bucket_id: bucketid1, + customer_id: customer_id.clone(), batch_index: batch_user_index, amount: charge, } @@ -3762,14 +4013,14 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { } let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - let balance1 = Balances::free_balance(report_before.vault); + let balance1 = Balances::free_balance(report_before.vault.clone()); let balance2 = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance1, balance2); assert_eq!(report_before.vault, DdcPayouts::account_id()); assert_eq!(balance1 - Balances::minimum_balance(), total_charge); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -3805,7 +4056,7 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payees.len() - 1) as u16, @@ -3815,11 +4066,12 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { for batch in payees.iter() { let before_batch = Balances::free_balance(DdcPayouts::account_id()); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index, batch.to_vec(), + MMRProof::default(), )); let mut batch_charge = 0; @@ -3831,8 +4083,8 @@ fn send_rewarding_providers_batch_100_nodes_large_usage_works() { let transfer_charge = ratio1_transfer * report_after.total_customer_charge.transfer; let ratio1_storage = Perquintill::from_rational( - node_usage1.stored_bytes, - total_nodes_usage.stored_bytes, + node_usage1.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, ); let storage_charge = ratio1_storage * report_after.total_customer_charge.storage; @@ -3876,12 +4128,13 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - let num_nodes = 100; + let num_nodes = 10; let num_users = 5; - let dac_account = 123u128; - let bank = 1u128; + let dac_account = AccountId::from([123; 32]); + let bank = AccountId::from([1; 32]); let cluster_id = ONE_CLUSTER_ID; let era = 100; + let bucketid1: BucketId = 1; let user_batch_size = 10; let node_batch_size = 10; let mut batch_user_index = 0; @@ -3917,10 +4170,10 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { number_of_gets: usage1.number_of_gets * 2, }; - let mut payees: Vec> = Vec::new(); - let mut node_batch: Vec<(u128, NodeUsage)> = Vec::new(); + let mut payees: Vec> = Vec::new(); + let mut node_batch: Vec<(AccountId, NodeUsage)> = Vec::new(); let mut total_nodes_usage = NodeUsage::default(); - for i in 10..10 + num_nodes { + for i in 10u8..10 + num_nodes { let ratio = match i % 5 { 0 => Perquintill::from_float(1_000_000.0), 1 => Perquintill::from_float(0.5), @@ -3936,7 +4189,7 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { _ => unreachable!(), }; node_usage.transferred_bytes = ratio * node_usage.transferred_bytes; - node_usage.stored_bytes = ratio * node_usage.stored_bytes; + node_usage.stored_bytes = (ratio * node_usage.stored_bytes as u64) as i64; node_usage.number_of_puts = ratio * node_usage.number_of_puts; node_usage.number_of_gets = ratio * node_usage.number_of_gets; @@ -3945,7 +4198,7 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { total_nodes_usage.number_of_puts += node_usage.number_of_puts; total_nodes_usage.number_of_gets += node_usage.number_of_gets; - node_batch.push((i, node_usage)); + node_batch.push((AccountId::from([i; 32]), node_usage)); if node_batch.len() == node_batch_size { payees.push(node_batch.clone()); node_batch.clear(); @@ -3956,9 +4209,9 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { } let mut total_charge = 0u128; - let mut payers: Vec> = Vec::new(); - let mut user_batch: Vec<(u128, CustomerUsage)> = Vec::new(); - for user_id in 1000..1000 + num_users { + let mut payers: Vec> = Vec::new(); + let mut user_batch: Vec<(AccountId, BucketId, CustomerUsage)> = Vec::new(); + for user_id in 100u8..100 + num_users { let ratio = match user_id % 5 { 0 => Perquintill::from_float(1_000_000.0), 1 => Perquintill::from_float(10_000_000.0), @@ -3970,20 +4223,21 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { let mut user_usage = usage1.clone(); user_usage.transferred_bytes = ratio * user_usage.transferred_bytes; - user_usage.stored_bytes = ratio * user_usage.stored_bytes; + user_usage.stored_bytes = (ratio * user_usage.stored_bytes as u64) as i64; user_usage.number_of_puts = ratio * user_usage.number_of_puts; user_usage.number_of_gets = ratio * user_usage.number_of_gets; let expected_charge = calculate_charge_for_month(cluster_id, user_usage.clone()); Balances::transfer( - RuntimeOrigin::signed(bank), - user_id, + &bank.clone(), + &AccountId::from([user_id; 32]), (expected_charge * 2).max(Balances::minimum_balance()), + ExistenceRequirement::KeepAlive, ) .unwrap(); total_charge += expected_charge; - user_batch.push((user_id, user_usage)); + user_batch.push((AccountId::from([user_id; 32]), bucketid1, user_usage)); if user_batch.len() == user_batch_size { payers.push(user_batch.clone()); user_batch.clear(); @@ -3993,16 +4247,16 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { payers.push(user_batch.clone()); } - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, end_era, )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payers.len() - 1) as u16, @@ -4010,21 +4264,23 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { for batch in payers.iter() { assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_user_index, batch.to_vec(), + MMRProof::default(), )); - for (customer_id, usage) in batch.iter() { + for (customer_id, _bucket_id, usage) in batch.iter() { let charge = calculate_charge_for_month(cluster_id, usage.clone()); System::assert_has_event( Event::Charged { cluster_id, era, - customer_id: *customer_id, + customer_id: customer_id.clone(), + bucket_id: bucketid1, batch_index: batch_user_index, amount: charge, } @@ -4035,14 +4291,14 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { } let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - let balance1 = Balances::free_balance(report_before.vault); + let balance1 = Balances::free_balance(report_before.vault.clone()); let balance2 = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance1, balance2); assert_eq!(report_before.vault, DdcPayouts::account_id()); assert_eq!(balance1 - Balances::minimum_balance(), total_charge); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -4078,7 +4334,7 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payees.len() - 1) as u16, @@ -4088,11 +4344,12 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { for batch in payees.iter() { let before_batch = Balances::free_balance(DdcPayouts::account_id()); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index, batch.to_vec(), + MMRProof::default(), )); let mut batch_charge = 0; @@ -4104,8 +4361,8 @@ fn send_rewarding_providers_batch_100_nodes_small_large_usage_works() { let transfer_charge = ratio1_transfer * report_after.total_customer_charge.transfer; let ratio1_storage = Perquintill::from_rational( - node_usage1.stored_bytes, - total_nodes_usage.stored_bytes, + node_usage1.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, ); let storage_charge = ratio1_storage * report_after.total_customer_charge.storage; @@ -4159,23 +4416,24 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { let mock_randomness = MockRandomness::default(); let min: u64 = 1024; let max: u64 = 1024 * 1024; - let num_nodes = 100; - let num_users = 100; - let dac_account = 123u128; - let bank = 1u128; + let num_nodes = 10; + let num_users = 10; + let dac_account = AccountId::from([123; 32]); + let bank = AccountId::from([1; 32]); let cluster_id = CERE_CLUSTER_ID; let era = 100; let user_batch_size = 10; let node_batch_size = 10; let mut batch_user_index = 0; let mut batch_node_index = 0; - let mut payees: Vec> = Vec::new(); - let mut node_batch: Vec<(u128, NodeUsage)> = Vec::new(); + let bucket_id1: BucketId = 1; + let mut payees: Vec> = Vec::new(); + let mut node_batch: Vec<(AccountId, NodeUsage)> = Vec::new(); let mut total_nodes_usage = NodeUsage::default(); - for i in 10..10 + num_nodes { + for i in 10u8..10 + num_nodes { let node_usage = NodeUsage { transferred_bytes: generate_random_u64(&mock_randomness, min, max), - stored_bytes: generate_random_u64(&mock_randomness, min, max), + stored_bytes: (generate_random_u64(&mock_randomness, min, max)) as i64, number_of_puts: generate_random_u64(&mock_randomness, min, max), number_of_gets: generate_random_u64(&mock_randomness, min, max), }; @@ -4185,7 +4443,7 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { total_nodes_usage.number_of_puts += node_usage.number_of_puts; total_nodes_usage.number_of_gets += node_usage.number_of_gets; - node_batch.push((i, node_usage)); + node_batch.push((AccountId::from([i; 32]), node_usage)); if node_batch.len() == node_batch_size { payees.push(node_batch.clone()); node_batch.clear(); @@ -4196,26 +4454,27 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { } let mut total_charge = 0u128; - let mut payers: Vec> = Vec::new(); - let mut user_batch: Vec<(u128, CustomerUsage)> = Vec::new(); - for user_id in 1000..1000 + num_users { + let mut payers: Vec> = Vec::new(); + let mut user_batch: Vec<(AccountId, BucketId, CustomerUsage)> = Vec::new(); + for user_id in 100u8..100 + num_users { let user_usage = CustomerUsage { transferred_bytes: generate_random_u64(&mock_randomness, min, max), - stored_bytes: generate_random_u64(&mock_randomness, min, max), + stored_bytes: (generate_random_u64(&mock_randomness, min, max)) as i64, number_of_puts: generate_random_u64(&mock_randomness, min, max), number_of_gets: generate_random_u64(&mock_randomness, min, max), }; let expected_charge = calculate_charge_for_month(cluster_id, user_usage.clone()); Balances::transfer( - RuntimeOrigin::signed(bank), - user_id, + &bank.clone(), + &AccountId::from([user_id; 32]), (expected_charge * 2).max(Balances::minimum_balance()), + ExistenceRequirement::KeepAlive, ) .unwrap(); total_charge += expected_charge; - user_batch.push((user_id, user_usage)); + user_batch.push((AccountId::from([user_id; 32]), bucket_id1, user_usage)); if user_batch.len() == user_batch_size { payers.push(user_batch.clone()); user_batch.clear(); @@ -4225,16 +4484,16 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { payers.push(user_batch.clone()); } - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, end_era, )); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payers.len() - 1) as u16, @@ -4242,21 +4501,23 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { for batch in payers.iter() { assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_user_index, batch.to_vec(), + MMRProof::default(), )); - for (customer_id, usage) in batch.iter() { + for (customer_id, _bucket_id, usage) in batch.iter() { let charge = calculate_charge_for_month(cluster_id, usage.clone()); System::assert_has_event( Event::Charged { cluster_id, era, - customer_id: *customer_id, + bucket_id: bucket_id1, + customer_id: customer_id.clone(), batch_index: batch_user_index, amount: charge, } @@ -4267,14 +4528,14 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { } let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - let balance1 = Balances::free_balance(report_before.vault); + let balance1 = Balances::free_balance(report_before.vault.clone()); let balance2 = Balances::free_balance(DdcPayouts::account_id()); assert_eq!(balance1, balance2); assert_eq!(report_before.vault, DdcPayouts::account_id()); assert_eq!(balance1 - Balances::minimum_balance(), total_charge); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -4310,7 +4571,7 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, (payees.len() - 1) as u16, @@ -4320,11 +4581,12 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { for batch in payees.iter() { let before_batch = Balances::free_balance(DdcPayouts::account_id()); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_node_index, batch.to_vec(), + MMRProof::default(), )); let mut batch_charge = 0; @@ -4336,8 +4598,8 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { let transfer_charge = ratio1_transfer * report_after.total_customer_charge.transfer; let ratio1_storage = Perquintill::from_rational( - node_usage1.stored_bytes, - total_nodes_usage.stored_bytes, + node_usage1.stored_bytes as u64, + total_nodes_usage.stored_bytes as u64, ); let storage_charge = ratio1_storage * report_after.total_customer_charge.storage; @@ -4373,17 +4635,19 @@ fn send_rewarding_providers_batch_100_nodes_random_usage_works() { #[test] fn end_rewarding_providers_fails_uninitialised() { ExtBuilder.build_and_execute(|| { - let root_account = 1u128; - let dac_account = 2u128; - let user1 = 3u128; - let user2 = 4u128; - let node1 = 33u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); + let user2 = AccountId::from([4; 32]); + let node1 = AccountId::from([33; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 1; let batch_index = 0; - let payers1 = vec![(user1, CustomerUsage::default())]; - let payers2 = vec![(user2, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let payers1 = vec![(user1, bucket_id1, CustomerUsage::default())]; + let payers2 = vec![(user2, bucket_id2, CustomerUsage::default())]; let payees = vec![(node1, NodeUsage::default())]; let total_node_usage = NodeUsage::default(); let start_date = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap(); // April 1st @@ -4407,11 +4671,11 @@ fn end_rewarding_providers_fails_uninitialised() { BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4419,7 +4683,7 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -4428,7 +4692,7 @@ fn end_rewarding_providers_fails_uninitialised() { assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4436,7 +4700,7 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -4444,7 +4708,7 @@ fn end_rewarding_providers_fails_uninitialised() { assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4452,16 +4716,17 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4469,16 +4734,17 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index + 1, payers2, + MMRProof::default(), )); assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4486,14 +4752,14 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4501,7 +4767,7 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -4510,7 +4776,7 @@ fn end_rewarding_providers_fails_uninitialised() { assert_noop!( DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, ), @@ -4518,11 +4784,12 @@ fn end_rewarding_providers_fails_uninitialised() { ); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees, + MMRProof::default(), )); assert_noop!( @@ -4547,13 +4814,14 @@ fn end_rewarding_providers_works() { let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - let dac_account = 2u128; - let user1 = 1u128; - let node1 = 33u128; + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([1; 32]); + let node1 = AccountId::from([33; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 0; let batch_index = 0; + let bucket_id1: BucketId = 1; let usage1 = CustomerUsage { transferred_bytes: 23452345, stored_bytes: 3345234523, @@ -4569,13 +4837,13 @@ fn end_rewarding_providers_works() { number_of_gets: usage1.number_of_gets * 2 / 3, }; let total_node_usage = node_usage1.clone(); - let payers = vec![(user1, usage1)]; + let payers = vec![(user1, bucket_id1, usage1)]; let payees = vec![(node1, node_usage1)]; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -4583,31 +4851,32 @@ fn end_rewarding_providers_works() { )); let mut report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::Initialized); + assert_eq!(report.state, PayoutState::Initialized); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -4615,15 +4884,16 @@ fn end_rewarding_providers_works() { )); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees, + MMRProof::default(), )); assert_ok!(DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -4631,7 +4901,7 @@ fn end_rewarding_providers_works() { System::assert_last_event(Event::RewardingFinished { cluster_id, era }.into()); report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::ProvidersRewarded); + assert_eq!(report.state, PayoutState::ProvidersRewarded); }) } @@ -4644,17 +4914,19 @@ fn end_billing_report_fails_uninitialised() { let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - let root_account = 1u128; - let dac_account = 2u128; - let user1 = 3u128; - let user2 = 4u128; - let node1 = 33u128; + let root_account = AccountId::from([1; 32]); + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); + let user2 = AccountId::from([4; 32]); + let node1 = AccountId::from([33; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 1; let batch_index = 0; - let payers1 = vec![(user1, CustomerUsage::default())]; - let payers2 = vec![(user2, CustomerUsage::default())]; + let bucket_id1: BucketId = 1; + let bucket_id2: BucketId = 2; + let payers1 = vec![(user1, bucket_id1, CustomerUsage::default())]; + let payers2 = vec![(user2, bucket_id2, CustomerUsage::default())]; let payees = vec![(node1, NodeUsage::default())]; let total_node_usage = NodeUsage::default(); @@ -4668,15 +4940,19 @@ fn end_billing_report_fails_uninitialised() { BadOrigin ); - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::BillingReportDoesNotExist ); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -4684,61 +4960,83 @@ fn end_billing_report_fails_uninitialised() { )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers1, + MMRProof::default(), )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index + 1, payers2, + MMRProof::default(), )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -4746,29 +5044,39 @@ fn end_billing_report_fails_uninitialised() { )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees.clone(), + MMRProof::default(), )); assert_noop!( - DdcPayouts::end_billing_report(RuntimeOrigin::signed(dac_account), cluster_id, era,), + DdcPayouts::end_billing_report( + RuntimeOrigin::signed(dac_account.clone()), + cluster_id, + era, + ), Error::::NotExpectedState ); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index + 1, payees, + MMRProof::default(), )); assert_noop!( @@ -4789,21 +5097,22 @@ fn end_billing_report_works() { let start_era: i64 = DateTime::::from_naive_utc_and_offset(start_date.and_time(time), Utc).timestamp(); let end_era: i64 = start_era + (30.44 * 24.0 * 3600.0) as i64; - let dac_account = 2u128; - let user1 = 3u128; - let node1 = 33u128; + let dac_account = AccountId::from([2; 32]); + let user1 = AccountId::from([3; 32]); + let node1 = AccountId::from([33; 32]); let cluster_id = ClusterId::from([12; 20]); let era = 100; let max_batch_index = 0; let batch_index = 0; let total_node_usage = NodeUsage::default(); - let payers = vec![(user1, CustomerUsage::default())]; + let bucket_id1 = 1; + let payers = vec![(user1, bucket_id1, CustomerUsage::default())]; let payees = vec![(node1, NodeUsage::default())]; - assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account.clone())); assert_ok!(DdcPayouts::begin_billing_report( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, start_era, @@ -4811,31 +5120,32 @@ fn end_billing_report_works() { )); let report = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); - assert_eq!(report.state, State::Initialized); + assert_eq!(report.state, PayoutState::Initialized); assert_ok!(DdcPayouts::begin_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, )); assert_ok!(DdcPayouts::send_charging_customers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payers, + MMRProof::default(), )); assert_ok!(DdcPayouts::end_charging_customers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); assert_ok!(DdcPayouts::begin_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, max_batch_index, @@ -4843,15 +5153,16 @@ fn end_billing_report_works() { )); assert_ok!(DdcPayouts::send_rewarding_providers_batch( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, batch_index, payees, + MMRProof::default(), )); assert_ok!(DdcPayouts::end_rewarding_providers( - RuntimeOrigin::signed(dac_account), + RuntimeOrigin::signed(dac_account.clone()), cluster_id, era, )); @@ -4867,6 +5178,6 @@ fn end_billing_report_works() { let report_end = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); assert!(report_end.rewarding_processed_batches.is_empty()); assert!(report_end.charging_processed_batches.is_empty()); - assert_eq!(report_end.state, State::Finalized); + assert_eq!(report_end.state, PayoutState::Finalized); }) } diff --git a/pallets/ddc-payouts/src/weights.rs b/pallets/ddc-payouts/src/weights.rs index 1b9fafa41..063b7141c 100644 --- a/pallets/ddc-payouts/src/weights.rs +++ b/pallets/ddc-payouts/src/weights.rs @@ -1,9 +1,9 @@ //! Autogenerated weights for pallet_ddc_payouts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bench`, CPU: `DO-Premium-AMD` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/cere @@ -11,12 +11,13 @@ // pallet // --chain=dev // --execution=wasm -// --pallet=pallet-ddc-payouts +// --wasm-execution=compiled +// --pallet=pallet_ddc_payouts // --extrinsic=* // --steps=50 // --repeat=20 // --template=./.maintain/frame-weight-template.hbs -// --output=pallets/ddc-payouts/src/weights.rs +// --output=pallets/ddc-payouts/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -41,86 +42,115 @@ pub trait WeightInfo { /// Weights for pallet_ddc_payouts using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: DdcPayouts AuthorisedCaller (r:0 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:0 w:1) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_authorised_caller() -> Weight { - Weight::from_parts(90_258_000_u64, 0) + Weight::from_parts(12_694_000_u64, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_billing_report() -> Weight { - Weight::from_parts(214_646_000_u64, 0) + Weight::from_parts(26_119_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_charging_customers() -> Weight { - Weight::from_parts(228_676_000_u64, 0) + Weight::from_parts(28_303_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: DdcCustomers Ledger (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: DdcPayouts DebtorCustomers (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcCustomers::Ledger` (r:999 w:999) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `DdcPayouts::DebtorCustomers` (r:999 w:999) + // Proof: `DdcPayouts::DebtorCustomers` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1000]`. fn send_charging_customers_batch(b: u32, ) -> Weight { - Weight::from_parts(891_324_000_u64, 0) - // Standard Error: 3_864_375 - .saturating_add(Weight::from_parts(558_679_506_u64, 0).saturating_mul(b as u64)) + Weight::from_parts(135_154_000_u64, 0) + // Standard Error: 318_121 + .saturating_add(Weight::from_parts(79_437_612_u64, 0).saturating_mul(b as u64)) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(b as u64))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(b as u64))) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: System Account (r:3 w:3) - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:3 w:3) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Staking::Validators` (r:2 w:0) + // Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + // Storage: `Staking::Bonded` (r:1 w:0) + // Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + // Storage: `Staking::Ledger` (r:1 w:0) + // Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + // Storage: `Staking::Nominators` (r:1 w:0) + // Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) fn end_charging_customers() -> Weight { - Weight::from_parts(1_691_550_000_u64, 0) + Weight::from_parts(281_087_000_u64, 0) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_rewarding_providers() -> Weight { - Weight::from_parts(234_686_000_u64, 0) + Weight::from_parts(29_155_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: System Account (r:2 w:2) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1000 w:1000) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 1000]`. fn send_rewarding_providers_batch(b: u32, ) -> Weight { - Weight::from_parts(565_710_000_u64, 0) - // Standard Error: 854_032 - .saturating_add(Weight::from_parts(408_429_599_u64, 0).saturating_mul(b as u64)) - .saturating_add(T::DbWeight::get().reads(4_u64)) + Weight::from_parts(91_662_000_u64, 0) + // Standard Error: 34_032 + .saturating_add(Weight::from_parts(62_733_592_u64, 0).saturating_mul(b as u64)) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(b as u64))) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(b as u64))) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn end_rewarding_providers() -> Weight { - Weight::from_parts(274_535_000_u64, 0) + Weight::from_parts(29_986_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn end_billing_report() -> Weight { - Weight::from_parts(232_626_000_u64, 0) + Weight::from_parts(29_615_000_u64, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -128,86 +158,115 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: DdcPayouts AuthorisedCaller (r:0 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:0 w:1) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_authorised_caller() -> Weight { - Weight::from_parts(90_258_000_u64, 0) + Weight::from_parts(12_694_000_u64, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_billing_report() -> Weight { - Weight::from_parts(214_646_000_u64, 0) + Weight::from_parts(26_119_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_charging_customers() -> Weight { - Weight::from_parts(228_676_000_u64, 0) + Weight::from_parts(28_303_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: DdcCustomers Ledger (r:1 w:1) - // Storage: System Account (r:2 w:2) - // Storage: DdcPayouts DebtorCustomers (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcCustomers::Ledger` (r:999 w:999) + // Proof: `DdcCustomers::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `DdcPayouts::DebtorCustomers` (r:999 w:999) + // Proof: `DdcPayouts::DebtorCustomers` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1000]`. fn send_charging_customers_batch(b: u32, ) -> Weight { - Weight::from_parts(891_324_000_u64, 0) - // Standard Error: 3_864_375 - .saturating_add(Weight::from_parts(558_679_506_u64, 0).saturating_mul(b as u64)) + Weight::from_parts(135_154_000_u64, 0) + // Standard Error: 318_121 + .saturating_add(Weight::from_parts(79_437_612_u64, 0).saturating_mul(b as u64)) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(b as u64))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(b as u64))) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: System Account (r:3 w:3) - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: Staking Validators (r:2 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: Staking Ledger (r:1 w:0) - // Storage: Staking Nominators (r:1 w:0) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:3 w:3) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Staking::Validators` (r:2 w:0) + // Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + // Storage: `Staking::Bonded` (r:1 w:0) + // Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + // Storage: `Staking::Ledger` (r:1 w:0) + // Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + // Storage: `Staking::Nominators` (r:1 w:0) + // Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) fn end_charging_customers() -> Weight { - Weight::from_parts(1_691_550_000_u64, 0) + Weight::from_parts(281_087_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn begin_rewarding_providers() -> Weight { - Weight::from_parts(234_686_000_u64, 0) + Weight::from_parts(29_155_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) - // Storage: System Account (r:2 w:2) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1000 w:1000) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 1000]`. fn send_rewarding_providers_batch(b: u32, ) -> Weight { - Weight::from_parts(565_710_000_u64, 0) - // Standard Error: 854_032 - .saturating_add(Weight::from_parts(408_429_599_u64, 0).saturating_mul(b as u64)) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + Weight::from_parts(91_662_000_u64, 0) + // Standard Error: 34_032 + .saturating_add(Weight::from_parts(62_733_592_u64, 0).saturating_mul(b as u64)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(b as u64))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(b as u64))) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn end_rewarding_providers() -> Weight { - Weight::from_parts(274_535_000_u64, 0) + Weight::from_parts(29_986_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) - // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: `DdcPayouts::AuthorisedCaller` (r:1 w:0) + // Proof: `DdcPayouts::AuthorisedCaller` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `DdcPayouts::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcPayouts::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) fn end_billing_report() -> Weight { - Weight::from_parts(232_626_000_u64, 0) + Weight::from_parts(29_615_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/pallets/ddc-staking/Cargo.toml b/pallets/ddc-staking/Cargo.toml index 8e1b4dc6e..027584665 100644 --- a/pallets/ddc-staking/Cargo.toml +++ b/pallets/ddc-staking/Cargo.toml @@ -11,12 +11,14 @@ repository.workspace = true [dependencies] # 3rd-party dependencies codec = { workspace = true } +hex-literal = { workspace = true } scale-info = { workspace = true } # Substrate dependencies frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } +log = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } @@ -27,6 +29,10 @@ ddc-primitives = { workspace = true } [dev-dependencies] lazy_static = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } +pallet-contracts = { workspace = true } +pallet-ddc-clusters = { workspace = true, default-features = true } +pallet-ddc-nodes = { workspace = true, default-features = true } +pallet-insecure-randomness-collective-flip = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } parking_lot = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } diff --git a/pallets/ddc-staking/src/benchmarking.rs b/pallets/ddc-staking/src/benchmarking.rs index 59f347114..ecd280e2a 100644 --- a/pallets/ddc-staking/src/benchmarking.rs +++ b/pallets/ddc-staking/src/benchmarking.rs @@ -1,6 +1,9 @@ //! DdcStaking pallet benchmarking. -use ddc_primitives::{NodeParams, NodeType, StorageNodeMode, StorageNodeParams, StorageNodePubKey}; +use ddc_primitives::{ + ClusterParams, ClusterProtocolParams, NodeParams, NodeType, StorageNodeMode, StorageNodeParams, + StorageNodePubKey, +}; pub use frame_benchmarking::{ account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; @@ -15,6 +18,22 @@ use crate::Pallet as DdcStaking; const USER_SEED: u32 = 999666; +fn next_block() { + frame_system::Pallet::::set_block_number( + frame_system::Pallet::::block_number() + BlockNumberFor::::from(1_u32), + ); +} + +fn fast_forward_to(n: BlockNumberFor) { + while frame_system::Pallet::::block_number() < n { + next_block::(); + } +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + benchmarks! { bond { let stash = create_funded_user::("stash", USER_SEED, 100); @@ -28,7 +47,7 @@ benchmarks! { NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, host: vec![1u8; 255], - domain: vec![2u8; 256], + domain: vec![2u8; 255], ssl: true, http_port: 35000u16, grpc_port: 25000u16, @@ -78,7 +97,7 @@ benchmarks! { store { let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([0; 32])); - let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or(100u128), node_pub_key)?; + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterProtocol::get_bond_size(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or(100u128), node_pub_key)?; whitelist_account!(controller); }: _(RawOrigin::Signed(controller), ClusterId::from([1; 20])) @@ -92,12 +111,12 @@ benchmarks! { clear_activated_nodes::(); let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([0; 32])); - let (storage_stash, storage_controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or(10u128), node_pub_key)?; + let (storage_stash, storage_controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterProtocol::get_bond_size(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or(10u128), node_pub_key)?; DdcStaking::::store(RawOrigin::Signed(storage_controller.clone()).into(), ClusterId::from([1; 20]))?; assert!(Storages::::contains_key(&storage_stash)); frame_system::Pallet::::set_block_number(BlockNumberFor::::from(1u32)); DdcStaking::::chill(RawOrigin::Signed(storage_controller.clone()).into())?; - frame_system::Pallet::::set_block_number(BlockNumberFor::::from(1u32) + T::ClusterVisitor::get_chill_delay(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or_else(|_| BlockNumberFor::::from(10u32))); + frame_system::Pallet::::set_block_number(BlockNumberFor::::from(1u32) + T::ClusterProtocol::get_chill_delay(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or_else(|_| BlockNumberFor::::from(10u32))); whitelist_account!(storage_controller); }: _(RawOrigin::Signed(storage_controller)) @@ -124,9 +143,106 @@ benchmarks! { assert!(Nodes::::contains_key(&new_node)); } - impl_benchmark_test_suite!( - DdcStaking, - crate::mock::ExtBuilder::default().build(), - crate::mock::Test, - ); + fast_chill { + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([1; 32])); + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterProtocol::get_bond_size(&ClusterId::from([1; 20]), NodeType::Storage).unwrap_or(100u128), node_pub_key)?; + DdcStaking::::store(RawOrigin::Signed(controller.clone()).into(), ClusterId::from([1; 20]))?; + + whitelist_account!(controller); + }: _(RawOrigin::Signed(controller.clone())) + verify { + let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; + let chilling = ledger.chilling; + assert!(chilling.is_some()); + } + + bond_cluster { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5000); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5000); + + T::ClusterCreator::create_cluster( + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0, + }, + ClusterProtocolParams::default() + )?; + + whitelist_account!(cluster_reserve_id); + + }: _(RawOrigin::Signed(cluster_reserve_id.clone()), cluster_id) + verify { + assert!(ClusterBonded::::contains_key(&cluster_reserve_id)); + assert!(ClusterLedger::::contains_key(&cluster_manager_id)); + let amount = T::ClusterBondingAmount::get(); + assert_last_event::(Event::Bonded(cluster_reserve_id, amount).into()); + } + + unbond_cluster { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5000); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5000); + + T::ClusterCreator::create_cluster( + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0, + }, + ClusterProtocolParams::default() + )?; + + DdcStaking::::bond_cluster(RawOrigin::Signed(cluster_reserve_id.clone()).into(), cluster_id)?; + + whitelist_account!(cluster_manager_id); + + }: _(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id) + verify { + let amount = T::ClusterBondingAmount::get(); + assert_last_event::(Event::Unbonded(cluster_reserve_id, amount).into()); + } + + withdraw_unbonded_cluster { + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = create_funded_user_with_balance::("cluster-controller", 0, 5000); + let cluster_reserve_id = create_funded_user_with_balance::("cluster-stash", 0, 5000); + + T::ClusterCreator::create_cluster( + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0, + }, + ClusterProtocolParams::default() + )?; + + DdcStaking::::bond_cluster(RawOrigin::Signed(cluster_reserve_id.clone()).into(), cluster_id)?; + next_block::(); + + DdcStaking::::unbond_cluster(RawOrigin::Signed(cluster_manager_id.clone()).into(), cluster_id)?; + fast_forward_to::(frame_system::Pallet::::block_number() + T::ClusterUnboningDelay::get() + BlockNumberFor::::from(1_u32)); + + whitelist_account!(cluster_reserve_id); + + }: _(RawOrigin::Signed(cluster_manager_id.clone()), cluster_id) + verify { + assert!(!ClusterBonded::::contains_key(&cluster_reserve_id)); + assert!(!ClusterLedger::::contains_key(&cluster_manager_id)); + let amount = T::ClusterBondingAmount::get(); + assert_last_event::(Event::Withdrawn(cluster_reserve_id, amount).into()); + } } diff --git a/pallets/ddc-staking/src/lib.rs b/pallets/ddc-staking/src/lib.rs index 65f8639e7..ee4ef92e1 100644 --- a/pallets/ddc-staking/src/lib.rs +++ b/pallets/ddc-staking/src/lib.rs @@ -23,16 +23,20 @@ pub(crate) mod mock; #[cfg(test)] mod tests; +const LOG_TARGET: &str = "runtime::ddc-staking"; + +pub mod migrations; + pub mod weights; use core::fmt::Debug; use codec::{Decode, Encode, HasCompact}; use ddc_primitives::traits::{ - cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + cluster::{ClusterCreator, ClusterProtocol, ClusterQuery}, node::{NodeCreator, NodeVisitor}, staking::{StakerCreator, StakingVisitor, StakingVisitorError}, }; -pub use ddc_primitives::{ClusterId, NodePubKey, NodeType}; +pub use ddc_primitives::{ClusterId, ClusterNodesCount, NodePubKey, NodeType}; use frame_support::{ assert_ok, pallet_prelude::*, @@ -50,7 +54,8 @@ use sp_std::prelude::*; use crate::weights::WeightInfo; -const DDC_STAKING_ID: LockIdentifier = *b"ddcstake"; // DDC maintainer's stake +const DDC_CLUSTER_STAKING_ID: LockIdentifier = *b"clrstake"; // DDC clusters stake +const DDC_NODE_STAKING_ID: LockIdentifier = *b"ddcstake"; // DDC clusters maintainer's stake /// The balance type of this pallet. pub type BalanceOf = @@ -146,13 +151,13 @@ impl< #[frame_support::pallet] pub mod pallet { - use ddc_primitives::traits::{cluster::ClusterManager, node::NodeVisitorError}; + use ddc_primitives::traits::cluster::ClusterManager; use super::*; /// The current storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = - frame_support::traits::StorageVersion::new(0); + frame_support::traits::StorageVersion::new(1); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -168,7 +173,7 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - type ClusterVisitor: ClusterVisitor; + type ClusterProtocol: ClusterProtocol>; type ClusterCreator: ClusterCreator>; @@ -177,6 +182,10 @@ pub mod pallet { type NodeVisitor: NodeVisitor; type NodeCreator: NodeCreator; + + type ClusterBondingAmount: Get>; + + type ClusterUnboningDelay: Get>; } /// Map from all locked "stash" accounts to the controller account. @@ -211,15 +220,28 @@ pub mod pallet { #[pallet::getter(fn leaving_storages)] pub type LeavingStorages = StorageMap<_, Twox64Concat, T::AccountId, ClusterId>; + /// Map from all clusters locked "stash" accounts to the controller account. + #[pallet::storage] + #[pallet::getter(fn cluster_bonded)] + pub type ClusterBonded = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; + + /// Map of all clusters staking ledgers. + #[pallet::storage] + #[pallet::getter(fn cluster_ledger)] + pub type ClusterLedger = + StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger, T>>; + #[pallet::genesis_config] pub struct GenesisConfig { #[allow(clippy::type_complexity)] pub storages: Vec<(T::AccountId, T::AccountId, NodePubKey, BalanceOf, ClusterId)>, + #[allow(clippy::type_complexity)] + pub clusters: Vec<(T::AccountId, T::AccountId, ClusterId)>, } impl Default for GenesisConfig { fn default() -> Self { - GenesisConfig { storages: Default::default() } + GenesisConfig { storages: Default::default(), clusters: Default::default() } } } @@ -243,6 +265,39 @@ pub mod pallet { cluster, )); } + + for &(ref cluster_stash, ref cluster_controller, _cluster) in &self.clusters { + let amount = T::ClusterBondingAmount::get(); + + assert!( + !>::contains_key(cluster_stash), + "Cluster is already bonded" + ); + + assert!( + !>::contains_key(cluster_controller), + "Cluster ledger is already exists" + ); + + assert!( + T::Currency::free_balance(cluster_stash) >= amount, + "Cluster Stash do not have enough balance to participate in storage network." + ); + + assert_ok!(frame_system::Pallet::::inc_consumers(cluster_stash)); + + >::insert(cluster_stash, cluster_controller); + + let ledger = StakingLedger { + stash: cluster_stash.clone(), + total: amount, + active: amount, + chilling: Default::default(), + unlocking: Default::default(), + }; + + Pallet::::update_cluster_ledger(cluster_controller, &ledger); + } } } @@ -320,6 +375,7 @@ pub mod pallet { /// Action is prohibited for a node provider stash account that is in the process of /// leaving a cluster NodeIsLeaving, + UnbondingProhibited, } #[pallet::call] @@ -436,8 +492,8 @@ pub mod pallet { let min_active_bond = if let Some(cluster_id) = Self::storages(&ledger.stash) { let bond_size = - T::ClusterVisitor::get_bond_size(&cluster_id, NodeType::Storage) - .map_err(Into::>::into)?; + T::ClusterProtocol::get_bond_size(&cluster_id, NodeType::Storage) + .map_err(|_| Error::::NoClusterGovParams)?; bond_size.saturated_into::>() } else { // If node is not assigned to a cluster or node is chilling, allow to unbond @@ -454,11 +510,11 @@ pub mod pallet { let unbonding_delay = if T::NodeVisitor::exists(&node_pub_key) { let node_cluster_id = T::NodeVisitor::get_cluster_id(&node_pub_key) - .map_err(Into::>::into)?; + .map_err(|_| Error::::NoCluster)?; if let Some(cluster_id) = node_cluster_id { - let bonding_params = T::ClusterVisitor::get_bonding_params(&cluster_id) - .map_err(Into::>::into)?; + let bonding_params = T::ClusterProtocol::get_bonding_params(&cluster_id) + .map_err(|_| Error::::NoClusterGovParams)?; let min_bond_size = match node_pub_key { NodePubKey::StoragePubKey(_) => bonding_params.storage_bond_size, @@ -536,7 +592,7 @@ pub mod pallet { // left. We can now safely remove all staking-related information. Self::kill_stash(&stash)?; // Remove the lock. - T::Currency::remove_lock(DDC_STAKING_ID, &stash); + T::Currency::remove_lock(DDC_NODE_STAKING_ID, &stash); } else { // This was the consequence of a partial unbond. just update the ledger and move on. Self::update_ledger(&controller, &ledger); @@ -578,12 +634,15 @@ pub mod pallet { pub fn store(origin: OriginFor, cluster_id: ClusterId) -> DispatchResult { let controller = ensure_signed(origin)?; - T::ClusterVisitor::ensure_cluster(&cluster_id).map_err(Into::>::into)?; + ensure!( + >::cluster_exists(&cluster_id), + Error::::NoCluster + ); let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; // Retrieve the respective bond size from Cluster Visitor - let bond_size = T::ClusterVisitor::get_bond_size(&cluster_id, NodeType::Storage) - .map_err(Into::>::into)?; + let bond_size = T::ClusterProtocol::get_bond_size(&cluster_id, NodeType::Storage) + .map_err(|_| Error::::NoClusterGovParams)?; ensure!( ledger.active >= bond_size.saturated_into::>(), Error::::InsufficientBond @@ -641,8 +700,8 @@ pub mod pallet { // Extract delay from the cluster settings. let (cluster, delay) = if let Some(cluster) = Self::storages(&ledger.stash) { - let chill_delay = T::ClusterVisitor::get_chill_delay(&cluster, NodeType::Storage) - .map_err(Into::>::into)?; + let chill_delay = T::ClusterProtocol::get_chill_delay(&cluster, NodeType::Storage) + .map_err(|_| Error::::NoClusterGovParams)?; (cluster, chill_delay) } else { return Ok(()); // node is already chilling or leaving the cluster @@ -751,7 +810,8 @@ pub mod pallet { let cluster_id = >::get(&stash).ok_or(Error::::NodeHasNoStake)?; - let is_cluster_node = T::ClusterManager::contains_node(&cluster_id, &node_pub_key); + let is_cluster_node = + T::ClusterManager::contains_node(&cluster_id, &node_pub_key, None); ensure!(!is_cluster_node, Error::::FastChillProhibited); // block number + 1 => no overflow @@ -761,10 +821,140 @@ pub mod pallet { Ok(()) } + + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::bond_cluster())] + pub fn bond_cluster(origin: OriginFor, cluster_id: ClusterId) -> DispatchResult { + let cluster_stash = ensure_signed(origin)?; + let (controller, stash) = + >::get_manager_and_reserve_id(&cluster_id)?; + + ensure!(stash == cluster_stash, Error::::NotStash); + + if >::contains_key(&stash) { + Err(Error::::AlreadyBonded)? + } + + if >::contains_key(&controller) { + Err(Error::::AlreadyPaired)? + } + + let amount = T::ClusterBondingAmount::get(); + + // Reject a bond which is considered to be _dust_. + if amount < T::Currency::minimum_balance() { + Err(Error::::InsufficientBond)? + } + + T::ClusterProtocol::bond_cluster(&cluster_id)?; + + frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; + + >::insert(&stash, &controller); + + let balance = T::Currency::free_balance(&stash); + if balance < amount { + return Err(Error::::InsufficientBond)?; + } + + Self::deposit_event(Event::::Bonded(stash.clone(), amount)); + let ledger = StakingLedger { + stash, + total: amount, + active: amount, + chilling: Default::default(), + unlocking: Default::default(), + }; + Self::update_cluster_ledger(&controller, &ledger); + Ok(()) + } + + #[pallet::call_index(9)] + #[pallet::weight(T::WeightInfo::unbond_cluster())] + pub fn unbond_cluster(origin: OriginFor, cluster_id: ClusterId) -> DispatchResult { + let cluster_controller = ensure_signed(origin)?; + let controller = T::ClusterManager::get_manager_account_id(&cluster_id)?; + + ensure!(controller == cluster_controller, Error::::NotController); + + let mut ledger = Self::cluster_ledger(&controller).ok_or(Error::::NotController)?; + ensure!( + ledger.unlocking.len() < MaxUnlockingChunks::get() as usize, + Error::::NoMoreChunks, + ); + + T::ClusterProtocol::start_unbond_cluster(&cluster_id)?; + + // Unbond the full amount + let amount = ledger.active; + ledger.active = + ledger.active.checked_sub(&amount).ok_or(Error::::ArithmeticUnderflow)?; + + let unbonding_delay = T::ClusterUnboningDelay::get(); + + // block number + configuration -> no overflow + let block = >::block_number() + unbonding_delay; + if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.block == block) { + // To keep the chunk count down, we only keep one chunk per block. Since + // `unlocking` is a FiFo queue, if a chunk exists for `block` we know that it + // will be the last one. + chunk.value = chunk.value.defensive_saturating_add(amount) + } else { + ledger + .unlocking + .try_push(UnlockChunk { value: amount, block }) + .map_err(|_| Error::::NoMoreChunks)?; + }; + + Self::update_cluster_ledger(&controller, &ledger); + + Self::deposit_event(Event::::Unbonded(ledger.stash, amount)); + + Ok(()) + } + + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::withdraw_unbonded_cluster())] + pub fn withdraw_unbonded_cluster( + origin: OriginFor, + cluster_id: ClusterId, + ) -> DispatchResult { + let cluster_controller = ensure_signed(origin)?; + let controller = T::ClusterManager::get_manager_account_id(&cluster_id)?; + ensure!(controller == cluster_controller, Error::::NotController); + + let mut ledger = Self::cluster_ledger(&controller).ok_or(Error::::NotController)?; + let (stash, old_total) = (ledger.stash.clone(), ledger.total); + + ledger = ledger.consolidate_unlocked(>::block_number()); + + if ledger.unlocking.is_empty() && ledger.active < T::Currency::minimum_balance() { + // This account must have called `unbond_cluster()` with some value that caused the + // active portion to fall below existential deposit + will have no more unlocking + // chunks left. We can now safely remove all staking-related information. + Self::kill_cluster_stash(&stash)?; + // Remove the lock. + T::Currency::remove_lock(DDC_CLUSTER_STAKING_ID, &stash); + T::ClusterProtocol::end_unbond_cluster(&cluster_id)?; + } else { + // This was the consequence of a partial unbond. just update the ledger and move on. + Self::update_cluster_ledger(&controller, &ledger); + }; + + // `old_total` should never be less than the new total because + // `consolidate_unlocked` strictly subtracts balance. + if ledger.total < old_total { + // Already checked that this won't overflow by entry condition. + let value = old_total - ledger.total; + Self::deposit_event(Event::::Withdrawn(stash, value)); + } + + Ok(()) + } } impl Pallet { - /// Update the ledger for a controller. + /// Update the ledger for a node controller. /// /// This will also update the stash lock. fn update_ledger( @@ -772,7 +962,7 @@ pub mod pallet { ledger: &StakingLedger, T>, ) { T::Currency::set_lock( - DDC_STAKING_ID, + DDC_NODE_STAKING_ID, &ledger.stash, ledger.total, WithdrawReasons::all(), @@ -780,6 +970,22 @@ pub mod pallet { >::insert(controller, ledger); } + /// Update the ledger for a cluster. + /// + /// This will also update the stash lock. + fn update_cluster_ledger( + controller: &T::AccountId, + ledger: &StakingLedger, T>, + ) { + T::Currency::set_lock( + DDC_CLUSTER_STAKING_ID, + &ledger.stash, + ledger.total, + WithdrawReasons::all(), + ); + >::insert(controller, ledger); + } + /// Chill a stash account. fn chill_stash(stash: &T::AccountId) { let chilled_as_storage = Self::do_remove_storage(stash); @@ -827,6 +1033,21 @@ pub mod pallet { Ok(()) } + /// Remove all associated data of a cluster stash account from the staking system. + /// + /// This is called: + /// - after a `withdraw_unbonded_cluster()` call that frees all of a stash's bonded balance. + fn kill_cluster_stash(stash: &T::AccountId) -> DispatchResult { + let controller = >::get(stash).ok_or(Error::::NotStash)?; + + >::remove(stash); + >::remove(&controller); + + frame_system::Pallet::::dec_consumers(stash); + + Ok(()) + } + /// This function will add a storage network participant to the `Storages` storage map. /// /// If the storage network participant already exists, their cluster will be updated. @@ -861,7 +1082,7 @@ pub mod pallet { ) -> DispatchResult { Nodes::::insert(&node, &stash); Providers::::insert(&stash, &node); - >::insert(&stash, &controller); + Bonded::::insert(&stash, &controller); let stash_balance = T::Currency::free_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded(stash.clone(), value)); @@ -879,6 +1100,26 @@ pub mod pallet { Ok(()) } + + fn bond_cluster( + cluster_stash: T::AccountId, + cluster_controller: T::AccountId, + cluster_id: ClusterId, + ) -> DispatchResult { + ClusterBonded::::insert(&cluster_stash, &cluster_controller); + let amount = T::ClusterBondingAmount::get(); + Self::deposit_event(Event::::Bonded(cluster_stash.clone(), amount)); + let ledger = StakingLedger { + stash: cluster_stash, + total: amount, + active: amount, + chilling: Default::default(), + unlocking: Default::default(), + }; + Self::update_cluster_ledger(&cluster_controller, &ledger); + T::ClusterProtocol::bond_cluster(&cluster_id)?; + Ok(()) + } } impl StakingVisitor for Pallet { @@ -913,22 +1154,11 @@ pub mod pallet { Ok(is_chilling_attempt) } - } - - impl From for Error { - fn from(error: ClusterVisitorError) -> Self { - match error { - ClusterVisitorError::ClusterDoesNotExist => Error::::NoCluster, - ClusterVisitorError::ClusterGovParamsNotSet => Error::::NoClusterGovParams, - } - } - } - impl From for Error { - fn from(error: NodeVisitorError) -> Self { - match error { - NodeVisitorError::NodeDoesNotExist => Error::::NodeIsNotFound, - } + fn stash_by_ctrl(controller: &T::AccountId) -> Result { + Self::ledger(controller) + .map(|l| l.stash) + .ok_or(StakingVisitorError::ControllerDoesNotExist) } } } diff --git a/pallets/ddc-staking/src/migrations.rs b/pallets/ddc-staking/src/migrations.rs new file mode 100644 index 000000000..6260ec042 --- /dev/null +++ b/pallets/ddc-staking/src/migrations.rs @@ -0,0 +1,196 @@ +pub mod v1 { + #[cfg(feature = "try-runtime")] + use ddc_primitives::ClusterStatus; + use ddc_primitives::{ + traits::{ClusterProtocol, ClusterQuery, NodeVisitor}, + ClusterId, NodePubKey, + }; + use frame_support::{ + pallet_prelude::*, + traits::{Currency, LockableCurrency, OnRuntimeUpgrade, WithdrawReasons}, + weights::Weight, + }; + #[cfg(feature = "try-runtime")] + use hex_literal::hex; + use sp_runtime::Saturating; + use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + + use crate::{ + ClusterBonded, ClusterLedger, Config, Nodes, Pallet, StakingLedger, DDC_CLUSTER_STAKING_ID, + LOG_TARGET, + }; + + #[cfg(feature = "try-runtime")] + const DEVNET_CLUSTER: [u8; 20] = hex!("7f82864e4f097e63d04cc279e4d8d2eb45a42ffa"); + #[cfg(feature = "try-runtime")] + const TESTNET_CLUSTER: [u8; 20] = hex!("825c4b2352850de9986d9d28568db6f0c023a1e3"); + #[cfg(feature = "try-runtime")] + const MAINNET_CLUSTER: [u8; 20] = hex!("0059f5ada35eee46802d80750d5ca4a490640511"); + #[cfg(feature = "try-runtime")] + const KNOWN_ACTIVE_CLUSTERS: [[u8; 20]; 3] = [DEVNET_CLUSTER, TESTNET_CLUSTER, MAINNET_CLUSTER]; + + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> Weight { + let current_version = Pallet::::current_storage_version(); + let onchain_version = Pallet::::on_chain_storage_version(); + let mut weight = T::DbWeight::get().reads(1); + + if onchain_version == 0 && current_version == 1 { + let cluster_bonding_amount = T::ClusterBondingAmount::get(); + let minimum_balance = T::Currency::minimum_balance(); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if cluster_bonding_amount < minimum_balance { + return weight; + } + + let mut bonded_nodes: Vec = Vec::new(); + let mut bonded_nodes_count = 0u64; + Nodes::::iter().for_each(|(node_pub_key, _)| { + bonded_nodes_count.saturating_inc(); + bonded_nodes.push(node_pub_key); + }); + weight.saturating_accrue(T::DbWeight::get().reads(bonded_nodes_count)); + + let mut served_clusters: BTreeMap = BTreeMap::new(); + for node_pub_key in bonded_nodes.iter() { + if let Ok(Some(cluster_id)) = T::NodeVisitor::get_cluster_id(node_pub_key) { + served_clusters.insert(cluster_id, ()); + } + weight.saturating_accrue(T::DbWeight::get().reads(1)); + } + + for (cluster_id, _) in served_clusters.iter() { + if let Ok((cluster_controller, cluster_stash)) = + >::get_manager_and_reserve_id( + cluster_id, + ) { + let cluster_stash_balance = T::Currency::free_balance(&cluster_stash); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + if cluster_stash_balance >= cluster_bonding_amount { + if T::ClusterProtocol::bond_cluster(cluster_id).is_ok() { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } else { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + continue; + } + + if frame_system::Pallet::::inc_consumers(&cluster_stash).is_ok() { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } else { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + continue; + } + + >::insert(&cluster_stash, &cluster_controller); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + let ledger = StakingLedger { + stash: cluster_stash, + total: cluster_bonding_amount, + active: cluster_bonding_amount, + chilling: Default::default(), + unlocking: Default::default(), + }; + + T::Currency::set_lock( + DDC_CLUSTER_STAKING_ID, + &ledger.stash, + ledger.total, + WithdrawReasons::all(), + ); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + >::insert(cluster_controller, ledger); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + if T::ClusterProtocol::activate_cluster_protocol(cluster_id).is_ok() { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } else { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + continue; + } + } + } + weight.saturating_accrue(T::DbWeight::get().reads(1)); + } + + // Update storage version. + StorageVersion::new(1).put::>(); + weight + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); + + weight + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + frame_support::ensure!( + Pallet::::on_chain_storage_version() == 0, + "must upgrade linearly" + ); + let pre_clusters_bonded_count = ClusterBonded::::iter().count(); + let pre_clusters_ledgers_count = ClusterLedger::::iter().count(); + + assert_eq!( + pre_clusters_bonded_count, 0, + "clusters bonds should be empty before the migration" + ); + + assert_eq!( + pre_clusters_ledgers_count, 0, + "clusters ledgers should be empty before the migration" + ); + + let mut clusters_to_activate: Vec = Vec::new(); + for bytes_id in KNOWN_ACTIVE_CLUSTERS.iter() { + let cluster_id = ClusterId::from(bytes_id); + if >::cluster_exists(&cluster_id) { + clusters_to_activate.push(cluster_id); + } + } + + Ok(clusters_to_activate.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), DispatchError> { + let clusters_to_activate: Vec = Decode::decode(&mut &state[..]).expect( + "the state parameter should be something that was generated by pre_upgrade", + ); + + for cluster_id in clusters_to_activate.iter() { + let (cluster_controller, cluster_stash) = + >::get_manager_and_reserve_id(cluster_id) + .expect("no controller and stash accounts found for activating cluster"); + + assert_eq!( + >::get(cluster_stash) + .expect("binding is not created for activating cluster"), + cluster_controller + ); + + let ledger = >::get(cluster_controller) + .expect("staking ledger is not created for activating cluster"); + + let bonding_amount = T::ClusterBondingAmount::get(); + assert_eq!(ledger.total, bonding_amount); + assert_eq!(ledger.active, bonding_amount); + + let cluster_status = + >::get_cluster_status(cluster_id) + .expect("no activating cluster found"); + + assert_eq!(cluster_status, ClusterStatus::Activated); + } + + Ok(()) + } + } +} diff --git a/pallets/ddc-staking/src/mock.rs b/pallets/ddc-staking/src/mock.rs index a66f7fca3..b1cb4729c 100644 --- a/pallets/ddc-staking/src/mock.rs +++ b/pallets/ddc-staking/src/mock.rs @@ -2,37 +2,31 @@ #![allow(dead_code)] -use std::cell::RefCell; - use ddc_primitives::{ - traits::{ - cluster::{ClusterManager, ClusterManagerError, ClusterVisitor, ClusterVisitorError}, - node::{NodeVisitor, NodeVisitorError}, - }, - ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterParams, ClusterPricingParams, - NodeParams, NodePubKey, StorageNodePubKey, + ClusterNodeKind, ClusterNodeStatus, ClusterParams, ClusterProtocolParams, ClusterStatus, + NodeParams, NodePubKey, StorageNodeParams, StorageNodePubKey, }; use frame_support::{ construct_runtime, - dispatch::DispatchResult, - traits::{ConstU32, ConstU64, Everything}, + traits::{ConstBool, ConstU32, ConstU64, Everything, Nothing}, weights::constants::RocksDbWeight, }; use frame_system::mocking::{MockBlock, MockUncheckedExtrinsic}; -use lazy_static::lazy_static; -use parking_lot::{ReentrantMutex, ReentrantMutexGuard}; +use pallet_ddc_clusters::cluster::Cluster; +use pallet_ddc_nodes::StorageNode; use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perquintill, + traits::{BlakeTwo256, Convert, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, Perbill, Perquintill, }; -use sp_std::collections::btree_map::BTreeMap; use crate::{self as pallet_ddc_staking, *}; +pub type Signature = MultiSignature; + /// The AccountId alias in this test module. -pub(crate) type AccountId = u64; +pub(crate) type AccountId = <::Signer as IdentifyAccount>::AccountId; pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u128; @@ -46,14 +40,30 @@ construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event, HoldReason}, + Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, DdcStaking: pallet_ddc_staking::{Pallet, Call, Config, Storage, Event}, + DdcNodes: pallet_ddc_nodes::{Pallet, Call, Storage, Event}, + DdcClusters: pallet_ddc_clusters::{Pallet, Call, Storage, Config, Event}, } ); parameter_types! { pub static ExistentialDeposit: Balance = 1; + pub static ClusterBondingAmount: Balance = 50; + pub static ClusterUnboningDelay: BlockNumber = 2; +} + +impl Convert> for Test { + fn convert(w: Weight) -> BalanceOf { + w.ref_time().into() + } } +type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + impl frame_system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); @@ -91,9 +101,10 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); - type MaxHolds = (); - type RuntimeHoldReason = (); + type MaxHolds = ConstU32<1>; + type RuntimeHoldReason = RuntimeHoldReason; } impl pallet_timestamp::Config for Test { @@ -103,285 +114,377 @@ impl pallet_timestamp::Config for Test { type WeightInfo = (); } -impl crate::pallet::Config for Test { +parameter_types! { + pub const DepositPerItem: Balance = 0; + pub const DepositPerByte: Balance = 0; + pub const SignedClaimHandicap: BlockNumber = 2; + pub const TombstoneDeposit: Balance = 16; + pub const StorageSizeOffset: u32 = 8; + pub const RentByteFee: Balance = 4; + pub const RentDepositOffset: Balance = 10_000; + pub const SurchargeReward: Balance = 150; + pub const MaxDepth: u32 = 100; + pub const MaxValueSize: u32 = 16_384; + pub Schedule: pallet_contracts::Schedule = Default::default(); + pub static DefaultDepositLimit: Balance = 10_000_000; + pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + pub const MaxDelegateDependencies: u32 = 32; +} + +impl pallet_contracts::Config for Test { + type Time = Timestamp; + type Randomness = Randomness; type Currency = Balances; type RuntimeEvent = RuntimeEvent; + type CallStack = [pallet_contracts::Frame; 5]; + type WeightPrice = Self; //pallet_transaction_payment::Module; type WeightInfo = (); - type ClusterVisitor = TestClusterVisitor; - type ClusterManager = TestClusterManager; - type NodeVisitor = MockNodeVisitor; - type NodeCreator = TestNodeCreator; - type ClusterCreator = TestClusterCreator; + type ChainExtension = (); + type Schedule = Schedule; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type DefaultDepositLimit = DefaultDepositLimit; + type AddressGenerator = pallet_contracts::DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxStorageKeyLen = ConstU32<128>; + type UnsafeUnstableInterface = ConstBool; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type MaxDelegateDependencies = MaxDelegateDependencies; + type RuntimeHoldReason = RuntimeHoldReason; + type Debug = (); + type Environment = (); + type Migrations = (); + type Xcm = (); } -pub(crate) type DdcStakingCall = crate::Call; -pub(crate) type TestRuntimeCall = ::RuntimeCall; -pub struct TestNodeCreator; -pub struct TestClusterCreator; -pub struct TestClusterVisitor; - -impl NodeCreator for TestNodeCreator { - fn create_node( - _node_pub_key: NodePubKey, - _provider_id: T::AccountId, - _node_params: NodeParams, - ) -> DispatchResult { - Ok(()) - } -} +impl pallet_insecure_randomness_collective_flip::Config for Test {} -impl ClusterCreator for TestClusterCreator { - fn create_new_cluster( - _cluster_id: ClusterId, - _cluster_manager_id: T::AccountId, - _cluster_reserve_id: T::AccountId, - _cluster_params: ClusterParams, - _cluster_gov_params: ClusterGovParams>, - ) -> DispatchResult { - Ok(()) - } +impl pallet_ddc_nodes::Config for Test { + type RuntimeEvent = RuntimeEvent; + type StakingVisitor = pallet_ddc_staking::Pallet; + type WeightInfo = (); } -impl ClusterVisitor for TestClusterVisitor { - fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { - Ok(()) - } - fn get_bond_size( - _cluster_id: &ClusterId, - _node_type: NodeType, - ) -> Result { - Ok(10) - } - fn get_chill_delay( - _cluster_id: &ClusterId, - _node_type: NodeType, - ) -> Result, ClusterVisitorError> { - Ok(BlockNumberFor::::from(10u32)) - } - fn get_unbonding_delay( - _cluster_id: &ClusterId, - _node_type: NodeType, - ) -> Result, ClusterVisitorError> { - Ok(BlockNumberFor::::from(10u32)) - } - - fn get_pricing_params( - _cluster_id: &ClusterId, - ) -> Result { - Ok(ClusterPricingParams { - unit_per_mb_stored: 2, - unit_per_mb_streamed: 3, - unit_per_put_request: 4, - unit_per_get_request: 5, - }) - } - - fn get_fees_params(_cluster_id: &ClusterId) -> Result { - Ok(ClusterFeesParams { - treasury_share: Perquintill::from_percent(1), - validators_share: Perquintill::from_percent(10), - cluster_reserve_share: Perquintill::from_percent(2), - }) - } - - fn get_reserve_account_id( - _cluster_id: &ClusterId, - ) -> Result { - Err(ClusterVisitorError::ClusterDoesNotExist) - } +impl pallet_ddc_clusters::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NodeRepository = pallet_ddc_nodes::Pallet; + type StakingVisitor = pallet_ddc_staking::Pallet; + type StakerCreator = pallet_ddc_staking::Pallet; + type Currency = Balances; + type WeightInfo = (); + type MinErasureCodingRequiredLimit = ConstU32<0>; + type MinErasureCodingTotalLimit = ConstU32<0>; + type MinReplicationTotalLimit = ConstU32<0>; +} - fn get_bonding_params( - cluster_id: &ClusterId, - ) -> Result>, ClusterVisitorError> { - Ok(ClusterBondingParams { - storage_bond_size: >::get_bond_size( - cluster_id, - NodeType::Storage, - ) - .unwrap_or_default(), - storage_chill_delay: >::get_chill_delay( - cluster_id, - NodeType::Storage, - ) - .unwrap_or_default(), - storage_unbonding_delay: - >::get_unbonding_delay( - cluster_id, - NodeType::Storage, - ) - .unwrap_or_default(), - }) - } +impl crate::pallet::Config for Test { + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ClusterProtocol = pallet_ddc_clusters::Pallet; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type NodeCreator = pallet_ddc_nodes::Pallet; + type ClusterBondingAmount = ClusterBondingAmount; + type ClusterUnboningDelay = ClusterUnboningDelay; } -pub struct TestClusterManager; -impl ClusterManager for TestClusterManager { - fn contains_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { - true - } +pub(crate) type DdcStakingCall = crate::Call; +pub(crate) type TestRuntimeCall = ::RuntimeCall; - fn add_node( - _cluster_id: &ClusterId, - _node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { - Ok(()) - } +// (stash, controller, cluster) +#[allow(clippy::type_complexity)] +pub(crate) type BuiltClusterBond = (AccountId, AccountId, ClusterId); +// (stash, controller, node, stake, cluster) +#[allow(clippy::type_complexity)] +pub(crate) type BuiltNodeBond = (AccountId, AccountId, NodePubKey, Balance, ClusterId); - fn remove_node( - _cluster_id: &ClusterId, - _node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError> { - Ok(()) - } -} +#[allow(clippy::type_complexity)] +pub(crate) type BuiltCluster = (Cluster, ClusterProtocolParams); -lazy_static! { - // We have to use the ReentrantMutex as every test's thread that needs to perform some configuration on the mock acquires the lock at least 2 times: - // the first time when the mock configuration happens, and - // the second time when the pallet calls the MockNodeVisitor during execution - static ref MOCK_NODE: ReentrantMutex> = - ReentrantMutex::new(RefCell::new(MockNode::default())); -} +#[allow(clippy::type_complexity)] +pub(crate) type BuiltNode = (NodePubKey, StorageNode, Option); -pub struct MockNode { - pub cluster_id: Option, - pub exists: bool, +pub(crate) struct ClusterAssignment { + pub(crate) cluster_id: [u8; 20], + pub(crate) status: ClusterNodeStatus, + pub(crate) kind: ClusterNodeKind, } -impl Default for MockNode { - fn default() -> Self { - Self { cluster_id: None, exists: true } - } +pub const NODE_KEY_1: [u8; 32] = [12; 32]; +pub const NODE_STASH_1: [u8; 32] = [11; 32]; +pub const NODE_CONTROLLER_1: [u8; 32] = [10; 32]; + +pub const NODE_KEY_2: [u8; 32] = [22; 32]; +pub const NODE_STASH_2: [u8; 32] = [21; 32]; +pub const NODE_CONTROLLER_2: [u8; 32] = [20; 32]; + +pub const NODE_KEY_3: [u8; 32] = [32; 32]; +pub const NODE_STASH_3: [u8; 32] = [31; 32]; +pub const NODE_CONTROLLER_3: [u8; 32] = [30; 32]; + +pub const NODE_KEY_4: [u8; 32] = [42; 32]; +pub const NODE_STASH_4: [u8; 32] = [41; 32]; +pub const NODE_CONTROLLER_4: [u8; 32] = [40; 32]; + +pub const CLUSTER_ID: [u8; 20] = [1; 20]; +pub const CLUSTER_STASH: [u8; 32] = [102; 32]; +pub const CLUSTER_CONTROLLER: [u8; 32] = [101; 32]; + +pub const USER_KEY_1: [u8; 32] = [1; 32]; +pub const USER_KEY_2: [u8; 32] = [2; 32]; +pub const USER_KEY_3: [u8; 32] = [3; 32]; +pub const USER_KEY_4: [u8; 32] = [4; 32]; + +pub const NODE_KEY_5: [u8; 32] = [52; 32]; +pub const NODE_KEY_6: [u8; 32] = [62; 32]; + +pub const ENDOWMENT: u128 = 100; + +pub(crate) fn build_default_setup( +) -> (Vec, Vec, Vec, Vec) { + ( + vec![build_cluster( + CLUSTER_ID, + CLUSTER_STASH, + CLUSTER_CONTROLLER, + ClusterParams::default(), + ClusterProtocolParams { + treasury_share: Perquintill::from_percent(1), + validators_share: Perquintill::from_percent(10), + cluster_reserve_share: Perquintill::from_percent(2), + storage_bond_size: 10, + storage_chill_delay: 10u32.into(), + storage_unbonding_delay: 10u32.into(), + unit_per_mb_stored: 2, + unit_per_mb_streamed: 3, + unit_per_put_request: 4, + unit_per_get_request: 5, + }, + ClusterStatus::Activated, + )], + vec![ + build_node( + NODE_KEY_1, + NODE_CONTROLLER_1, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ), + build_node( + NODE_KEY_2, + NODE_CONTROLLER_2, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ), + build_node( + NODE_KEY_3, + NODE_CONTROLLER_3, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ), + build_node( + NODE_KEY_4, + NODE_CONTROLLER_4, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ), + ], + vec![build_cluster_bond(CLUSTER_STASH, CLUSTER_CONTROLLER, CLUSTER_ID)], + vec![ + build_node_bond(NODE_STASH_1, NODE_CONTROLLER_1, NODE_KEY_1, ENDOWMENT, CLUSTER_ID), + build_node_bond(NODE_STASH_2, NODE_CONTROLLER_2, NODE_KEY_2, ENDOWMENT, CLUSTER_ID), + build_node_bond(NODE_STASH_3, NODE_CONTROLLER_3, NODE_KEY_3, ENDOWMENT, CLUSTER_ID), + build_node_bond(NODE_STASH_4, NODE_CONTROLLER_4, NODE_KEY_4, ENDOWMENT, CLUSTER_ID), + ], + ) } -pub struct MockNodeVisitor; - -impl MockNodeVisitor { - // Every test's thread must hold the lock till the end of its test - pub fn set_and_hold_lock(mock: MockNode) -> ReentrantMutexGuard<'static, RefCell> { - let lock = MOCK_NODE.lock(); - *lock.borrow_mut() = mock; - lock - } +pub(crate) fn build_cluster( + cluster_id: [u8; 20], + manager_id: [u8; 32], + reserve_id: [u8; 32], + params: ClusterParams, + protocol_params: ClusterProtocolParams, + status: ClusterStatus, +) -> BuiltCluster { + let mut cluster = Cluster::new( + ClusterId::from(cluster_id), + AccountId::from(manager_id), + AccountId::from(reserve_id), + params, + ); + cluster.status = status; + (cluster, protocol_params) +} - // Every test's thread must release the lock that it previously acquired in the end of its - // test - pub fn reset_and_release_lock(lock: ReentrantMutexGuard<'static, RefCell>) { - *lock.borrow_mut() = MockNode::default(); - } +pub fn build_node( + pub_key: [u8; 32], + provider_id: [u8; 32], + params: StorageNodeParams, + assignment: Option, +) -> BuiltNode { + let key = NodePubKey::StoragePubKey(AccountId::from(pub_key)); + let mut node = StorageNode::new( + key.clone(), + AccountId::from(provider_id), + NodeParams::StorageParams(params), + ) + .unwrap(); + + let cluster_id_opt = if let Some(ClusterAssignment { cluster_id, .. }) = assignment { + Some(ClusterId::from(cluster_id)) + } else { + None + }; + node.cluster_id = cluster_id_opt; + + (key, node, assignment) } -impl NodeVisitor for MockNodeVisitor { - fn get_cluster_id(_node_pub_key: &NodePubKey) -> Result, NodeVisitorError> { - let lock = MOCK_NODE.lock(); - let mock_ref = lock.borrow(); - Ok(mock_ref.cluster_id) - } - fn exists(_node_pub_key: &NodePubKey) -> bool { - let lock = MOCK_NODE.lock(); - let mock_ref = lock.borrow(); - mock_ref.exists - } +pub(crate) fn build_cluster_bond( + stash: [u8; 32], + controller: [u8; 32], + cluster_id: [u8; 20], +) -> BuiltClusterBond { + (AccountId::from(stash), AccountId::from(controller), ClusterId::from(cluster_id)) } -pub struct ExtBuilder { - has_storages: bool, - stakes: BTreeMap, - storages: Vec<(AccountId, AccountId, Balance, ClusterId)>, +pub(crate) fn build_node_bond( + stash: [u8; 32], + controller: [u8; 32], + node_key: [u8; 32], + bond: Balance, + cluster_id: [u8; 20], +) -> BuiltNodeBond { + ( + AccountId::from(stash), + AccountId::from(controller), + NodePubKey::StoragePubKey(StorageNodePubKey::from(node_key)), + bond, + ClusterId::from(cluster_id), + ) } -impl Default for ExtBuilder { - fn default() -> Self { - Self { has_storages: true, stakes: Default::default(), storages: Default::default() } +fn insert_unique_balance( + account_vec: &mut Vec<(AccountId, Balance)>, + account_id: AccountId, + balance: Balance, +) { + if !account_vec.iter().any(|(id, _)| *id == account_id) { + account_vec.push((account_id, balance)); } } +pub struct ExtBuilder; + impl ExtBuilder { - pub fn has_storages(mut self, has: bool) -> Self { - self.has_storages = has; - self - } - pub fn set_stake(mut self, who: AccountId, stake: Balance) -> Self { - self.stakes.insert(who, stake); - self - } - pub fn add_storage( - mut self, - stash: AccountId, - controller: AccountId, - stake: Balance, - cluster: ClusterId, - ) -> Self { - self.storages.push((stash, controller, stake, cluster)); - self - } - pub fn build(self) -> TestExternalities { + pub fn build( + self, + clusts: Vec, + nodes: Vec, + clusters_bonds: Vec, + nodes_bondes: Vec, + ) -> TestExternalities { sp_tracing::try_init_simple(); - let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let _ = pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 100), - (2, 100), - (3, 100), - (4, 100), - // storage controllers - (10, 100), - (20, 100), - (30, 100), - (40, 100), - // storage stashes - (11, 100), - (21, 100), - (31, 100), - (41, 100), - ], + let mut balances: Vec<(AccountId, Balance)> = vec![ + (AccountId::from(USER_KEY_1), ENDOWMENT), + (AccountId::from(USER_KEY_2), ENDOWMENT), + (AccountId::from(USER_KEY_3), ENDOWMENT), + (AccountId::from(USER_KEY_4), ENDOWMENT), + ]; + + let mut storage_nodes = Vec::new(); + let mut clusters_storage_nodes = Vec::new(); + for (pub_key, node, cluster_assignment) in nodes.iter() { + storage_nodes.push(node.clone()); + if let Some(ClusterAssignment { cluster_id, kind, status }) = cluster_assignment { + clusters_storage_nodes.push(( + ClusterId::from(cluster_id), + vec![(pub_key.clone(), kind.clone(), status.clone())], + )); + } } - .assimilate_storage(&mut storage); - let mut storages = vec![]; - if self.has_storages { - storages = vec![ - // (stash, controller, node, stake, cluster) - ( - 11, - 10, - NodePubKey::StoragePubKey(StorageNodePubKey::new([12; 32])), - 100, - ClusterId::from([1; 20]), - ), - ( - 21, - 20, - NodePubKey::StoragePubKey(StorageNodePubKey::new([22; 32])), - 100, - ClusterId::from([1; 20]), - ), - ( - 31, - 30, - NodePubKey::StoragePubKey(StorageNodePubKey::new([32; 32])), - 100, - ClusterId::from([1; 20]), - ), - ( - 41, - 40, - NodePubKey::StoragePubKey(StorageNodePubKey::new([42; 32])), - 100, - ClusterId::from([1; 20]), - ), - ]; + + let mut clusters = Vec::new(); + let mut clusters_protocol_params = Vec::new(); + for (cluster, cluster_protocol_params) in clusts.iter() { + clusters.push(cluster.clone()); + clusters_protocol_params.push((cluster.cluster_id, cluster_protocol_params.clone())); + } + + for cluster in clusters.iter() { + insert_unique_balance(&mut balances, cluster.manager_id.clone(), ENDOWMENT); + insert_unique_balance(&mut balances, cluster.reserve_id.clone(), ENDOWMENT); + } + + for (stash, controller, _) in clusters_bonds.iter() { + insert_unique_balance(&mut balances, stash.clone(), ENDOWMENT); + insert_unique_balance(&mut balances, controller.clone(), ENDOWMENT); + } + + for (_, node, _) in nodes.iter() { + insert_unique_balance(&mut balances, node.provider_id.clone(), ENDOWMENT); + } + + for (stash, controller, _, _, _) in nodes_bondes.iter() { + insert_unique_balance(&mut balances, stash.clone(), ENDOWMENT); + insert_unique_balance(&mut balances, controller.clone(), ENDOWMENT); } let _ = - pallet_ddc_staking::GenesisConfig:: { storages }.assimilate_storage(&mut storage); + pallet_balances::GenesisConfig:: { balances }.assimilate_storage(&mut storage); + + let _ = pallet_ddc_nodes::GenesisConfig:: { storage_nodes } + .assimilate_storage(&mut storage); + + let _ = pallet_ddc_clusters::GenesisConfig:: { + clusters, + clusters_protocol_params, + clusters_nodes: clusters_storage_nodes, + } + .assimilate_storage(&mut storage); + + let _ = pallet_ddc_staking::GenesisConfig:: { + storages: nodes_bondes, + clusters: clusters_bonds, + } + .assimilate_storage(&mut storage); TestExternalities::new(storage) } - pub fn build_and_execute(self, test: impl FnOnce()) { + + pub fn build_and_execute( + self, + clusters: Vec, + clusters_nodes: Vec, + clusters_bonds: Vec, + nodes_bondes: Vec, + test: impl FnOnce(), + ) { sp_tracing::try_init_simple(); - let mut ext = self.build(); + let mut ext = self.build(clusters, clusters_nodes, clusters_bonds, nodes_bondes); ext.execute_with(test); ext.execute_with(post_condition); } @@ -398,7 +501,7 @@ fn check_ledgers() { fn assert_ledger_consistent(controller: AccountId) { // ensures ledger.total == ledger.active + sum(ledger.unlocking). - let ledger = DdcStaking::ledger(controller).expect("Not a controller."); + let ledger = DdcStaking::ledger(controller.clone()).expect("Not a controller."); let real_total: Balance = ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value); assert_eq!(real_total, ledger.total); assert!( diff --git a/pallets/ddc-staking/src/testing_utils.rs b/pallets/ddc-staking/src/testing_utils.rs index c302b2350..71c6e4ad5 100644 --- a/pallets/ddc-staking/src/testing_utils.rs +++ b/pallets/ddc-staking/src/testing_utils.rs @@ -1,8 +1,8 @@ //! Testing utils for ddc-staking. use ddc_primitives::{ - ClusterGovParams, ClusterId, ClusterParams, NodeParams, StorageNodeMode, StorageNodeParams, - StorageNodePubKey, + ClusterId, ClusterParams, ClusterProtocolParams, NodeParams, StorageNodeMode, + StorageNodeParams, StorageNodePubKey, }; use frame_benchmarking::account; use frame_support::traits::Currency; @@ -63,7 +63,7 @@ pub fn create_stash_controller_node( NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, host: vec![1u8; 255], - domain: vec![2u8; 256], + domain: vec![2u8; 255], ssl: true, http_port: 35000u16, grpc_port: 25000u16, @@ -100,7 +100,7 @@ pub fn create_stash_controller_node_with_balance( NodeParams::StorageParams(StorageNodeParams { mode: StorageNodeMode::Storage, host: vec![1u8; 255], - domain: vec![2u8; 256], + domain: vec![2u8; 255], ssl: true, http_port: 35000u16, grpc_port: 25000u16, @@ -117,24 +117,25 @@ pub fn create_stash_controller_node_with_balance( erasure_coding_total: 6, replication_total: 3, }; - let cluster_gov_params: ClusterGovParams, BlockNumberFor> = ClusterGovParams { - treasury_share: Perquintill::default(), - validators_share: Perquintill::default(), - cluster_reserve_share: Perquintill::default(), - storage_bond_size: 10u32.into(), - storage_chill_delay: 50u32.into(), - storage_unbonding_delay: 50u32.into(), - unit_per_mb_stored: 10, - unit_per_mb_streamed: 10, - unit_per_put_request: 10, - unit_per_get_request: 10, - }; - T::ClusterCreator::create_new_cluster( + let cluster_protocol_params: ClusterProtocolParams, BlockNumberFor> = + ClusterProtocolParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + storage_bond_size: 10u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + T::ClusterCreator::create_cluster( cluster_id, stash.clone(), stash.clone(), cluster_params, - cluster_gov_params, + cluster_protocol_params, )?; DdcStaking::::bond( diff --git a/pallets/ddc-staking/src/tests.rs b/pallets/ddc-staking/src/tests.rs index b1cace762..164e70aff 100644 --- a/pallets/ddc-staking/src/tests.rs +++ b/pallets/ddc-staking/src/tests.rs @@ -1,8 +1,15 @@ //! Tests for the module. -use ddc_primitives::StorageNodePubKey; +use ddc_primitives::{ + ClusterNodeKind, ClusterNodeStatus, ClusterParams, ClusterProtocolParams, ClusterStatus, + DdcEra, StorageNodeParams, StorageNodePubKey, +}; use frame_support::{assert_noop, assert_ok, traits::ReservableCurrency}; use pallet_balances::Error as BalancesError; +use pallet_ddc_clusters::{ + cluster::{Cluster, ClusterProps}, + Error as ClustersError, +}; use super::{mock::*, *}; @@ -11,35 +18,41 @@ pub const INIT_TIMESTAMP: u64 = 30_000; #[test] fn test_default_staking_ledger() { - // Verifies initial conditions of mock - ExtBuilder::default().build_and_execute(|| { + let (clusters, nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { let default_staking_ledger = StakingLedger::< ::AccountId, BalanceOf, Test, - >::default_from(1); + >::default_from(AccountId::from(USER_KEY_1)); // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(default_staking_ledger.stash, 1); + assert_eq!(default_staking_ledger.stash, AccountId::from(USER_KEY_1)); assert_eq!(default_staking_ledger.total, Zero::zero()); }); } #[test] fn basic_setup_works() { - // Verifies initial conditions of mock - ExtBuilder::default().build_and_execute(|| { + let (clusters, nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(DdcStaking::bonded(11), Some(10)); + assert_eq!( + DdcStaking::bonded(AccountId::from(NODE_STASH_1)), + Some(AccountId::from(NODE_CONTROLLER_1)) + ); // Account 21 is stashed and locked, and account 20 is the controller - assert_eq!(DdcStaking::bonded(21), Some(20)); + assert_eq!( + DdcStaking::bonded(AccountId::from(NODE_STASH_2)), + Some(AccountId::from(NODE_CONTROLLER_2)) + ); // Account 1 is not a stashed - assert_eq!(DdcStaking::bonded(1), None); + assert_eq!(DdcStaking::bonded(AccountId::from(USER_KEY_1)), None); // Account 10 controls the stash from account 11, which is 100 units assert_eq!( - DdcStaking::ledger(10), + DdcStaking::ledger(AccountId::from(NODE_CONTROLLER_1)), Some(StakingLedger { - stash: 11, + stash: AccountId::from(NODE_STASH_1), total: 100, active: 100, chilling: Default::default(), @@ -48,9 +61,9 @@ fn basic_setup_works() { ); // Account 20 controls the stash from account 21, which is 100 units assert_eq!( - DdcStaking::ledger(20), + DdcStaking::ledger(AccountId::from(NODE_CONTROLLER_2)), Some(StakingLedger { - stash: 21, + stash: AccountId::from(NODE_STASH_2), total: 100, active: 100, chilling: Default::default(), @@ -58,140 +71,194 @@ fn basic_setup_works() { }) ); // Account 1 does not control any stash - assert_eq!(DdcStaking::ledger(1), None); + assert_eq!(DdcStaking::ledger(AccountId::from(USER_KEY_1)), None); }); } #[test] fn change_controller_works() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { // 10 and 11 are bonded as stash controller. - assert_eq!(DdcStaking::bonded(11), Some(10)); + assert_eq!( + DdcStaking::bonded(AccountId::from(NODE_STASH_1)), + Some(AccountId::from(NODE_CONTROLLER_1)) + ); // 10 can control 11 who is initially a validator. - assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(10))); + assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(AccountId::from( + NODE_CONTROLLER_1 + )))); // Change controller. - assert_ok!(DdcStaking::set_controller(RuntimeOrigin::signed(11), 3)); + assert_ok!(DdcStaking::set_controller( + RuntimeOrigin::signed(AccountId::from(NODE_STASH_1)), + AccountId::from(USER_KEY_3) + )); assert_noop!( - DdcStaking::set_controller(RuntimeOrigin::signed(11), 3), + DdcStaking::set_controller( + RuntimeOrigin::signed(AccountId::from(NODE_STASH_1)), + AccountId::from(USER_KEY_3) + ), Error::::AlreadyPaired ); - assert_eq!(DdcStaking::bonded(11), Some(3)); + assert_eq!( + DdcStaking::bonded(AccountId::from(NODE_STASH_1)), + Some(AccountId::from(USER_KEY_3)) + ); // 10 is no longer in control. assert_noop!( - DdcStaking::store(RuntimeOrigin::signed(10), ClusterId::from([1; 20])), + DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(NODE_CONTROLLER_1)), + ClusterId::from(CLUSTER_ID) + ), Error::::NotController ); // 3 is a new controller. - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(3), ClusterId::from([1; 20]))); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + ClusterId::from(CLUSTER_ID) + )); }) } #[test] fn not_enough_inital_bond_flow() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node(NODE_KEY_5, USER_KEY_4, StorageNodeParams::default(), None); + let node_6 = build_node(NODE_KEY_6, USER_KEY_2, StorageNodeParams::default(), None); + nodes.push(node_5); + nodes.push(node_6); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); // Add new Storage participant, account 3 controlled by 4 with node 5. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 5 )); // Not enough tokens bonded to serve assert_noop!( - DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20])), + DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + ), Error::::InsufficientBond ); - // Add new Storage participant, account 1 controlled by 2 with node 3. + // Add new Storage participant, account 1 controlled by 2 with node 6. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(1), - 2, - NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + AccountId::from(USER_KEY_2), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_6)), 100 )); // Not enough tokens bonded to store assert_noop!( - DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20])), + DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + ), Error::::InsufficientBond ); // Can not bond extra assert_noop!( DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 5 ), Error::::AlreadyBonded ); // Unbond all bonded amount - assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(4), 5)); - System::assert_last_event(Event::Unbonded(3, 5).into()); + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), 5)); + System::assert_last_event(Event::Unbonded(AccountId::from(USER_KEY_3), 5).into()); System::set_block_number(11); // Withdraw unbonded tokens to clear up the stash controller pair - assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(4))); - System::assert_last_event(Event::Withdrawn(3, 5).into()); + assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(AccountId::from( + USER_KEY_4 + )))); + System::assert_last_event(Event::Withdrawn(AccountId::from(USER_KEY_3), 5).into()); // Bond sufficient amount assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 10 )); // Serving should work - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20]))); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + )); }) } #[test] fn unbonding_edge_cases_work() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node(NODE_KEY_5, USER_KEY_4, StorageNodeParams::default(), None); + nodes.push(node_5); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); // Add new Storage participant, account 3 controlled by 4 with node 5. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 100 )); - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20]))); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + )); - assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1)); + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), 1)); while System::block_number() < 33 { - assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1)); - System::assert_last_event(Event::Unbonded(3, 1).into()); + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), 1)); + System::assert_last_event(Event::Unbonded(AccountId::from(USER_KEY_3), 1).into()); System::set_block_number(System::block_number() + 1); } - assert_noop!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1), Error::::NoMoreChunks); + assert_noop!( + DdcStaking::unbond(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), 1), + Error::::NoMoreChunks + ); }) } #[test] fn set_node_works() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); // 10 and 11 are bonded as stash controller. - assert_eq!(DdcStaking::bonded(11), Some(10)); + assert_eq!( + DdcStaking::bonded(AccountId::from(NODE_STASH_1)), + Some(AccountId::from(NODE_CONTROLLER_1)) + ); // Node is already paired assert_noop!( DdcStaking::set_node( - RuntimeOrigin::signed(10), - NodePubKey::StoragePubKey(StorageNodePubKey::new([12; 32])) + RuntimeOrigin::signed(AccountId::from(NODE_CONTROLLER_1)), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_1)) ), Error::::AlreadyPaired ); @@ -199,21 +266,21 @@ fn set_node_works() { // Node cannot be changed assert_noop!( DdcStaking::set_node( - RuntimeOrigin::signed(11), - NodePubKey::StoragePubKey(StorageNodePubKey::new([12; 32])) + RuntimeOrigin::signed(AccountId::from(NODE_STASH_1)), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_1)) ), Error::::AlreadyInRole ); // Schedule Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(10))); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(NODE_CONTROLLER_1)))); System::set_block_number(11); // Actual Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(10))); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(NODE_CONTROLLER_1)))); // Setting node should work assert_ok!(DdcStaking::set_node( - RuntimeOrigin::signed(11), + RuntimeOrigin::signed(AccountId::from(NODE_STASH_1)), NodePubKey::StoragePubKey(StorageNodePubKey::new([13; 32])) )); }) @@ -221,59 +288,97 @@ fn set_node_works() { #[test] fn cancel_previous_chill_works() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node(NODE_KEY_5, USER_KEY_4, StorageNodeParams::default(), None); + let node_6 = build_node(NODE_KEY_6, USER_KEY_2, StorageNodeParams::default(), None); + + nodes.push(node_5); + nodes.push(node_6); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); - let cluster_id = ClusterId::from([1; 20]); + let cluster_id = ClusterId::from(CLUSTER_ID); // Add new Storage participant, account 3 controlled by 4 with node 5. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 100 )); - // Add new Storage participant, account 1 controlled by 2 with node 3. + // Add new Storage participant, account 1 controlled by 2 with node 6. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(1), - 2, - NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + AccountId::from(USER_KEY_2), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_6)), 100 )); // Not enough tokens bonded to serve - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), cluster_id)); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + cluster_id + )); - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(2), ClusterId::from([1; 20]))); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_2)), + ClusterId::from(CLUSTER_ID) + )); // Schedule Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(4))); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)))); // Not enough tokens bonded to serve - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), cluster_id)); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + cluster_id + )); // Schedule Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(2))); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_2)))); // Not enough tokens bonded to serve - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(2), cluster_id)); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_2)), + cluster_id + )); }) } #[test] fn staking_should_work() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node( + NODE_KEY_5, + USER_KEY_4, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ); + + let node_6 = build_node(NODE_KEY_6, USER_KEY_4, StorageNodeParams::default(), None); + nodes.push(node_5); + nodes.push(node_6); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); // Put some money in account that we'll use. - for i in 1..5 { - let _ = Balances::make_free_balance_be(&i, 2000); - } + let _ = Balances::make_free_balance_be(&AccountId::from(USER_KEY_1), 2000); + let _ = Balances::make_free_balance_be(&AccountId::from(USER_KEY_2), 2000); + let _ = Balances::make_free_balance_be(&AccountId::from(USER_KEY_3), 2000); + let _ = Balances::make_free_balance_be(&AccountId::from(USER_KEY_4), 2000); // Bond dust should fail assert_noop!( DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 0 ), Error::::InsufficientBond @@ -281,22 +386,28 @@ fn staking_should_work() { // Add new Storage participant, account 3 controlled by 4 with node 5. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(3), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 1500 )); let events = System::events(); - assert_eq!(events[events.len() - 2].event, Event::Bonded(3, 1500).into()); - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([0; 20]))); - System::assert_last_event(Event::Activated(3).into()); + assert_eq!( + events[events.len() - 2].event, + Event::Bonded(AccountId::from(USER_KEY_3), 1500).into() + ); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + )); + System::assert_last_event(Event::Activated(AccountId::from(USER_KEY_3)).into()); // Controller already paired assert_noop!( DdcStaking::bond( - RuntimeOrigin::signed(5), - 4, - NodePubKey::StoragePubKey(StorageNodePubKey::new([10; 32])), + RuntimeOrigin::signed(AccountId::from([115; 32])), + AccountId::from(USER_KEY_4), + NodePubKey::StoragePubKey(StorageNodePubKey::new([117; 32])), 10 ), Error::::AlreadyPaired @@ -305,9 +416,9 @@ fn staking_should_work() { // Node already paired assert_noop!( DdcStaking::bond( - RuntimeOrigin::signed(5), - 6, - NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), + RuntimeOrigin::signed(AccountId::from([115; 32])), + AccountId::from([116; 32]), + NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)), 10 ), Error::::AlreadyPaired @@ -315,38 +426,46 @@ fn staking_should_work() { // Account 4 controls the stash from account 3, which is 1500 units, 3 is a Storage // participant, 5 is a DDC node. - assert_eq!(DdcStaking::bonded(3), Some(4)); assert_eq!( - DdcStaking::ledger(4), + DdcStaking::bonded(AccountId::from(USER_KEY_3)), + Some(AccountId::from(USER_KEY_4)) + ); + assert_eq!( + DdcStaking::ledger(AccountId::from(USER_KEY_4)), Some(StakingLedger { - stash: 3, + stash: AccountId::from(USER_KEY_3), total: 1500, active: 1500, chilling: Default::default(), unlocking: Default::default(), }) ); - assert_eq!(DdcStaking::storages(3), Some(ClusterId::from([0; 20]))); assert_eq!( - DdcStaking::nodes(NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32]))), - Some(3) + DdcStaking::storages(AccountId::from(USER_KEY_3)), + Some(ClusterId::from(CLUSTER_ID)) + ); + assert_eq!( + DdcStaking::nodes(NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5))), + Some(AccountId::from(USER_KEY_3)) ); // Set initial block timestamp. Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); // Schedule Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(4))); - System::assert_last_event(Event::ChillSoon(3, ClusterId::from([0; 20]), 11).into()); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)))); + System::assert_last_event( + Event::ChillSoon(AccountId::from(USER_KEY_3), ClusterId::from(CLUSTER_ID), 11).into(), + ); // Removal is scheduled, stashed value of 4 is still lock. let chilling = System::block_number() + 10u64; - // TestClusterVisitor::get_chill_delay(&ClusterId::from([1; 20]), NodeType::Storage) + // TestClusterProtocol::get_chill_delay(&ClusterId::from([1; 20]), NodeType::Storage) // .unwrap_or(10_u64); assert_eq!( - DdcStaking::ledger(4), + DdcStaking::ledger(AccountId::from(USER_KEY_4)), Some(StakingLedger { - stash: 3, + stash: AccountId::from(USER_KEY_3), total: 1500, active: 1500, chilling: Some(chilling), @@ -354,15 +473,21 @@ fn staking_should_work() { }) ); // It cannot reserve more than 500 that it has free from the total 2000 - assert_noop!(Balances::reserve(&3, 501), BalancesError::::LiquidityRestrictions); - assert_ok!(Balances::reserve(&3, 409)); + assert_noop!( + Balances::reserve(&AccountId::from(USER_KEY_3), 501), + BalancesError::::LiquidityRestrictions + ); + assert_ok!(Balances::reserve(&AccountId::from(USER_KEY_3), 409)); // Too early to call chill the second time - assert_noop!(DdcStaking::chill(RuntimeOrigin::signed(4)), Error::::TooEarly); + assert_noop!( + DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_4))), + Error::::TooEarly + ); // Fast chill should not be allowed assert_noop!( - DdcStaking::fast_chill(RuntimeOrigin::signed(4)), + DdcStaking::fast_chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_4))), Error::::FastChillProhibited ); @@ -374,9 +499,9 @@ fn staking_should_work() { // Ledger is not changed until we make another call to `chill`. assert_eq!( - DdcStaking::ledger(4), + DdcStaking::ledger(AccountId::from(USER_KEY_4)), Some(StakingLedger { - stash: 3, + stash: AccountId::from(USER_KEY_3), total: 1500, active: 1500, chilling: Some(chilling), @@ -385,62 +510,77 @@ fn staking_should_work() { ); // Actual Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(4))); - System::assert_last_event(Event::Chilled(3).into()); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(AccountId::from(USER_KEY_4)))); + System::assert_last_event(Event::Chilled(AccountId::from(USER_KEY_3)).into()); // Account 3 is no longer a Storage participant. - assert_eq!(DdcStaking::storages(3), None); + assert_eq!(DdcStaking::storages(AccountId::from(USER_KEY_3)), None); }); } #[test] fn storage_full_unbonding_works() { - ExtBuilder::default().build_and_execute(|| { + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node( + NODE_KEY_5, + USER_KEY_4, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ); + + nodes.push(node_5); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { System::set_block_number(1); - let provider_stash: u64 = 3; - let provider_controller: u64 = 4; - let cluster_id = ClusterId::from([1; 20]); - let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])); - - let lock = MockNodeVisitor::set_and_hold_lock(MockNode { - cluster_id: Some(cluster_id), - exists: true, - }); + let provider_stash = AccountId::from(USER_KEY_3); + let provider_controller = AccountId::from(USER_KEY_4); + let cluster_id = ClusterId::from(CLUSTER_ID); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)); let storage_bond_size = 10_u128; let storage_chill_delay = 10_u64; let storage_unbond_delay = 10_u64; // Put some money in account that we'll use. - let _ = Balances::make_free_balance_be(&provider_controller, 2000); - let _ = Balances::make_free_balance_be(&provider_stash, 2000); + let _ = Balances::make_free_balance_be(&provider_controller.clone(), 2000); + let _ = Balances::make_free_balance_be(&provider_stash.clone(), 2000); // Add new Storage participant, account 1 controlled by 2 with node 1. assert_ok!(DdcStaking::bond( - RuntimeOrigin::signed(provider_stash), - provider_controller, + RuntimeOrigin::signed(provider_stash.clone()), + provider_controller.clone(), node_pub_key.clone(), storage_bond_size, // min bond size )); let events = System::events(); assert_eq!( events[events.len() - 2].event, - Event::Bonded(provider_stash, storage_bond_size).into() + Event::Bonded(provider_stash.clone(), storage_bond_size).into() ); - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(provider_controller), cluster_id)); - System::assert_last_event(Event::Activated(provider_stash).into()); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(provider_controller.clone()), + cluster_id + )); + System::assert_last_event(Event::Activated(provider_stash.clone()).into()); - assert_eq!(DdcStaking::storages(provider_stash), Some(cluster_id)); - assert_eq!(DdcStaking::nodes(node_pub_key), Some(provider_stash)); + assert_eq!(DdcStaking::storages(provider_stash.clone()), Some(cluster_id)); + assert_eq!(DdcStaking::nodes(node_pub_key), Some(provider_stash.clone())); // Set block timestamp. Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); // Schedule Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller.clone()))); let chilling = System::block_number() + storage_chill_delay; - System::assert_last_event(Event::ChillSoon(provider_stash, cluster_id, chilling).into()); + System::assert_last_event( + Event::ChillSoon(provider_stash.clone(), cluster_id, chilling).into(), + ); // Set the block number that allows us to chill. while System::block_number() < chilling { @@ -449,20 +589,22 @@ fn storage_full_unbonding_works() { } // Actual Storage participant removal. - assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); - System::assert_last_event(Event::Chilled(provider_stash).into()); + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller.clone()))); + System::assert_last_event(Event::Chilled(provider_stash.clone()).into()); // Account is no longer a Storage participant. - assert_eq!(DdcStaking::storages(provider_stash), None); + assert_eq!(DdcStaking::storages(provider_stash.clone()), None); // Start unbonding all tokens assert_ok!(DdcStaking::unbond( - RuntimeOrigin::signed(provider_controller), + RuntimeOrigin::signed(provider_controller.clone()), storage_bond_size )); - System::assert_has_event(Event::LeaveSoon(provider_stash).into()); - assert_eq!(DdcStaking::leaving_storages(provider_stash), Some(cluster_id)); - System::assert_last_event(Event::Unbonded(provider_stash, storage_bond_size).into()); + System::assert_has_event(Event::LeaveSoon(provider_stash.clone()).into()); + assert_eq!(DdcStaking::leaving_storages(provider_stash.clone()), Some(cluster_id)); + System::assert_last_event( + Event::Unbonded(provider_stash.clone(), storage_bond_size).into(), + ); let unbonding = System::block_number() + storage_unbond_delay; // Set the block number that allows us to chill. @@ -471,24 +613,26 @@ fn storage_full_unbonding_works() { Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); } - assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(provider_controller))); - System::assert_has_event(Event::Withdrawn(provider_stash, storage_bond_size).into()); - assert_eq!(DdcStaking::leaving_storages(provider_stash), None); + assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed( + provider_controller.clone() + ))); + System::assert_has_event( + Event::Withdrawn(provider_stash.clone(), storage_bond_size).into(), + ); + assert_eq!(DdcStaking::leaving_storages(provider_stash.clone()), None); System::assert_last_event(Event::Left(provider_stash).into()); - - MockNodeVisitor::reset_and_release_lock(lock); }); } #[test] fn staking_creator_works() { - // Verifies initial conditions of mock - ExtBuilder::default().build_and_execute(|| { - let stash: u64 = 1; - let controller: u64 = 2; - let cluster_id = ClusterId::from([1; 20]); + let (clusters, nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { + let stash = AccountId::from(USER_KEY_1); + let controller = AccountId::from(USER_KEY_2); + let cluster_id = ClusterId::from(CLUSTER_ID); let value = 5; - let storage_node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])); + let storage_node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)); assert_ok!( >>::bond_stake_and_participate( @@ -504,13 +648,32 @@ fn staking_creator_works() { #[test] fn staking_visitor_works() { - // Verifies initial conditions of mock - ExtBuilder::default().build_and_execute(|| { - let cluster_id = ClusterId::from([1; 20]); - let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])); + let (clusters, mut nodes, clusters_bonds, nodes_bondes) = build_default_setup(); + + let node_5 = build_node( + NODE_KEY_5, + USER_KEY_4, + StorageNodeParams::default(), + Some(ClusterAssignment { + cluster_id: CLUSTER_ID, + status: ClusterNodeStatus::ValidationSucceeded, + kind: ClusterNodeKind::Genesis, + }), + ); + + nodes.push(node_5); + + ExtBuilder.build_and_execute(clusters, nodes, clusters_bonds, nodes_bondes, || { + let cluster_id = ClusterId::from(CLUSTER_ID); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new(NODE_KEY_5)); // Add new Storage participant, account 3 controlled by 4 with node 5. - assert_ok!(DdcStaking::bond(RuntimeOrigin::signed(3), 4, node_pub_key.clone(), 100)); + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + AccountId::from(USER_KEY_4), + node_pub_key.clone(), + 100 + )); assert!(>::has_stake(&node_pub_key,)); @@ -520,7 +683,10 @@ fn staking_visitor_works() { assert!(!result); } - assert_ok!(DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20]))); + assert_ok!(DdcStaking::store( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(CLUSTER_ID) + )); if let Ok(result) = >::has_activated_stake(&node_pub_key, &cluster_id) @@ -529,3 +695,497 @@ fn staking_visitor_works() { } }); } + +#[test] +fn bond_cluster_works() { + let cluster = build_cluster( + CLUSTER_ID, + CLUSTER_CONTROLLER, + CLUSTER_STASH, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Unbonded, + ); + + ExtBuilder.build_and_execute(vec![cluster], vec![], vec![], vec![], || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(CLUSTER_ID) + ), + Error::::NotStash + ); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from([0; 20]) + ), + ClustersError::::ClusterDoesNotExist + ); + + let _ = Balances::make_free_balance_be(&AccountId::from(CLUSTER_STASH), 10); + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(CLUSTER_STASH)), + ClusterId::from(CLUSTER_ID) + ), + Error::::InsufficientBond + ); + + let _ = Balances::make_free_balance_be(&AccountId::from(CLUSTER_STASH), 1000); + assert_ok!(DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(CLUSTER_STASH)), + ClusterId::from(CLUSTER_ID) + )); + + assert_eq!( + DdcStaking::cluster_bonded(AccountId::from(CLUSTER_STASH)), + Some(AccountId::from(CLUSTER_CONTROLLER)) + ); + + assert_eq!( + DdcStaking::cluster_ledger(AccountId::from(CLUSTER_CONTROLLER)), + Some(StakingLedger { + stash: AccountId::from(CLUSTER_STASH), + total: 50, + active: 50, + chilling: Default::default(), + unlocking: Default::default(), + }) + ); + + System::assert_has_event(Event::Bonded(AccountId::from(CLUSTER_STASH), 50).into()); + + assert_eq!( + DdcClusters::clusters(ClusterId::from(CLUSTER_ID)), + Some(Cluster { + cluster_id: ClusterId::from(CLUSTER_ID), + manager_id: AccountId::from(CLUSTER_CONTROLLER), + reserve_id: AccountId::from(CLUSTER_STASH), + props: ClusterProps:: { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0 + }, + status: ClusterStatus::Bonded, + last_validated_era_id: DdcEra::default() + }) + ); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(CLUSTER_STASH)), + ClusterId::from(CLUSTER_ID) + ), + Error::::AlreadyBonded + ); + }); +} + +#[test] +fn bond_cluster_works_only_for_unbonded_cluster() { + const BONDED_CLUSTER_ID: [u8; 20] = [10; 20]; + let cluster_1 = build_cluster( + BONDED_CLUSTER_ID, + USER_KEY_1, + USER_KEY_1, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + let bond_1 = build_cluster_bond(USER_KEY_1, USER_KEY_1, BONDED_CLUSTER_ID); + + const ACTIVATED_CLUSTER_ID: [u8; 20] = [11; 20]; + let cluster_2 = build_cluster( + ACTIVATED_CLUSTER_ID, + USER_KEY_2, + USER_KEY_2, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + let bond_2 = build_cluster_bond(USER_KEY_2, USER_KEY_2, ACTIVATED_CLUSTER_ID); + + const UNBONDING_CLUSTER_ID: [u8; 20] = [12; 20]; + let cluster_3 = build_cluster( + UNBONDING_CLUSTER_ID, + USER_KEY_3, + USER_KEY_3, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Unbonding, + ); + let bond_3 = build_cluster_bond(USER_KEY_3, USER_KEY_3, UNBONDING_CLUSTER_ID); + + ExtBuilder.build_and_execute( + vec![cluster_1, cluster_2, cluster_3], + vec![], + vec![bond_1, bond_2, bond_3], + vec![], + || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + ), + Error::::AlreadyBonded + ); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_2)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + ), + Error::::AlreadyBonded + ); + + assert_noop!( + DdcStaking::bond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_3)), + ClusterId::from(UNBONDING_CLUSTER_ID) + ), + Error::::AlreadyBonded + ); + }, + ); +} +#[test] +fn unbond_bonded_cluster_works() { + const BONDED_CLUSTER_ID: [u8; 20] = [10; 20]; + let cluster_1 = build_cluster( + BONDED_CLUSTER_ID, + USER_KEY_1, + USER_KEY_1, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + let bond_1 = build_cluster_bond(USER_KEY_1, USER_KEY_1, BONDED_CLUSTER_ID); + + ExtBuilder.build_and_execute(vec![cluster_1], vec![], vec![bond_1], vec![], || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(BONDED_CLUSTER_ID) + ), + Error::::NotController + ); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from([111; 20]) + ), + ClustersError::::ClusterDoesNotExist + ); + + assert_ok!(DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + )); + + let mut unlocking = Vec::new(); + let chunk = UnlockChunk { value: 50u128, block: 3u64 }; + unlocking.push(chunk); + + assert_eq!( + DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), + Some(StakingLedger { + stash: AccountId::from(USER_KEY_1), + total: 50, + active: 0, + chilling: Default::default(), + unlocking: unlocking.try_into().unwrap() + }) + ); + + System::assert_has_event(Event::Unbonded(AccountId::from(USER_KEY_1), 50).into()); + + assert_eq!( + DdcClusters::clusters(ClusterId::from(BONDED_CLUSTER_ID)), + Some(Cluster { + cluster_id: ClusterId::from(BONDED_CLUSTER_ID), + manager_id: AccountId::from(USER_KEY_1), + reserve_id: AccountId::from(USER_KEY_1), + props: ClusterProps:: { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0 + }, + status: ClusterStatus::Unbonding, + last_validated_era_id: DdcEra::default() + }) + ); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + ), + ClustersError::::UnexpectedClusterStatus + ); + }); +} + +#[test] +fn unbond_activated_cluster_works() { + const ACTIVATED_CLUSTER_ID: [u8; 20] = [11; 20]; + let cluster_1 = build_cluster( + ACTIVATED_CLUSTER_ID, + USER_KEY_1, + USER_KEY_1, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + let bond_1 = build_cluster_bond(USER_KEY_1, USER_KEY_1, ACTIVATED_CLUSTER_ID); + + ExtBuilder.build_and_execute(vec![cluster_1], vec![], vec![bond_1], vec![], || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + ), + Error::::NotController + ); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from([111; 20]) + ), + ClustersError::::ClusterDoesNotExist + ); + + assert_ok!(DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + )); + + let mut unlocking = Vec::new(); + let chunk = UnlockChunk { value: 50u128, block: 3u64 }; + unlocking.push(chunk); + assert_eq!( + DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), + Some(StakingLedger { + stash: AccountId::from(USER_KEY_1), + total: 50, + active: 0, + chilling: Default::default(), + unlocking: unlocking.try_into().unwrap() + }) + ); + + System::assert_has_event(Event::Unbonded(AccountId::from(USER_KEY_1), 50).into()); + + assert_eq!( + DdcClusters::clusters(ClusterId::from(ACTIVATED_CLUSTER_ID)), + Some(Cluster { + cluster_id: ClusterId::from(ACTIVATED_CLUSTER_ID), + manager_id: AccountId::from(USER_KEY_1), + reserve_id: AccountId::from(USER_KEY_1), + props: ClusterProps:: { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0 + }, + status: ClusterStatus::Unbonding, + last_validated_era_id: DdcEra::default() + }) + ); + + assert_noop!( + DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + ), + ClustersError::::UnexpectedClusterStatus + ); + }); +} + +#[test] +fn withdraw_unbonded_cluster_works() { + const BONDED_CLUSTER_ID: [u8; 20] = [10; 20]; + let cluster_1 = build_cluster( + BONDED_CLUSTER_ID, + USER_KEY_1, + USER_KEY_1, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Bonded, + ); + let bond_1 = build_cluster_bond(USER_KEY_1, USER_KEY_1, BONDED_CLUSTER_ID); + + ExtBuilder.build_and_execute(vec![cluster_1], vec![], vec![bond_1], vec![], || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(BONDED_CLUSTER_ID) + ), + Error::::NotController + ); + + assert_noop!( + DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from([111; 20]) + ), + ClustersError::::ClusterDoesNotExist + ); + + assert_ok!(DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + )); + + assert_ok!(DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + )); + + let mut unlocking = Vec::new(); + let chunk = UnlockChunk { value: 50u128, block: 3u64 }; + unlocking.push(chunk); + assert_eq!( + DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), + Some(StakingLedger { + stash: AccountId::from(USER_KEY_1), + total: 50, + active: 0, + chilling: Default::default(), + unlocking: unlocking.try_into().unwrap() + }) + ); + + System::set_block_number(System::block_number() + 2); + + assert_ok!(DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(BONDED_CLUSTER_ID) + )); + + System::assert_last_event(Event::Withdrawn(AccountId::from(USER_KEY_1), 50).into()); + + assert_eq!(DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), None); + assert_eq!(DdcStaking::cluster_bonded(AccountId::from(USER_KEY_1)), None); + + assert_eq!( + DdcClusters::clusters(ClusterId::from(BONDED_CLUSTER_ID)), + Some(Cluster { + cluster_id: ClusterId::from(BONDED_CLUSTER_ID), + manager_id: AccountId::from(USER_KEY_1), + reserve_id: AccountId::from(USER_KEY_1), + props: ClusterProps:: { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0 + }, + status: ClusterStatus::Unbonded, + last_validated_era_id: DdcEra::default() + }) + ); + }); +} + +#[test] +fn withdraw_activated_cluster_works() { + const ACTIVATED_CLUSTER_ID: [u8; 20] = [11; 20]; + let cluster_1 = build_cluster( + ACTIVATED_CLUSTER_ID, + USER_KEY_1, + USER_KEY_1, + ClusterParams::default(), + ClusterProtocolParams::default(), + ClusterStatus::Activated, + ); + let bond_1 = build_cluster_bond(USER_KEY_1, USER_KEY_1, ACTIVATED_CLUSTER_ID); + + ExtBuilder.build_and_execute(vec![cluster_1], vec![], vec![bond_1], vec![], || { + System::set_block_number(1); + + assert_noop!( + DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + ), + Error::::NotController + ); + + assert_noop!( + DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_4)), + ClusterId::from([111; 20]) + ), + ClustersError::::ClusterDoesNotExist + ); + + assert_ok!(DdcStaking::unbond_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + )); + + assert_ok!(DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + )); + + let mut unlocking = Vec::new(); + let chunk = UnlockChunk { value: 50u128, block: 3u64 }; + unlocking.push(chunk); + assert_eq!( + DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), + Some(StakingLedger { + stash: AccountId::from(USER_KEY_1), + total: 50, + active: 0, + chilling: Default::default(), + unlocking: unlocking.try_into().unwrap() + }) + ); + + System::set_block_number(System::block_number() + 2); + + assert_ok!(DdcStaking::withdraw_unbonded_cluster( + RuntimeOrigin::signed(AccountId::from(USER_KEY_1)), + ClusterId::from(ACTIVATED_CLUSTER_ID) + )); + + System::assert_last_event(Event::Withdrawn(AccountId::from(USER_KEY_1), 50).into()); + + assert_eq!(DdcStaking::cluster_ledger(AccountId::from(USER_KEY_1)), None); + assert_eq!(DdcStaking::cluster_bonded(AccountId::from(USER_KEY_1)), None); + + assert_eq!( + DdcClusters::clusters(ClusterId::from(ACTIVATED_CLUSTER_ID)), + Some(Cluster { + cluster_id: ClusterId::from(ACTIVATED_CLUSTER_ID), + manager_id: AccountId::from(USER_KEY_1), + reserve_id: AccountId::from(USER_KEY_1), + props: ClusterProps:: { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0 + }, + status: ClusterStatus::Unbonded, + last_validated_era_id: DdcEra::default() + }) + ); + }); +} diff --git a/pallets/ddc-staking/src/weights.rs b/pallets/ddc-staking/src/weights.rs index 5bd1147c9..025e610f2 100644 --- a/pallets/ddc-staking/src/weights.rs +++ b/pallets/ddc-staking/src/weights.rs @@ -1,9 +1,9 @@ //! Autogenerated weights for pallet_ddc_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `Yahors-MacBook-Pro.local`, CPU: `` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/cere @@ -11,12 +11,13 @@ // pallet // --chain=dev // --execution=wasm -// --pallet=pallet-ddc-staking +// --wasm-execution=compiled +// --pallet=pallet_ddc_staking // --extrinsic=* // --steps=50 // --repeat=20 // --template=./.maintain/frame-weight-template.hbs -// --output=pallets/ddc-staking/src/weights.rs +// --output=pallets/ddc-staking/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -35,153 +36,350 @@ pub trait WeightInfo { fn set_controller() -> Weight; fn set_node() -> Weight; fn fast_chill() -> Weight; + fn bond_cluster() -> Weight; + fn unbond_cluster() -> Weight; + fn withdraw_unbonded_cluster() -> Weight; } /// Weights for pallet_ddc_staking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: DdcStaking Bonded (r:1 w:1) - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Nodes (r:1 w:1) - // Storage: DdcStaking Providers (r:1 w:1) - // Storage: DdcNodes StorageNodes (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) + // Storage: `DdcStaking::Bonded` (r:1 w:1) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:1) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:1) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn bond() -> Weight { - Weight::from_parts(39_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(6_u64)) + Weight::from_parts(91_663_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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`) fn unbond() -> Weight { - Weight::from_parts(37_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(6_u64)) + Weight::from_parts(76_793_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn withdraw_unbonded() -> Weight { - Weight::from_parts(33_000_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(5_u64)) + Weight::from_parts(70_663_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: DdcStaking Storages (r:1 w:1) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:1) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn store() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(48_231_000_u64, 0) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:1) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) fn chill() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(41_127_000_u64, 0) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - // Storage: DdcStaking Bonded (r:1 w:1) - // Storage: DdcStaking Ledger (r:2 w:2) + // Storage: `DdcStaking::Bonded` (r:1 w:1) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:2 w:2) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_controller() -> Weight { - Weight::from_parts(14_000_000_u64, 0) + Weight::from_parts(28_604_000_u64, 0) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Nodes (r:1 w:2) - // Storage: DdcStaking Providers (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcStaking::Nodes` (r:1 w:2) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:1) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_node() -> Weight { - Weight::from_parts(14_000_000_u64, 0) + Weight::from_parts(30_047_000_u64, 0) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn fast_chill() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(48_291_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterBonded` (r:1 w:1) + // Proof: `DdcStaking::ClusterBonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn bond_cluster() -> Weight { + Weight::from_parts(92_223_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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`) + fn unbond_cluster() -> Weight { + Weight::from_parts(84_528_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterBonded` (r:1 w:1) + // Proof: `DdcStaking::ClusterBonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn withdraw_unbonded_cluster() -> Weight { + Weight::from_parts(99_307_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: DdcStaking Bonded (r:1 w:1) - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Nodes (r:1 w:1) - // Storage: DdcStaking Providers (r:1 w:1) - // Storage: DdcNodes StorageNodes (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) + // Storage: `DdcStaking::Bonded` (r:1 w:1) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:1) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:1) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn bond() -> Weight { - Weight::from_parts(39_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + Weight::from_parts(91_663_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: DdcNodes StorageNodes (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:0) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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`) fn unbond() -> Weight { - Weight::from_parts(37_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + Weight::from_parts(76_793_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: Balances Locks (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn withdraw_unbonded() -> Weight { - Weight::from_parts(33_000_000_u64, 0) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + Weight::from_parts(70_663_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcClusters Clusters (r:1 w:0) - // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) - // Storage: DdcStaking Providers (r:1 w:0) - // Storage: DdcStaking Storages (r:1 w:1) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:1) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn store() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(48_231_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:1) - // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:1) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersGovParams` (r:1 w:0) + // Proof: `DdcClusters::ClustersGovParams` (`max_values`: None, `max_size`: None, mode: `Measured`) fn chill() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(41_127_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - // Storage: DdcStaking Bonded (r:1 w:1) - // Storage: DdcStaking Ledger (r:2 w:2) + // Storage: `DdcStaking::Bonded` (r:1 w:1) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:2 w:2) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_controller() -> Weight { - Weight::from_parts(14_000_000_u64, 0) + Weight::from_parts(28_604_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Nodes (r:1 w:2) - // Storage: DdcStaking Providers (r:1 w:1) - // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking LeavingStorages (r:1 w:0) + // Storage: `DdcStaking::Nodes` (r:1 w:2) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:1) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::LeavingStorages` (r:1 w:0) + // Proof: `DdcStaking::LeavingStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_node() -> Weight { - Weight::from_parts(14_000_000_u64, 0) + Weight::from_parts(30_047_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + // Storage: `DdcStaking::Ledger` (r:1 w:1) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Providers` (r:1 w:0) + // Proof: `DdcStaking::Providers` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn fast_chill() -> Weight { - Weight::from_parts(28_000_000_u64, 0) + Weight::from_parts(48_291_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterBonded` (r:1 w:1) + // Proof: `DdcStaking::ClusterBonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn bond_cluster() -> Weight { + Weight::from_parts(92_223_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:0) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`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`) + fn unbond_cluster() -> Weight { + Weight::from_parts(84_528_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } -} \ No newline at end of file + // Storage: `DdcClusters::Clusters` (r:1 w:1) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterLedger` (r:1 w:1) + // Proof: `DdcStaking::ClusterLedger` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::ClusterBonded` (r:1 w:1) + // Proof: `DdcStaking::ClusterBonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `Balances::Locks` (r:1 w:1) + // Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + // Storage: `Balances::Freezes` (r:1 w:0) + // Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn withdraw_unbonded_cluster() -> Weight { + Weight::from_parts(99_307_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } +} diff --git a/pallets/ddc-verification/Cargo.toml b/pallets/ddc-verification/Cargo.toml new file mode 100644 index 000000000..909237ec7 --- /dev/null +++ b/pallets/ddc-verification/Cargo.toml @@ -0,0 +1,76 @@ +[package] +name = "pallet-ddc-verification" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true + +[dependencies] +array-bytes = { workspace = true } +base64ct = { workspace = true } +# 3rd-party dependencies +codec = { workspace = true } +# Cere dependencies +ddc-primitives = { workspace = true } +# Substrate dependencies +frame-benchmarking = { workspace = true, optional = true } +frame-election-provider-support = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +hex = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } +polkadot-ckb-merkle-mountain-range = { workspace = true } +rand = { workspace = true, features = ["small_rng", "alloc"], default-features = false } +scale-info = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sp-application-crypto = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-staking = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true, default-features = true } +pallet-session = { workspace = true, default-features = true } +pallet-staking = { workspace = true, default-features = true } +pallet-staking-reward-curve = { workspace = true } +pallet-timestamp = { workspace = true } +sp-core = { workspace = true, default-features = true } +sp-keystore = { workspace = true } + +[features] +default = ["std"] +std = [ + "rand/std", + "hex/std", + "polkadot-ckb-merkle-mountain-range/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std", + "scale-info/std", + "sp-runtime/std", + "sp-io/std", + "sp-core/std", + "sp-application-crypto/std", + "sp-staking/std", + "frame-election-provider-support/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/pallets/ddc-verification/src/benchmarking.rs b/pallets/ddc-verification/src/benchmarking.rs new file mode 100644 index 000000000..6695674e5 --- /dev/null +++ b/pallets/ddc-verification/src/benchmarking.rs @@ -0,0 +1,37 @@ +#![cfg(feature = "runtime-benchmarks")] + +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; +use sp_std::vec; + +use super::*; +#[allow(unused)] +use crate::Pallet as DdcVerification; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn create_billing_reports() { + let cluster_id = ClusterId::from([1; 20]); + let era: DdcEra = 1; + let merkel_root_hash: MmrRootHash = array_bytes::hex_n_into_unchecked( + "95803defe6ea9f41e7ec6afa497064f21bfded027d8812efacbdf984e630cbdc", + ); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + create_billing_reports( + RawOrigin::Signed(caller), + cluster_id, + era, + merkel_root_hash, + ); + + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.merkle_root_hash, ActivityHash::from(merkel_root_hash)); + } + + impl_benchmark_test_suite!(DdcVerification, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs new file mode 100644 index 000000000..92ffacce3 --- /dev/null +++ b/pallets/ddc-verification/src/lib.rs @@ -0,0 +1,4049 @@ +//! # DDC Verification Pallet +//! +//! The DDC Verification pallet is used to validate zk-SNARK Proof and Signature +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +#![allow(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +use core::str; + +use base64ct::{Base64, Encoding}; +use ddc_primitives::{ + traits::{ + ClusterManager, ClusterValidator, CustomerVisitor, NodeVisitor, PayoutVisitor, + ValidatorVisitor, + }, + ActivityHash, BatchIndex, ClusterId, CustomerUsage, DdcEra, MMRProof, NodeParams, NodePubKey, + NodeUsage, PayoutState, StorageNodeParams, +}; +use frame_support::{ + pallet_prelude::*, + traits::{Get, OneSessionHandler}, +}; +use frame_system::{ + offchain::{AppCrypto, CreateSignedTransaction, SendSignedTransaction, Signer}, + pallet_prelude::*, +}; +pub use pallet::*; +use polkadot_ckb_merkle_mountain_range::{ + util::{MemMMR, MemStore}, + MerkleProof, MMR, +}; +use scale_info::prelude::{format, string::String}; +use serde::{Deserialize, Serialize}; +use sp_application_crypto::RuntimeAppPublic; +use sp_runtime::{ + offchain as rt_offchain, + offchain::{http, StorageKind}, + traits::Hash, + Percent, +}; +use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +pub mod weights; +use itertools::Itertools; +use rand::{prelude::*, rngs::SmallRng, SeedableRng}; +use sp_staking::StakingInterface; +use sp_std::fmt::Debug; + +use crate::weights::WeightInfo; + +#[cfg(test)] +pub(crate) mod mock; +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +pub mod pallet { + + use ddc_primitives::{AggregatorInfo, BucketId, MergeActivityHash, KEY_TYPE}; + use frame_support::PalletId; + use sp_core::crypto::AccountId32; + use sp_runtime::SaturatedConversion; + + use super::*; + + /// The current storage version. + const STORAGE_VERSION: frame_support::traits::StorageVersion = + frame_support::traits::StorageVersion::new(0); + + const SUCCESS_CODE: u16 = 200; + const _BUF_SIZE: usize = 128; + const RESPONSE_TIMEOUT: u64 = 20000; + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + /// The module configuration trait. + pub trait Config: CreateSignedTransaction> + frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The accounts's pallet id, used for deriving its sovereign account ID. + #[pallet::constant] + type PalletId: Get; + /// Weight info type. + type WeightInfo: WeightInfo; + /// DDC clusters nodes manager. + type ClusterValidator: ClusterValidator; + type ClusterManager: ClusterManager; + type PayoutVisitor: PayoutVisitor; + /// DDC nodes read-only registry. + type NodeVisitor: NodeVisitor; + /// The output of the `ActivityHasher` function. + type ActivityHash: Member + + Parameter + + MaybeSerializeDeserialize + + Ord + + Into + + From; + /// The hashing system (algorithm) + type ActivityHasher: Hash; + /// The identifier type for an authority. + type AuthorityId: Member + + Parameter + + RuntimeAppPublic + + Ord + + MaybeSerializeDeserialize + + Into + + From; + /// The identifier type for an offchain worker. + type OffchainIdentifierId: AppCrypto; + /// The majority of validators. + const MAJORITY: u8; + /// Block to start from. + const BLOCK_TO_START: u16; + const DAC_REDUNDANCY_FACTOR: u16; + + #[pallet::constant] + type AggregatorsQuorum: Get; + + const MAX_PAYOUT_BATCH_COUNT: u16; + const MAX_PAYOUT_BATCH_SIZE: u16; + const MAX_MERKLE_NODE_IDENTIFIER: u16; + /// The access to staking functionality. + type StakingVisitor: StakingInterface; + type AccountIdConverter: From + Into; + type CustomerVisitor: CustomerVisitor; + } + + /// The event type. + #[pallet::event] + /// The `generate_deposit` macro generates a function on `Pallet` called `deposit_event` which + /// will properly convert the error type of your pallet into `RuntimeEvent` (recall `type + /// RuntimeEvent: From>`, so it can be converted) and deposit it via + /// `frame_system::Pallet::deposit_event`. + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// A new billing report was created from `ClusterId` and `ERA`. + BillingReportCreated { + cluster_id: ClusterId, + era_id: DdcEra, + }, + /// A verification key was stored with `VerificationKey`. + VerificationKeyStored { + verification_key: Vec, + }, + /// A new payout batch was created from `ClusterId` and `ERA`. + PayoutBatchCreated { + cluster_id: ClusterId, + era_id: DdcEra, + }, + EraValidationReady { + cluster_id: ClusterId, + era_id: DdcEra, + }, + EraValidationNotReady { + cluster_id: ClusterId, + era_id: DdcEra, + }, + /// Node Usage Retrieval Error. + NodeUsageRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + node_pub_key: NodePubKey, + validator: T::AccountId, + }, + /// Bucket aggregates Retrieval Error. + BucketAggregatesRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + node_pub_key: NodePubKey, + validator: T::AccountId, + }, + EraRetrievalError { + cluster_id: ClusterId, + node_pub_key: NodePubKey, + validator: T::AccountId, + }, + PrepareEraTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + payers_merkle_root_hash: ActivityHash, + payees_merkle_root_hash: ActivityHash, + validator: T::AccountId, + }, + BeginBillingReportTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + BeginChargingCustomersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + SendChargingCustomersBatchTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + validator: T::AccountId, + }, + SendRewardingProvidersBatchTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + validator: T::AccountId, + }, + EndChargingCustomersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + BeginRewardingProvidersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + EndRewardingProvidersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + EndBillingReportTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + BillingReportDoesNotExist { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + EmptyCustomerActivity { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + BatchIndexConversionFailed { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + NoAvailableSigner { + validator: T::AccountId, + }, + NotEnoughDACNodes { + num_nodes: u16, + validator: T::AccountId, + }, + FailedToCreateMerkleRoot { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + FailedToCreateMerkleProof { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + }, + FailedToFetchCurrentValidator { + validator: T::AccountId, + }, + FailedToFetchNodeProvider { + validator: T::AccountId, + }, + ValidatorKeySet { + validator: T::AccountId, + }, + FailedToFetchClusterNodes { + validator: T::AccountId, + }, + FailedToFetchDacNodes { + validator: T::AccountId, + }, + FailedToFetchNodeTotalUsage { + cluster_id: ClusterId, + node_pub_key: NodePubKey, + validator: T::AccountId, + }, + EraValidationRootsPosted { + cluster_id: ClusterId, + era_id: DdcEra, + validator: T::AccountId, + payers_merkle_root_hash: ActivityHash, + payees_merkle_root_hash: ActivityHash, + payers_batch_merkle_root_hashes: Vec, + payees_batch_merkle_root_hashes: Vec, + }, + BucketAggregateRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + bucket_id: BucketId, + node_pub_key: NodePubKey, + validator: T::AccountId, + }, + ChallengeResponseRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + aggregator: NodePubKey, + validator: T::AccountId, + }, + TraverseResponseRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + aggregator: NodePubKey, + validator: T::AccountId, + }, + EmptyConsistentGroup, + } + + /// Consensus Errors + #[derive(Debug, Encode, Decode, Clone, TypeInfo, PartialEq)] + pub enum OCWError { + /// Node Usage Retrieval Error. + NodeUsageRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + node_pub_key: NodePubKey, + }, + /// Bucket aggregates Retrieval Error. + BucketAggregatesRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + node_pub_key: NodePubKey, + }, + EraRetrievalError { + cluster_id: ClusterId, + node_pub_key: NodePubKey, + }, + /// Bucket aggregate Retrieval Error. + BucketAggregateRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + bucket_id: BucketId, + node_pub_key: NodePubKey, + }, + /// Challenge Response Retrieval Error. + ChallengeResponseRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + aggregator: NodePubKey, + }, + /// Traverse Response Retrieval Error. + TraverseResponseRetrievalError { + cluster_id: ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + aggregator: NodePubKey, + }, + PrepareEraTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + payers_merkle_root_hash: ActivityHash, + payees_merkle_root_hash: ActivityHash, + }, + BeginBillingReportTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + BeginChargingCustomersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + SendChargingCustomersBatchTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + }, + SendRewardingProvidersBatchTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + }, + EndChargingCustomersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + BeginRewardingProvidersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + EndRewardingProvidersTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + EndBillingReportTransactionError { + cluster_id: ClusterId, + era_id: DdcEra, + }, + BillingReportDoesNotExist { + cluster_id: ClusterId, + era_id: DdcEra, + }, + EmptyCustomerActivity { + cluster_id: ClusterId, + era_id: DdcEra, + }, + BatchIndexConversionFailed { + cluster_id: ClusterId, + era_id: DdcEra, + }, + NoAvailableSigner, + NotEnoughDACNodes { + num_nodes: u16, + }, + FailedToCreateMerkleRoot { + cluster_id: ClusterId, + era_id: DdcEra, + }, + FailedToCreateMerkleProof { + cluster_id: ClusterId, + era_id: DdcEra, + }, + FailedToFetchCurrentValidator, + FailedToFetchNodeProvider, + FailedToFetchClusterNodes, + FailedToFetchDacNodes, + FailedToFetchNodeTotalUsage { + cluster_id: ClusterId, + node_pub_key: NodePubKey, + }, + EmptyConsistentGroup, + } + + #[pallet::error] + #[derive(PartialEq)] + pub enum Error { + /// Bad verification key. + BadVerificationKey, + /// Bad requests. + BadRequest, + /// Not a validator. + Unauthorised, + /// Already signed era. + AlreadySignedEra, + NotExpectedState, + /// Already signed payout batch. + AlreadySignedPayoutBatch, + /// Node Retrieval Error. + NodeRetrievalError, + /// Cluster To Validate Retrieval Error. + ClusterToValidateRetrievalError, + /// Era To Validate Retrieval Error. + EraToValidateRetrievalError, + /// Era Per Node Retrieval Error. + EraPerNodeRetrievalError, + /// Fail to fetch Ids. + FailToFetchIds, + /// No validator exists. + NoValidatorExist, + /// Not a controller. + NotController, + /// Not a validator stash. + NotValidatorStash, + /// DDC Validator Key Not Registered + DDCValidatorKeyNotRegistered, + TransactionSubmissionError, + NoAvailableSigner, + /// Fail to generate proof + FailToGenerateProof, + /// Fail to verify merkle proof + FailToVerifyMerkleProof, + /// No Era Validation exist + NoEraValidation, + } + + /// Era validations + #[pallet::storage] + #[pallet::getter(fn era_validations)] + pub type EraValidations = StorageDoubleMap< + _, + Blake2_128Concat, + ClusterId, + Blake2_128Concat, + DdcEra, + EraValidation, + >; + + /// Cluster id storage + #[pallet::storage] + #[pallet::getter(fn cluster_to_validate)] + pub type ClusterToValidate = StorageValue<_, ClusterId>; + + /// List of validators. + #[pallet::storage] + #[pallet::getter(fn validator_set)] + pub type ValidatorSet = StorageValue<_, Vec, ValueQuery>; + + /// Validator stash key mapping + #[pallet::storage] + #[pallet::getter(fn get_stash_for_ddc_validator)] + pub type ValidatorToStashKey = StorageMap<_, Identity, T::AccountId, T::AccountId>; + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] + pub enum EraValidationStatus { + ValidatingData, + ReadyForPayout, + PayoutInProgress, + PayoutFailed, + PayoutSuccess, + PayoutSkipped, + } + + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] + #[scale_info(skip_type_params(T))] + pub struct EraValidation { + pub validators: BTreeMap<(ActivityHash, ActivityHash), Vec>, /* todo! change to signatures (T::AccountId, Signature) */ + pub start_era: i64, + pub end_era: i64, + pub payers_merkle_root_hash: ActivityHash, + pub payees_merkle_root_hash: ActivityHash, + pub status: EraValidationStatus, + } + + /// Era activity of a node. + #[derive( + Debug, + Serialize, + Deserialize, + Clone, + Hash, + Ord, + PartialOrd, + PartialEq, + Eq, + TypeInfo, + Encode, + Decode, + )] + pub struct EraActivity { + /// Era id. + pub id: DdcEra, + pub start: i64, + pub end: i64, + } + + pub struct CustomerBatch { + pub(crate) batch_index: BatchIndex, + pub(crate) payers: Vec<(T::AccountId, BucketId, CustomerUsage)>, + pub(crate) batch_proof: MMRProof, + } + + pub struct ProviderBatch { + pub(crate) batch_index: BatchIndex, + pub(crate) payees: Vec<(T::AccountId, NodeUsage)>, + pub(crate) batch_proof: MMRProof, + } + + /// Node aggregate response from aggregator. + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct NodeAggregateResponse { + /// Node id. + pub(crate) node_id: String, + /// Total amount of stored bytes. + pub(crate) stored_bytes: i64, + /// Total amount of transferred bytes. + pub(crate) transferred_bytes: u64, + /// Total number of puts. + pub(crate) number_of_puts: u64, + /// Total number of gets. + pub(crate) number_of_gets: u64, + } + + /// Bucket aggregate response from aggregator. + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct BucketAggregateResponse { + /// Bucket id + pub(crate) bucket_id: BucketId, + /// Total amount of stored bytes. + pub(crate) stored_bytes: i64, + /// Total amount of transferred bytes. + pub(crate) transferred_bytes: u64, + /// Total number of puts. + pub(crate) number_of_puts: u64, + /// Total number of gets. + pub(crate) number_of_gets: u64, + /// Bucket sub aggregates. + pub(crate) sub_aggregates: Vec, + } + + /// Sub aggregates of a bucket. + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + #[allow(non_snake_case)] + pub(crate) struct BucketSubAggregateResponse { + /// Node id. + pub(crate) NodeID: String, + /// Total amount of stored bytes. + pub(crate) stored_bytes: i64, + /// Total amount of transferred bytes. + pub(crate) transferred_bytes: u64, + /// Total number of puts. + pub(crate) number_of_puts: u64, + /// Total number of gets. + pub(crate) number_of_gets: u64, + } + + /// Bucket activity per a DDC node. + #[derive( + Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct BucketSubAggregate { + /// Bucket id + pub(crate) bucket_id: BucketId, + /// Node id. + pub(crate) node_id: String, + /// Total amount of stored bytes. + pub(crate) stored_bytes: i64, + /// Total amount of transferred bytes. + pub(crate) transferred_bytes: u64, + /// Total number of puts. + pub(crate) number_of_puts: u64, + /// Total number of gets. + pub(crate) number_of_gets: u64, + /// Aggregator data. + pub(crate) aggregator: AggregatorInfo, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct NodeAggregate { + /// Node id. + pub(crate) node_id: String, + /// Total amount of stored bytes. + pub(crate) stored_bytes: i64, + /// Total amount of transferred bytes. + pub(crate) transferred_bytes: u64, + /// Total number of puts. + pub(crate) number_of_puts: u64, + /// Total number of gets. + pub(crate) number_of_gets: u64, + /// Node data. + pub(crate) aggregator: AggregatorInfo, + } + + /// Challenge Response + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct ChallengeAggregateResponse { + /// proofs + pub proofs: Vec, //todo! add optional fields + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Proof { + pub merkle_tree_node_id: u64, + pub usage: Usage, + pub path: Vec, //todo! add base64 deserialization + pub leafs: Vec, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Usage { + /// Total amount of stored bytes. + pub stored_bytes: i64, + /// Total amount of transferred bytes. + pub transferred_bytes: u64, + /// Total number of puts. + pub number_of_puts: u64, + /// Total number of gets. + pub number_of_gets: u64, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Leaf { + pub record: Record, + pub transferred_bytes: u64, + pub stored_bytes: i64, + // todo! add links if there is no record + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + #[allow(non_snake_case)] + pub(crate) struct Record { + pub id: String, + pub upstream: Upstream, + pub downstream: Vec, + pub timestamp: String, + pub signature: Signature, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Upstream { + pub request: Request, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Downstream { + pub request: Request, + } + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + #[allow(non_snake_case)] + pub(crate) struct Request { + pub requestId: String, + pub requestType: String, + pub contentType: String, + pub bucketId: String, + pub pieceCid: String, + pub offset: String, + pub size: String, + pub timestamp: String, + pub signature: Signature, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct Signature { + pub algorithm: String, + pub signer: String, + pub value: String, + } + + #[derive( + Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, + )] + pub(crate) struct MerkleTreeNodeResponse { + merkle_tree_node_id: u64, + hash: String, + stored_bytes: i64, + transferred_bytes: u64, + number_of_puts: u64, + number_of_gets: u64, + } + + #[derive(Debug, Clone, PartialEq)] + pub(crate) struct ConsistentGroup(pub ActivityHash, pub Vec); + impl ConsistentGroup { + pub fn hash(&self) -> ActivityHash { + self.0 + } + + pub fn get(&self, idx: usize) -> Option<&A> { + self.1.get(idx) + } + + pub fn len(&self) -> usize { + self.1.len() + } + + pub fn _items(&self) -> &Vec { + &self.1 + } + } + + #[derive(Debug, Clone, PartialEq)] + pub(crate) struct ConsistentGroups { + pub in_consensus: Vec>, + pub in_quorum: Vec>, + pub in_others: Vec>, + } + + #[derive(Debug, Clone, Encode, Decode, TypeInfo, PartialEq)] + pub enum AggregateKey { + NodeAggregateKey(String), + BucketSubAggregateKey(BucketId, String), + } + + pub(crate) trait Aggregate: + Clone + Ord + PartialEq + Eq + Serialize + for<'de> Deserialize<'de> + Debug + { + fn hash(&self) -> ActivityHash; + fn get_key(&self) -> AggregateKey; + fn get_number_of_leaves(&self) -> u64; + fn get_aggregator(&self) -> AggregatorInfo; + } + + impl Aggregate for BucketSubAggregate { + fn hash(&self) -> ActivityHash { + let mut data = self.bucket_id.encode(); + data.extend_from_slice(&self.node_id.encode()); + data.extend_from_slice(&self.stored_bytes.encode()); + data.extend_from_slice(&self.transferred_bytes.encode()); + data.extend_from_slice(&self.number_of_puts.encode()); + data.extend_from_slice(&self.number_of_gets.encode()); + T::ActivityHasher::hash(&data).into() + } + + fn get_key(&self) -> AggregateKey { + AggregateKey::BucketSubAggregateKey(self.bucket_id, self.node_id.clone()) + } + + fn get_number_of_leaves(&self) -> u64 { + self.number_of_gets.saturating_add(self.number_of_puts) + } + + fn get_aggregator(&self) -> AggregatorInfo { + self.aggregator.clone() + } + } + + impl Aggregate for NodeAggregate { + fn hash(&self) -> ActivityHash { + let mut data = self.node_id.encode(); + data.extend_from_slice(&self.stored_bytes.encode()); + data.extend_from_slice(&self.transferred_bytes.encode()); + data.extend_from_slice(&self.number_of_puts.encode()); + data.extend_from_slice(&self.number_of_gets.encode()); + T::ActivityHasher::hash(&data).into() + } + + fn get_key(&self) -> AggregateKey { + AggregateKey::NodeAggregateKey(self.node_id.clone()) + } + + fn get_aggregator(&self) -> AggregatorInfo { + self.aggregator.clone() + } + + fn get_number_of_leaves(&self) -> u64 { + self.number_of_gets.saturating_add(self.number_of_puts) + } + } + pub trait NodeAggregateLeaf: + Clone + Ord + PartialEq + Eq + Serialize + for<'de> Deserialize<'de> + { + fn leaf_hash(&self) -> ActivityHash; + } + + pub trait BucketSubAggregateLeaf: + Clone + Ord + PartialEq + Eq + Serialize + for<'de> Deserialize<'de> + { + fn leaf_hash(&self) -> ActivityHash; + } + + impl NodeAggregateLeaf for Leaf { + fn leaf_hash(&self) -> ActivityHash { + let mut data = self.record.id.encode(); + data.extend_from_slice(&self.record.upstream.request.requestType.encode()); + data.extend_from_slice(&self.stored_bytes.encode()); + data.extend_from_slice(&self.transferred_bytes.encode()); + T::ActivityHasher::hash(&data).into() + } + } + + impl BucketSubAggregateLeaf for Leaf { + fn leaf_hash(&self) -> ActivityHash { + let mut data = self.record.upstream.request.bucketId.encode(); + data.extend_from_slice(&self.record.encode()); + data.extend_from_slice(&self.record.upstream.request.requestType.encode()); + data.extend_from_slice(&self.stored_bytes.encode()); + data.extend_from_slice(&self.transferred_bytes.encode()); + T::ActivityHasher::hash(&data).into() + } + } + + /// Unwrap or send an error log + macro_rules! unwrap_or_log_error { + ($result:expr, $error_msg:expr) => { + match $result { + Ok(val) => val, + Err(err) => { + log::error!("{}: {:?}", $error_msg, err); + return; + }, + } + }; + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn offchain_worker(block_number: BlockNumberFor) { + if !sp_io::offchain::is_validator() { + return; + } + + let signer = Signer::::any_account(); + if !signer.can_sign() { + log::error!("🚨No OCW is available."); + return; + } + + // todo! Need to uncomment this code + // if Self::fetch_current_validator().is_err() { + // let _ = signer.send_signed_transaction(|account| { + // Self::store_current_validator(account.id.encode()); + // + // Call::set_current_validator {} + // }); + // } + // todo! need to remove below code + if (block_number.saturated_into::() % 70) == 0 { + let _ = signer.send_signed_transaction(|account| { + Self::store_current_validator(account.id.encode()); + log::info!("🏭📋‍ Setting current validator... {:?}", account.id); + Call::set_current_validator {} + }); + } + + if (block_number.saturated_into::() % T::BLOCK_TO_START as u32) != 0 { + return; + } + + log::info!("👋 Hello from pallet-ddc-verification."); + + // todo: fetch clusters from ddc-clusters and loop the whole process for each cluster + let cluster_id = unwrap_or_log_error!( + Self::get_cluster_to_validate(), + "🏭❌ Error retrieving cluster to validate" + ); + + let batch_size = T::MAX_PAYOUT_BATCH_SIZE; + let mut errors: Vec = Vec::new(); + + let dac_era_result = Self::process_dac_era(&cluster_id, None, batch_size.into()); + + match dac_era_result { + Ok(Some(( + era_activity, + payers_merkle_root_hash, + payees_merkle_root_hash, + payers_batch_merkle_root_hashes, + payees_batch_merkle_root_hashes, + ))) => { + log::info!( + "🏭🚀 Processing era_id: {:?} for cluster_id: {:?}", + era_activity.clone(), + cluster_id + ); + + let results = signer.send_signed_transaction(|_account| { + Call::set_prepare_era_for_payout { + cluster_id, + era_activity: era_activity.clone(), + payers_merkle_root_hash, + payees_merkle_root_hash, + payers_batch_merkle_root_hashes: payers_batch_merkle_root_hashes + .clone(), + payees_batch_merkle_root_hashes: payees_batch_merkle_root_hashes + .clone(), + } + }); + + for (_, res) in &results { + match res { + Ok(()) => { + log::info!( + "🏭⛳️ Merkle roots posted on-chain for cluster_id: {:?}, era: {:?}", + cluster_id, + era_activity.clone() + ); + }, + Err(e) => { + log::error!( + "🏭❌ Error to post merkle roots on-chain for cluster_id: {:?}, era: {:?}: {:?}", + cluster_id, + era_activity.clone(), + e + ); + // Extrinsic call failed + errors.push(OCWError::PrepareEraTransactionError { + cluster_id, + era_id: era_activity.id, + payers_merkle_root_hash, + payees_merkle_root_hash, + }); + }, + } + } + }, + Ok(None) => { + log::info!("🏭ℹ️ No eras for DAC process for cluster_id: {:?}", cluster_id); + }, + Err(process_errors) => { + errors.extend(process_errors); + }, + }; + + // todo! factor out as macro as this is repetitive + match Self::prepare_begin_billing_report(&cluster_id) { + Ok(Some((era_id, start_era, end_era))) => { + log::info!( + "🏭🚀 process_start_payout processed successfully for cluster_id: {:?}, era_id: {:?}, start_era: {:?}, end_era: {:?} ", + cluster_id, + era_id, + start_era, + end_era + ); + let results = signer.send_signed_transaction(|_account| { + Call::begin_billing_report { cluster_id, era_id, start_era, end_era } + }); + + for (_, res) in &results { + match res { + Ok(()) => { + log::info!( + "🏭🏄‍ Sent begin_billing_report successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌ Error to post begin_billing_report for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::BeginBillingReportTransactionError { + cluster_id, + era_id, + }); + }, + } + } + }, + Ok(None) => { + log::info!("🏭❌ No era for payout for cluster_id: {:?}", cluster_id); + }, + Err(e) => { + errors.push(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_begin_charging_customers(&cluster_id, batch_size.into()) { + Ok(Some((era_id, max_batch_index))) => { + log::info!( + "🏭🎁 prepare_begin_charging_customers processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = signer.send_signed_transaction(|_acc| { + Call::begin_charging_customers { cluster_id, era_id, max_batch_index } + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭🚀 Sent begin_charging_customers successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌ Error to post begin_charging_customers for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::BeginChargingCustomersTransactionError { + cluster_id, + era_id, + }); + }, + } + } else { + log::error!("🏭❌ No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::error!( + "🏭🦀 No era for begin_charging_customers for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => errors.extend(e), + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_send_charging_customers_batch(&cluster_id, batch_size.into()) { + Ok(Some((era_id, batch_payout))) => { + let payers_log: Vec<(String, BucketId, CustomerUsage)> = batch_payout + .payers + .clone() + .into_iter() + .map(|(acc_id, bucket_id, customer_usage)| { + let account_id: T::AccountIdConverter = acc_id.into(); + let account_id_32: AccountId32 = account_id.into(); + let account_ref: &[u8; 32] = account_id_32.as_ref(); + (hex::encode(account_ref), bucket_id, customer_usage) + }) + .collect(); + log::info!( + "🏭🎁 prepare_send_charging_customers_batch processed successfully for cluster_id: {:?}, era_id: {:?} , batch_payout: {:?}", + cluster_id, + era_id, + payers_log + ); + + if let Some((_, res)) = + signer.send_signed_transaction(|_acc| Call::send_charging_customers_batch { + cluster_id, + era_id, + batch_index: batch_payout.batch_index, + payers: batch_payout.payers.clone(), + batch_proof: batch_payout.batch_proof.clone(), + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭🚀 Sent send_charging_customers_batch successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌ Error to post send_charging_customers_batch for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::SendChargingCustomersBatchTransactionError { + cluster_id, + era_id, + batch_index: batch_payout.batch_index, + }); + }, + } + } else { + log::error!("🏭❌ No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🏭🦀 No era for send_charging_customers_batch for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.extend(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_end_charging_customers(&cluster_id) { + Ok(Some(era_id)) => { + log::info!( + "🏭📝prepare_end_charging_customers processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = signer.send_signed_transaction(|_acc| { + Call::end_charging_customers { cluster_id, era_id } + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭📝Sent end_charging_customers successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌Error to post end_charging_customers for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::EndChargingCustomersTransactionError { + cluster_id, + era_id, + }); + }, + } + } else { + log::error!("🏭❌No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🏭📝No era for end_charging_customers for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.push(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_begin_rewarding_providers(&cluster_id, batch_size.into()) { + Ok(Some((era_id, max_batch_index, total_node_usage))) => { + log::info!( + "🏭📝prepare_begin_rewarding_providers processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = + signer.send_signed_transaction(|_acc| Call::begin_rewarding_providers { + cluster_id, + era_id, + max_batch_index, + total_node_usage: total_node_usage.clone(), + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭📝Sent begin_rewarding_providers successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌Error to post begin_rewarding_providers for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::BeginRewardingProvidersTransactionError { + cluster_id, + era_id, + }); + }, + } + } else { + log::error!("🏭❌No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🏭📝No era for begin_rewarding_providers for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.extend(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_send_rewarding_providers_batch(&cluster_id, batch_size.into()) { + Ok(Some((era_id, batch_payout))) => { + log::info!( + "🎁 prepare_send_rewarding_providers_batch processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = signer.send_signed_transaction(|_acc| { + Call::send_rewarding_providers_batch { + cluster_id, + era_id, + batch_index: batch_payout.batch_index, + payees: batch_payout.payees.clone(), + batch_proof: batch_payout.batch_proof.clone(), + } + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🚀 Sent send_rewarding_providers_batch successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🦀 Error to post send_rewarding_providers_batch for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push( + OCWError::SendRewardingProvidersBatchTransactionError { + cluster_id, + era_id, + batch_index: batch_payout.batch_index, + }, + ); + }, + } + } else { + log::error!("🦀 No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🦀 No era for send_rewarding_providers_batch for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.extend(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_end_rewarding_providers(&cluster_id) { + Ok(Some(era_id)) => { + log::info!( + "🏭📝prepare_end_rewarding_providers processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = signer.send_signed_transaction(|_acc| { + Call::end_rewarding_providers { cluster_id, era_id } + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭📝Sent end_rewarding_providers successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌Error to post end_rewarding_providers for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::EndRewardingProvidersTransactionError { + cluster_id, + era_id, + }); + }, + } + } else { + log::error!("🏭❌No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🏭📝No era for end_rewarding_providers for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.push(e); + }, + } + + // todo! factor out as macro as this is repetitive + match Self::prepare_end_billing_report(&cluster_id) { + Ok(Some(era_id)) => { + log::info!( + "🏭📝prepare_end_billing_report processed successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + + if let Some((_, res)) = signer.send_signed_transaction(|_acc| { + Call::end_billing_report { cluster_id, era_id } + }) { + match res { + Ok(_) => { + // Extrinsic call succeeded + log::info!( + "🏭📝Sent end_billing_report successfully for cluster_id: {:?}, era_id: {:?}", + cluster_id, + era_id + ); + }, + Err(e) => { + log::error!( + "🏭❌Error to post end_billing_report for cluster_id: {:?}, era_id: {:?}: {:?}", + cluster_id, + era_id, + e + ); + // Extrinsic call failed + errors.push(OCWError::EndBillingReportTransactionError { + cluster_id, + era_id, + }); + }, + } + } else { + log::error!("🏭❌No account available to sign the transaction"); + errors.push(OCWError::NoAvailableSigner); + } + }, + Ok(None) => { + log::info!( + "🏭📝No era for end_billing_report for cluster_id: {:?}", + cluster_id + ); + }, + Err(e) => { + errors.push(e); + }, + } + + if !errors.is_empty() { + let results = signer.send_signed_transaction(|_account| { + Call::emit_consensus_errors { errors: errors.clone() } + }); + + for (_, res) in &results { + match res { + Ok(()) => log::info!("✅ Successfully submitted emit_consensus_errors tx"), + Err(_) => log::error!("🏭❌ Failed to submit emit_consensus_errors tx"), + } + } + } + } + } + + impl Pallet { + #[allow(clippy::type_complexity)] + pub(crate) fn process_dac_era( + cluster_id: &ClusterId, + era_id_to_process: Option, + batch_size: usize, + ) -> Result< + Option<(EraActivity, ActivityHash, ActivityHash, Vec, Vec)>, + Vec, + > { + log::info!("🚀 Processing dac data for cluster_id: {:?}", cluster_id); + + let dac_nodes = Self::get_dac_nodes(cluster_id).map_err(|_| { + log::error!("🏭❌ Error retrieving dac nodes to validate cluster {:?}", cluster_id); + vec![OCWError::FailedToFetchDacNodes] + })?; + + let era_activity = if let Some(era_activity) = era_id_to_process { + EraActivity { + id: era_activity.id, + start: era_activity.start, + end: era_activity.end, + } + } else { + match Self::get_era_for_validation(cluster_id, &dac_nodes) { + Ok(Some(era_activity)) => era_activity, + Ok(None) => return Ok(None), + Err(err) => return Err(vec![err]), + } + }; + + // todo: move to cluster protocol parameters + let dac_redundancy_factor = T::DAC_REDUNDANCY_FACTOR; + let aggregators_quorum = T::AggregatorsQuorum::get(); + + let nodes_aggregates_by_aggregator = + Self::fetch_nodes_aggregates_for_era(cluster_id, era_activity.id, &dac_nodes) + .map_err(|err| vec![err])?; + + let buckets_aggregates_by_aggregator = + Self::fetch_buckets_aggregates_for_era(cluster_id, era_activity.id, &dac_nodes) + .map_err(|err| vec![err])?; + + let buckets_sub_aggregates_groups = Self::group_buckets_sub_aggregates_by_consistency( + cluster_id, + era_activity.id, + buckets_aggregates_by_aggregator, + dac_redundancy_factor, + aggregators_quorum, + ); + + let total_buckets_usage = + Self::get_total_usage(cluster_id, era_activity.id, buckets_sub_aggregates_groups)?; + + let customer_activity_hashes: Vec = + total_buckets_usage.clone().into_iter().map(|c| c.hash::()).collect(); + + let customer_activity_hashes_string: Vec = + customer_activity_hashes.clone().into_iter().map(hex::encode).collect(); + + log::info!( + "🧗‍ Customer Activity hashes for ClusterId: {:?} EraId: {:?} is: {:?}", + cluster_id, + era_activity.id, + customer_activity_hashes_string + ); + let customers_activity_batch_roots = Self::convert_to_batch_merkle_roots( + cluster_id, + era_activity.id, + Self::split_to_batches(&total_buckets_usage, batch_size), + ) + .map_err(|err| vec![err])?; + + let customer_batch_roots_string: Vec = + customers_activity_batch_roots.clone().into_iter().map(hex::encode).collect(); + + for (pos, batch_root) in customer_batch_roots_string.iter().enumerate() { + log::info!( + "🧗‍ Customer Activity batches for ClusterId: {:?} EraId: {:?} is: batch {:?} with root {:?} for activities {:?}", + cluster_id, + era_activity.id, + pos + 1, + batch_root, + customer_activity_hashes_string + ); + } + + let customers_activity_root = Self::create_merkle_root( + cluster_id, + era_activity.id, + &customers_activity_batch_roots, + ) + .map_err(|err| vec![err])?; + + log::info!( + "🧗‍ Customer Activity batches tree for ClusterId: {:?} EraId: {:?} is: batch with root {:?} for activities {:?}", + cluster_id, + era_activity.id, + hex::encode(customers_activity_root), + customer_batch_roots_string, + ); + + let nodes_aggregates_groups = Self::group_nodes_aggregates_by_consistency( + cluster_id, + era_activity.id, + nodes_aggregates_by_aggregator, + dac_redundancy_factor, + aggregators_quorum, + ); + + let total_nodes_usage = + Self::get_total_usage(cluster_id, era_activity.id, nodes_aggregates_groups)?; + + let node_activity_hashes: Vec = + total_nodes_usage.clone().into_iter().map(|c| c.hash::()).collect(); + + let node_activity_hashes_string: Vec = + node_activity_hashes.clone().into_iter().map(hex::encode).collect(); + + log::info!( + "🧗‍ Node Activity hashes for ClusterId: {:?} EraId: {:?} is: {:?}", + cluster_id, + era_activity.id, + node_activity_hashes_string + ); + + let nodes_activity_batch_roots = Self::convert_to_batch_merkle_roots( + cluster_id, + era_activity.id, + Self::split_to_batches(&total_nodes_usage, batch_size), + ) + .map_err(|err| vec![err])?; + + let nodes_activity_batch_roots_string: Vec = + nodes_activity_batch_roots.clone().into_iter().map(hex::encode).collect(); + + for (pos, batch_root) in nodes_activity_batch_roots_string.iter().enumerate() { + log::info!( + "🧗‍ Node Activity batches for ClusterId: {:?} EraId: {:?} is: batch {:?} with root {:?} for activities {:?}", + cluster_id, + era_activity.id, + pos + 1, + batch_root, + node_activity_hashes_string + ); + } + + let nodes_activity_root = + Self::create_merkle_root(cluster_id, era_activity.id, &nodes_activity_batch_roots) + .map_err(|err| vec![err])?; + + log::info!( + "🧗‍ Node Activity batches tree for ClusterId: {:?} EraId: {:?} is: batch with root {:?} for activities {:?}", + cluster_id, + era_activity.id, + hex::encode(nodes_activity_root), + nodes_activity_batch_roots_string, + ); + + Self::store_validation_activities( + cluster_id, + era_activity.id, + &total_buckets_usage, + customers_activity_root, + &customers_activity_batch_roots, + &total_nodes_usage, + nodes_activity_root, + &nodes_activity_batch_roots, + ); + log::info!("🙇‍ Dac data processing completed for cluster_id: {:?}", cluster_id); + Ok(Some(( + era_activity, + customers_activity_root, + nodes_activity_root, + customers_activity_batch_roots, + nodes_activity_batch_roots, + ))) + } + + pub(crate) fn get_total_usage( + cluster_id: &ClusterId, + era_id: DdcEra, + consistent_groups: ConsistentGroups, + ) -> Result, Vec> { + let mut total_usage: Vec = vec![]; + + // todo: run a light challenge for unanimous consensus + let in_consensus_usage = consistent_groups + .in_consensus + .clone() + .into_iter() + .map(|g| g.get(0).ok_or(vec![OCWError::EmptyConsistentGroup]).cloned()) + .collect::, _>>()?; + total_usage.extend(in_consensus_usage); + + // todo: run a light challenge for quorum, i.e. for majority + let in_quorum_usage = consistent_groups + .in_quorum + .clone() + .into_iter() + .map(|g| g.get(0).ok_or(vec![OCWError::EmptyConsistentGroup]).cloned()) + .collect::, _>>()?; + total_usage.extend(in_quorum_usage); + + let verified_usage = + Self::challenge_others(cluster_id, era_id, consistent_groups.in_others)?; + + if !verified_usage.is_empty() { + total_usage.extend(verified_usage); + } + + Ok(total_usage) + } + + pub(crate) fn challenge_others( + _cluster_id: &ClusterId, + _era_id: DdcEra, + others: Vec>, + ) -> Result, Vec> { + let redundancy_factor = T::DAC_REDUNDANCY_FACTOR; + let mut verified_usage: Vec = vec![]; + + for group in others { + if group.len() > redundancy_factor.into() { + log::info!( + "⚠️ Number of consistent aggregates exceeds the redundancy factor {:?}", + group.hash() + ); + + let excessive_aggregate = + group.get(0).ok_or(vec![OCWError::EmptyConsistentGroup]).cloned()?; + + log::info!( + "🔎‍ Challenging excessive aggregate {:?}", + excessive_aggregate.hash::() + ); + + // todo: run a challenge dedicated to the excessive number of aggregates. + // we assume it won't happen at the moment, so we just take the aggregate to + // payouts stage + verified_usage.push(excessive_aggregate); + } else { + let defective_aggregate = + group.get(0).ok_or(vec![OCWError::EmptyConsistentGroup]).cloned()?; + + log::info!( + "🔎‍ Challenging defective aggregate {:?}", + defective_aggregate.hash::() + ); + + let is_passed = true; + // todo: run an intensive challenge for deviating aggregate + // let is_passed = Self::_challenge_aggregate(_cluster_id, _era_id, + // &defective_aggregate)?; + if is_passed { + // we assume all aggregates are valid at the moment, so we just take the + // aggregate to payouts stage + verified_usage.push(defective_aggregate); + } + } + } + + Ok(verified_usage) + } + + pub(crate) fn _challenge_aggregate( + cluster_id: &ClusterId, + era_id: DdcEra, + aggregate: &A, + ) -> Result> { + let number_of_identifiers = T::MAX_MERKLE_NODE_IDENTIFIER; + + log::info!( + "🚀 Challenge process starts when bucket sub aggregates are not in consensus!" + ); + + let aggregate_key = aggregate.get_key(); + let merkle_node_ids = Self::_find_random_merkle_node_ids( + number_of_identifiers.into(), + aggregate.get_number_of_leaves(), + aggregate_key.clone(), + ); + + log::info!( + "🚀 Merkle Node Identifiers for aggregate key: {:?} identifiers: {:?}", + aggregate_key, + merkle_node_ids + ); + + let aggregator = aggregate.get_aggregator(); + + let challenge_response = Self::_fetch_challenge_responses( + cluster_id, + era_id, + aggregate_key.clone(), + merkle_node_ids, + aggregator.clone(), + ) + .map_err(|err| vec![err])?; + + log::info!( + "🚀 Fetched challenge response for aggregate key: {:?}, challenge_response: {:?}", + aggregate_key, + challenge_response + ); + + let calculated_merkle_root = Self::_get_hash_from_merkle_path( + challenge_response, + cluster_id, + era_id, + aggregate_key.clone(), + )?; + + log::info!( + "🚀 Calculated merkle root for aggregate key: {:?}, calculated_merkle_root: {:?}", + aggregate_key, + calculated_merkle_root + ); + + let traverse_response = Self::_fetch_traverse_response( + era_id, + aggregate_key.clone(), + vec![1], + 1, + &aggregator.node_params, + ) + .map_err(|_| { + vec![OCWError::TraverseResponseRetrievalError { + cluster_id: *cluster_id, + era_id, + aggregate_key: aggregate_key.clone(), + aggregator: aggregator.node_pub_key, + }] + })?; + + if let Some(root_merkle_node) = traverse_response.first() { + let mut merkle_root_buf = [0u8; _BUF_SIZE]; + let bytes = + Base64::decode(root_merkle_node.hash.clone(), &mut merkle_root_buf).unwrap(); // todo! remove unwrap + let traversed_merkle_root = ActivityHash::from(sp_core::H256::from_slice(bytes)); + + log::info!( + "🚀 Fetched merkle root for aggregate key: {:?} traversed_merkle_root: {:?}", + aggregate_key, + traversed_merkle_root + ); + + let is_matched = if calculated_merkle_root == traversed_merkle_root { + log::info!( + "🚀👍 The aggregate with hash {:?} and key {:?} has passed the challenge.", + aggregate.hash::(), + aggregate_key, + ); + + true + } else { + log::info!( + "🚀👎 The aggregate with hash {:?} and key {:?} has not passed the challenge.", + aggregate.hash::(), + aggregate_key, + ); + + false + }; + + Ok(is_matched) + } else { + Ok(false) + } + } + + pub(crate) fn _get_hash_from_merkle_path( + challenge_response: ChallengeAggregateResponse, + cluster_id: &ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + ) -> Result> { + log::info!("Getting hash from merkle tree path for aggregate key: {:?}", aggregate_key); + + let mut resulting_hash = ActivityHash::default(); + + for proof in challenge_response.proofs { + let leaf_record_hashes: Vec = match aggregate_key { + AggregateKey::BucketSubAggregateKey(_, _) => proof + .leafs + .into_iter() + .map(|p| NodeAggregateLeaf::leaf_hash::(&p)) + .collect(), + AggregateKey::NodeAggregateKey(_) => proof + .leafs + .into_iter() + .map(|p| BucketSubAggregateLeaf::leaf_hash::(&p)) + .collect(), + }; + + let leaf_record_hashes_string: Vec = + leaf_record_hashes.clone().into_iter().map(hex::encode).collect(); + + log::info!( + "🚀 Fetched leaf record hashes aggregate key: {:?} leaf_record_hashes: {:?}", + aggregate_key, + leaf_record_hashes_string + ); + + let leaf_node_root = + Self::create_merkle_root(cluster_id, era_id, &leaf_record_hashes) + .map_err(|err| vec![err])?; + + log::info!( + "🚀 Fetched leaf record root aggregate key: {:?} leaf_record_root_hash: {:?}", + aggregate_key, + hex::encode(leaf_node_root) + ); + + let paths = proof.path.iter().rev(); + + resulting_hash = leaf_node_root; + for path in paths { + let mut dec_buf = [0u8; _BUF_SIZE]; + let bytes = Base64::decode(path, &mut dec_buf).unwrap(); // todo! remove unwrap + let path_hash: ActivityHash = + ActivityHash::from(sp_core::H256::from_slice(bytes)); + + let node_root = + Self::create_merkle_root(cluster_id, era_id, &[resulting_hash, path_hash]) + .map_err(|err| vec![err])?; + + log::info!("🚀 Fetched leaf node root aggregate_key: {:?} for path:{:?} leaf_node_hash: {:?}", + aggregate_key, path, hex::encode(node_root)); + + resulting_hash = node_root; + } + } + + Ok(resulting_hash) + } + + pub(crate) fn _find_random_merkle_node_ids( + number_of_identifiers: usize, + number_of_leaves: u64, + aggregate_key: AggregateKey, + ) -> Vec { + let nonce_key = match aggregate_key { + AggregateKey::NodeAggregateKey(node_id) => node_id, + AggregateKey::BucketSubAggregateKey(.., node_id) => node_id, + }; + + let nonce = Self::_store_and_fetch_nonce(nonce_key); + let mut small_rng = SmallRng::seed_from_u64(nonce); + + let total_levels = number_of_leaves.ilog2() + 1; + let int_list: Vec = (0..total_levels as u64).collect(); + + let ids: Vec = int_list + .choose_multiple(&mut small_rng, number_of_identifiers) + .cloned() + .collect::>(); + + ids + } + + /// Computes the consensus for a set of partial activities across multiple buckets within a + /// given cluster and era. + /// + /// This function collects activities from various buckets, groups them by their consensus + /// ID, and then determines if a consensus is reached for each group based on the minimum + /// number of nodes and a given threshold. If the consensus is reached, the activity is + /// included in the result. Otherwise, appropriate errors are returned. + /// + /// # Input Parameters + /// - `cluster_id: &ClusterId`: The ID of the cluster for which consensus is being computed. + /// - `era_id: DdcEra`: The era ID within the cluster. + /// - `buckets_aggregates_by_aggregator: &[(NodePubKey, Vec)]`: A list of tuples, where + /// each tuple contains a node's public key and a vector of activities reported for that + /// bucket. + /// - `redundancy_factor: u16`: The number of aggregators that should report total activity + /// for a node or a bucket + /// - `quorum: Percent`: The threshold percentage that determines if an activity is in + /// consensus. + /// + /// # Output + /// - `Result, Vec>`: + /// - `Ok(Vec)`: A vector of activities that have reached consensus. + /// - `Err(Vec)`: A vector of errors indicating why consensus was not reached + /// for some activities. + pub(crate) fn group_buckets_sub_aggregates_by_consistency( + cluster_id: &ClusterId, + era_id: DdcEra, + buckets_aggregates_by_aggregator: Vec<(AggregatorInfo, Vec)>, + redundancy_factor: u16, + quorum: Percent, + ) -> ConsistentGroups { + let mut buckets_sub_aggregates: Vec = Vec::new(); + + log::info!( + "🏠⏳ Starting fetching bucket sub-aggregates for cluster_id: {:?} for era_id: {:?}", + cluster_id, + era_id + ); + for (aggregator_info, buckets_aggregates_resp) in + buckets_aggregates_by_aggregator.clone() + { + for bucket_aggregate_resp in buckets_aggregates_resp { + for bucket_sub_aggregate_resp in bucket_aggregate_resp.sub_aggregates.clone() { + let bucket_sub_aggregate = BucketSubAggregate { + bucket_id: bucket_aggregate_resp.bucket_id, + node_id: bucket_sub_aggregate_resp.NodeID, + stored_bytes: bucket_sub_aggregate_resp.stored_bytes, + transferred_bytes: bucket_sub_aggregate_resp.transferred_bytes, + number_of_puts: bucket_sub_aggregate_resp.number_of_puts, + number_of_gets: bucket_sub_aggregate_resp.number_of_gets, + aggregator: aggregator_info.clone(), + }; + + buckets_sub_aggregates.push(bucket_sub_aggregate); + } + + log::info!("🏠🚀 Fetched Bucket sub-aggregates for cluster_id: {:?} for era_id: {:?} for bucket_id {:?}::: Bucket Sub-Aggregates are {:?}", cluster_id, era_id, bucket_aggregate_resp.bucket_id, bucket_aggregate_resp.sub_aggregates); + } + } + + let buckets_sub_aggregates_groups = + Self::group_by_consistency(buckets_sub_aggregates, redundancy_factor, quorum); + + log::info!("🏠🌕 Bucket Sub-Aggregates, which are in consensus for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, buckets_sub_aggregates_groups.in_consensus); + log::info!("🏠🌗 Bucket Sub-Aggregates, which are in quorum for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, buckets_sub_aggregates_groups.in_quorum); + log::info!("🏠🌘 Bucket Sub-Aggregates, which are neither in consensus nor in quorum for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, buckets_sub_aggregates_groups.in_others); + + buckets_sub_aggregates_groups + } + + #[allow(dead_code)] + pub(crate) fn prepare_begin_billing_report( + cluster_id: &ClusterId, + ) -> Result, OCWError> { + Ok(Self::get_era_for_payout(cluster_id, EraValidationStatus::ReadyForPayout)) + // todo! get start and end values based on result + } + + pub(crate) fn prepare_begin_charging_customers( + cluster_id: &ClusterId, + batch_size: usize, + ) -> Result, Vec> { + if let Some((era_id, start, end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::Initialized + { + if let Some((_, _, customers_activity_batch_roots, _, _, _)) = + Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_customer_activity( + cluster_id, + era_id, + customers_activity_batch_roots, + ) + } else { + let era_activity = EraActivity { id: era_id, start, end }; + + let _ = Self::process_dac_era(cluster_id, Some(era_activity), batch_size)?; + + if let Some((_, _, customers_activity_batch_roots, _, _, _)) = + Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_customer_activity( + cluster_id, + era_id, + customers_activity_batch_roots, + ) + } else { + Ok(None) + } + } + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + pub(crate) fn fetch_customer_activity( + cluster_id: &ClusterId, + era_id: DdcEra, + customers_activity_batch_roots: Vec, + ) -> Result, Vec> { + if let Some(max_batch_index) = customers_activity_batch_roots.len().checked_sub(1) + // -1 cause payout expects max_index, not length + { + let max_batch_index: u16 = max_batch_index.try_into().map_err(|_| { + vec![OCWError::BatchIndexConversionFailed { cluster_id: *cluster_id, era_id }] + })?; + Ok(Some((era_id, max_batch_index))) + } else { + Err(vec![OCWError::EmptyCustomerActivity { cluster_id: *cluster_id, era_id }]) + } + } + + pub(crate) fn prepare_send_charging_customers_batch( + cluster_id: &ClusterId, + batch_size: usize, + ) -> Result)>, Vec> { + if let Some((era_id, start, end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::ChargingCustomers + { + if let Some(( + bucket_nodes_activity_in_consensus, + _, + customers_activity_batch_roots, + _, + _, + _, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_charging_activities( + cluster_id, + batch_size, + era_id, + bucket_nodes_activity_in_consensus, + customers_activity_batch_roots, + ) + } else { + let era_activity = EraActivity { id: era_id, start, end }; + + let _ = Self::process_dac_era(cluster_id, Some(era_activity), batch_size)?; + + if let Some(( + bucket_nodes_activity_in_consensus, + _, + customers_activity_batch_roots, + _, + _, + _, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_charging_activities( + cluster_id, + batch_size, + era_id, + bucket_nodes_activity_in_consensus, + customers_activity_batch_roots, + ) + } else { + Ok(None) + } + } + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + fn fetch_charging_activities( + cluster_id: &ClusterId, + batch_size: usize, + era_id: DdcEra, + bucket_nodes_activity_in_consensus: Vec, + customers_activity_batch_roots: Vec, + ) -> Result)>, Vec> { + let batch_index = T::PayoutVisitor::get_next_customer_batch_for_payment( + cluster_id, era_id, + ) + .map_err(|_| { + vec![OCWError::BillingReportDoesNotExist { cluster_id: *cluster_id, era_id }] + })?; + + if let Some(index) = batch_index { + let i: usize = index.into(); + // todo! store batched activity to avoid splitting it again each time + let customers_activity_batched = + Self::split_to_batches(&bucket_nodes_activity_in_consensus, batch_size); + + let batch_root = customers_activity_batch_roots[i]; + let store = MemStore::default(); + let mut mmr: MMR> = + MemMMR::<_, MergeActivityHash>::new(0, &store); + + let leaf_position_map: Vec<(ActivityHash, u64)> = customers_activity_batch_roots + .iter() + .map(|a| (*a, mmr.push(*a).unwrap())) + .collect(); + + let leaf_position: Vec<(u64, ActivityHash)> = leaf_position_map + .iter() + .filter(|&(l, _)| l == &batch_root) + .map(|&(ref l, p)| (p, *l)) + .collect(); + let position: Vec = + leaf_position.clone().into_iter().map(|(p, _)| p).collect(); + + let proof = mmr + .gen_proof(position) + .map_err(|_| OCWError::FailedToCreateMerkleProof { + cluster_id: *cluster_id, + era_id, + }) + .map_err(|e| vec![e])? + .proof_items() + .to_vec(); + + let batch_proof = MMRProof { + mmr_size: mmr.mmr_size(), + proof, + leaf_with_position: leaf_position[0], + }; + Ok(Some(( + era_id, + CustomerBatch { + batch_index: index, + payers: customers_activity_batched[i] + .iter() + .map(|activity| { + let account_id = + T::CustomerVisitor::get_bucket_owner(&activity.bucket_id) + .unwrap(); + let customer_usage = CustomerUsage { + transferred_bytes: activity.transferred_bytes, + stored_bytes: activity.stored_bytes, + number_of_puts: activity.number_of_puts, + number_of_gets: activity.number_of_gets, + }; + (account_id, activity.bucket_id, customer_usage) + }) + .collect(), + batch_proof, + }, + ))) + } else { + Ok(None) + } + } + + pub(crate) fn prepare_end_charging_customers( + cluster_id: &ClusterId, + ) -> Result, OCWError> { + if let Some((era_id, _start, _end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::ChargingCustomers && + T::PayoutVisitor::all_customer_batches_processed(cluster_id, era_id) + { + return Ok(Some(era_id)); + } + } + Ok(None) + } + + pub(crate) fn prepare_begin_rewarding_providers( + cluster_id: &ClusterId, + batch_size: usize, + ) -> Result, Vec> { + if let Some((era_id, start, end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + let nodes_total_usages = Self::get_nodes_total_usage(cluster_id)?; + + let nodes_total_usage: i64 = nodes_total_usages + .iter() + .filter_map(|usage| usage.as_ref().map(|u| u.stored_bytes)) + .sum(); + + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::CustomersChargedWithFees + { + if let Some(( + _, + _, + _, + nodes_activity_in_consensus, + _, + nodes_activity_batch_roots, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_reward_activities( + cluster_id, + era_id, + nodes_activity_in_consensus, + nodes_activity_batch_roots, + nodes_total_usage, + ) + } else { + let era_activity = EraActivity { id: era_id, start, end }; + + let _ = Self::process_dac_era(cluster_id, Some(era_activity), batch_size)?; + + if let Some(( + _, + _, + _, + nodes_activity_in_consensus, + _, + nodes_activity_batch_roots, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_reward_activities( + cluster_id, + era_id, + nodes_activity_in_consensus, + nodes_activity_batch_roots, + nodes_total_usage, + ) + } else { + Ok(None) + } + } + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + pub(crate) fn fetch_reward_activities( + cluster_id: &ClusterId, + era_id: DdcEra, + nodes_activity_in_consensus: Vec, + nodes_activity_batch_roots: Vec, + current_nodes_total_usage: i64, + ) -> Result, Vec> { + if let Some(max_batch_index) = nodes_activity_batch_roots.len().checked_sub(1) + // -1 cause payout expects max_index, not length + { + let max_batch_index: u16 = max_batch_index.try_into().map_err(|_| { + vec![OCWError::BatchIndexConversionFailed { cluster_id: *cluster_id, era_id }] + })?; + + let mut total_node_usage = NodeUsage { + transferred_bytes: 0, + stored_bytes: current_nodes_total_usage, + number_of_puts: 0, + number_of_gets: 0, + }; + + for activity in nodes_activity_in_consensus { + total_node_usage.transferred_bytes += activity.transferred_bytes; + total_node_usage.stored_bytes += activity.stored_bytes; + total_node_usage.number_of_puts += activity.number_of_puts; + total_node_usage.number_of_gets += activity.number_of_gets; + } + + Ok(Some((era_id, max_batch_index, total_node_usage))) + } else { + Err(vec![OCWError::EmptyCustomerActivity { cluster_id: *cluster_id, era_id }]) + } + } + + pub(crate) fn prepare_send_rewarding_providers_batch( + cluster_id: &ClusterId, + batch_size: usize, + ) -> Result)>, Vec> { + if let Some((era_id, start, end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::RewardingProviders + { + if let Some(( + _, + _, + _, + nodes_activity_in_consensus, + _, + nodes_activity_batch_roots, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_reward_provider_batch( + cluster_id, + batch_size, + era_id, + nodes_activity_in_consensus, + nodes_activity_batch_roots, + ) + } else { + let era_activity = EraActivity { id: era_id, start, end }; + + let _ = Self::process_dac_era(cluster_id, Some(era_activity), batch_size)?; + + if let Some(( + _, + _, + _, + nodes_activity_in_consensus, + _, + nodes_activity_batch_roots, + )) = Self::fetch_validation_activities::( + cluster_id, era_id, + ) { + Self::fetch_reward_provider_batch( + cluster_id, + batch_size, + era_id, + nodes_activity_in_consensus, + nodes_activity_batch_roots, + ) + } else { + Ok(None) + } + } + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + fn fetch_reward_provider_batch( + cluster_id: &ClusterId, + batch_size: usize, + era_id: DdcEra, + nodes_activity_in_consensus: Vec, + nodes_activity_batch_roots: Vec, + ) -> Result)>, Vec> { + let batch_index = T::PayoutVisitor::get_next_provider_batch_for_payment( + cluster_id, era_id, + ) + .map_err(|_| { + vec![OCWError::BillingReportDoesNotExist { cluster_id: *cluster_id, era_id }] + })?; + + if let Some(index) = batch_index { + let i: usize = index.into(); + // todo! store batched activity to avoid splitting it again each time + let nodes_activity_batched = + Self::split_to_batches(&nodes_activity_in_consensus, batch_size); + + let batch_root = nodes_activity_batch_roots[i]; + let store = MemStore::default(); + let mut mmr: MMR> = + MemMMR::<_, MergeActivityHash>::new(0, &store); + + let leaf_position_map: Vec<(ActivityHash, u64)> = nodes_activity_batch_roots + .iter() + .map(|a| (*a, mmr.push(*a).unwrap())) + .collect(); + + let leaf_position: Vec<(u64, ActivityHash)> = leaf_position_map + .iter() + .filter(|&(l, _)| l == &batch_root) + .map(|&(ref l, p)| (p, *l)) + .collect(); + let position: Vec = + leaf_position.clone().into_iter().map(|(p, _)| p).collect(); + + let proof = mmr + .gen_proof(position) + .map_err(|_| { + vec![OCWError::FailedToCreateMerkleProof { + cluster_id: *cluster_id, + era_id, + }] + })? + .proof_items() + .to_vec(); + + // todo! attend [i] through get(i).ok_or() + // todo! attend accountid conversion + let batch_proof = MMRProof { + mmr_size: mmr.mmr_size(), + proof, + leaf_with_position: leaf_position[0], + }; + Ok(Some(( + era_id, + ProviderBatch { + batch_index: index, + payees: nodes_activity_batched[i] + .iter() + .map(|activity| { + let node_id = activity.clone().node_id; + let provider_id = Self::fetch_provider_id(node_id).unwrap(); // todo! remove unwrap + let node_usage = NodeUsage { + transferred_bytes: activity.transferred_bytes, + stored_bytes: activity.stored_bytes, + number_of_puts: activity.number_of_puts, + number_of_gets: activity.number_of_gets, + }; + (provider_id, node_usage) + }) + .collect(), + batch_proof, + }, + ))) + } else { + Ok(None) + } + } + + pub(crate) fn prepare_end_rewarding_providers( + cluster_id: &ClusterId, + ) -> Result, OCWError> { + if let Some((era_id, _start, _end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::RewardingProviders && + T::PayoutVisitor::all_provider_batches_processed(cluster_id, era_id) + { + return Ok(Some(era_id)); + } + } + Ok(None) + } + + pub(crate) fn prepare_end_billing_report( + cluster_id: &ClusterId, + ) -> Result, OCWError> { + if let Some((era_id, _start, _end)) = + Self::get_era_for_payout(cluster_id, EraValidationStatus::PayoutInProgress) + { + if T::PayoutVisitor::get_billing_report_status(cluster_id, era_id) == + PayoutState::ProvidersRewarded + { + return Ok(Some(era_id)); + } + } + Ok(None) + } + + pub(crate) fn derive_key(cluster_id: &ClusterId, era_id: DdcEra) -> Vec { + format!("offchain::activities::{:?}::{:?}", cluster_id, era_id).into_bytes() + } + + pub(crate) fn store_current_validator(validator: Vec) { + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, &key, &validator); + } + + pub(crate) fn fetch_current_validator() -> Result, OCWError> { + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + + match sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + Some(data) => Ok(data), + None => Err(OCWError::FailedToFetchCurrentValidator), + } + } + + #[allow(clippy::too_many_arguments)] // todo! (2) refactor into 2 different methods (for customers and nodes) + use type info for + // derive_key + // todo! introduce new struct for input and remove clippy::type_complexity + pub(crate) fn store_validation_activities( + // todo! (3) add tests + cluster_id: &ClusterId, + era_id: DdcEra, + bucket_nodes_activity_in_consensus: &[A], + customers_activity_root: ActivityHash, + customers_activity_batch_roots: &[ActivityHash], + nodes_activity_in_consensus: &[B], + nodes_activity_root: ActivityHash, + nodes_activity_batch_roots: &[ActivityHash], + ) { + let key = Self::derive_key(cluster_id, era_id); + let encoded_tuple = ( + bucket_nodes_activity_in_consensus, + customers_activity_root, + customers_activity_batch_roots, + nodes_activity_in_consensus, + nodes_activity_root, + nodes_activity_batch_roots, + ) + .encode(); + + // Store the serialized data in local offchain storage + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, &key, &encoded_tuple); + } + + pub(crate) fn get_nodes_total_usage( + cluster_id: &ClusterId, + ) -> Result>, Vec> { + let mut results: Vec> = Vec::new(); + let mut errors: Vec = Vec::new(); + + let nodes = match T::ClusterManager::get_nodes(cluster_id) { + Ok(nodes_pub_keys) => nodes_pub_keys, + Err(_) => { + errors.push(OCWError::FailedToFetchClusterNodes); + return Err(errors); + }, + }; + + for node_pub_key in nodes.iter() { + match T::NodeVisitor::get_total_usage(node_pub_key) { + Ok(usage) => results.push(usage), + Err(_err) => { + errors.push(OCWError::FailedToFetchNodeTotalUsage { + cluster_id: *cluster_id, + node_pub_key: node_pub_key.clone(), + }); + }, + } + } + + if !errors.is_empty() { + return Err(errors); + } + + Ok(results) + } + + #[allow(clippy::type_complexity)] + pub(crate) fn fetch_validation_activities( + // todo! (4) add tests + // todo! introduce new struct for results and remove clippy::type_complexity + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Option<( + Vec, + ActivityHash, + Vec, + Vec, + ActivityHash, + Vec, + )> { + log::info!( + "🏠 Off-chain validation_activities cache hit for ClusterId: {:?} EraId: {:?}", + cluster_id, + era_id + ); + let key = Self::derive_key(cluster_id, era_id); + + // Retrieve encoded tuple from local storage + let encoded_tuple = + match sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + Some(data) => data, + None => return None, + }; + + // Attempt to decode tuple from bytes + match Decode::decode(&mut &encoded_tuple[..]) { + Ok(( + bucket_nodes_activity_in_consensus, + customers_activity_root, + customers_activity_batch_roots, + nodes_activity_in_consensus, + nodes_activity_root, + nodes_activity_batch_roots, + )) => Some(( + bucket_nodes_activity_in_consensus, + customers_activity_root, + customers_activity_batch_roots, + nodes_activity_in_consensus, + nodes_activity_root, + nodes_activity_batch_roots, + )), + Err(err) => { + // Print error message with details of the decoding error + log::error!("🦀Decoding error: {:?}", err); + None + }, + } + } + + pub(crate) fn _store_and_fetch_nonce(node_id: String) -> u64 { + let key = format!("offchain::activities::nonce::{:?}", node_id).into_bytes(); + let encoded_nonce = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) + .unwrap_or_else(|| 0.encode()); + + let nonce_data = match Decode::decode(&mut &encoded_nonce[..]) { + Ok(nonce) => nonce, + Err(err) => { + // Print error message with details of the decoding error + log::error!("🦀Decoding error while fetching nonce: {:?}", err); + 0 + }, + }; + + let new_nonce = nonce_data + 1; + + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, &key, &new_nonce.encode()); + nonce_data + } + pub(crate) fn store_provider_id( + // todo! (3) add tests + node_id: String, + provider_id: A, + ) { + let key = format!("offchain::activities::provider_id::{:?}", node_id).into_bytes(); + let encoded_tuple = provider_id.encode(); + + // Store the serialized data in local offchain storage + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, &key, &encoded_tuple); + } + + pub(crate) fn fetch_provider_id(node_id: String) -> Option { + let key = format!("offchain::activities::provider_id::{:?}", node_id).into_bytes(); + // Retrieve encoded tuple from local storage + let encoded_tuple = + match sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + Some(data) => data, + None => return None, + }; + + match Decode::decode(&mut &encoded_tuple[..]) { + Ok(provider_id) => Some(provider_id), + Err(err) => { + // Print error message with details of the decoding error + log::error!("🦀Decoding error while fetching provider id: {:?}", err); + None + }, + } + } + /// Converts a vector of activity batches into their corresponding Merkle roots. + /// + /// This function takes a vector of activity batches, where each batch is a vector of + /// activities. It computes the Merkle root for each batch by first hashing each activity + /// and then combining these hashes into a single Merkle root. + /// + /// # Input Parameters + /// - `activities: Vec>`: A vector of vectors, where each inner vector represents a + /// batch of activities. + /// + /// # Output + /// - `Vec`: A vector of Merkle roots, one for each batch of activities. + pub(crate) fn convert_to_batch_merkle_roots( + cluster_id: &ClusterId, + era_id: DdcEra, + activities: Vec>, + ) -> Result, OCWError> { + activities + .into_iter() + .map(|inner_vec| { + let activity_hashes: Vec = + inner_vec.into_iter().map(|a| a.hash::()).collect(); + Self::create_merkle_root(cluster_id, era_id, &activity_hashes).map_err(|_| { + OCWError::FailedToCreateMerkleRoot { cluster_id: *cluster_id, era_id } + }) + }) + .collect::, OCWError>>() + } + + /// Splits a slice of activities into batches of a specified size. + /// + /// This function sorts the given activities and splits them into batches of the specified + /// size. Each batch is returned as a separate vector. + /// + /// # Input Parameters + /// - `activities: &[A]`: A slice of activities to be split into batches. + /// - `batch_size: usize`: The size of each batch. + /// + /// # Output + /// - `Vec>`: A vector of vectors, where each inner vector is a batch of activities. + pub(crate) fn split_to_batches( + activities: &[A], + batch_size: usize, + ) -> Vec> { + if activities.is_empty() { + return vec![]; + } + // Sort the activities first + let mut sorted_activities = activities.to_vec(); + sorted_activities.sort(); // Sort using the derived Ord trait + + // Split the sorted activities into chunks and collect them into vectors + sorted_activities.chunks(batch_size).map(|chunk| chunk.to_vec()).collect() + } + + /// Creates a Merkle root from a list of activity hashes. + /// + /// This function takes a slice of `ActivityHash` and constructs a Merkle tree + /// using an in-memory store. It returns a tuple containing the Merkle root hash, + /// the size of the Merkle tree, and a vector mapping each input leaf to its position + /// in the Merkle tree. + /// + /// # Input Parameters + /// + /// * `leaves` - A slice of `ActivityHash` representing the leaves of the Merkle tree. + /// + /// # Output + /// + /// A `Result` containing: + /// * A tuple with the Merkle root `ActivityHash`, the size of the Merkle tree, and a vector + /// mapping each input leaf to its position in the Merkle tree. + /// * `OCWError::FailedToCreateMerkleRoot` if there is an error creating the Merkle root. + pub(crate) fn create_merkle_root( + cluster_id: &ClusterId, + era_id: DdcEra, + leaves: &[ActivityHash], + ) -> Result { + if leaves.is_empty() { + return Ok(ActivityHash::default()); + } + + let store = MemStore::default(); + let mut mmr: MMR> = + MemMMR::<_, MergeActivityHash>::new(0, &store); + + let mut leaves_with_position: Vec<(u64, ActivityHash)> = + Vec::with_capacity(leaves.len()); + + for &leaf in leaves { + match mmr.push(leaf) { + Ok(pos) => leaves_with_position.push((pos, leaf)), + Err(_) => + return Err(OCWError::FailedToCreateMerkleRoot { + cluster_id: *cluster_id, + era_id, + }), + } + } + + mmr.get_root() + .map_err(|_| OCWError::FailedToCreateMerkleRoot { cluster_id: *cluster_id, era_id }) + } + + /// Verify whether leaf is part of tree + /// + /// Parameters: + /// - `root`: merkle root + /// - `leaf`: Leaf of the tree + pub(crate) fn proof_merkle_leaf( + root: ActivityHash, + batch_proof: &MMRProof, + ) -> Result> { + let proof: MerkleProof = + MerkleProof::new(batch_proof.mmr_size, batch_proof.proof.clone()); + proof + .verify(root, vec![batch_proof.leaf_with_position]) + .map_err(|_| Error::::FailToVerifyMerkleProof) + } + + // todo! simplify method by removing start/end from the result + pub(crate) fn get_era_for_payout( + cluster_id: &ClusterId, + status: EraValidationStatus, + ) -> Option<(DdcEra, i64, i64)> { + let mut smallest_era_id: Option = None; + let mut start_era: i64 = Default::default(); + let mut end_era: i64 = Default::default(); + + for (stored_cluster_id, era_id, validation) in EraValidations::::iter() { + if stored_cluster_id == *cluster_id && + validation.status == status && + (smallest_era_id.is_none() || era_id < smallest_era_id.unwrap()) + { + smallest_era_id = Some(era_id); + start_era = validation.start_era; + end_era = validation.end_era; + } + } + + smallest_era_id.map(|era_id| (era_id, start_era, end_era)) + } + + /// Retrieves the last era in which the specified validator participated for a given + /// cluster. + /// + /// This function iterates through all eras in `EraValidations` for the given `cluster_id`, + /// filtering for eras where the specified `validator` is present in the validators list. + /// It returns the maximum era found where the validator participated. + /// + /// # Input Parameters + /// - `cluster_id: &ClusterId`: The ID of the cluster to check for the validator's + /// participation. + /// - `validator: T::AccountId`: The account ID of the validator whose participation is + /// being checked. + /// + /// # Output + /// - `Result, OCWError>`: + /// - `Ok(Some(DdcEra))`: The maximum era in which the validator participated. + /// - `Ok(None)`: The validator did not participate in any era for the given cluster. + /// - `Err(OCWError)`: An error occurred while retrieving the data. + // todo! add tests for start and end era + pub(crate) fn get_last_validated_era( + cluster_id: &ClusterId, + validator: T::AccountId, + ) -> Result, OCWError> { + let mut max_era: Option = None; + + // Iterate through all eras in EraValidations for the given cluster_id + >::iter_prefix(cluster_id) + .filter_map(|(era, validation)| { + // Filter for validators that contain the given validator + if validation + .validators + .values() + .any(|validators| validators.contains(&validator)) + { + Some(era) + } else { + None + } + }) + .for_each(|era| { + // Update max_era to the maximum era found + if let Some(current_max) = max_era { + if era > current_max { + max_era = Some(era); + } + } else { + max_era = Some(era); + } + }); + + Ok(max_era) + } + + /// Fetch current era across all DAC nodes to validate. + /// + /// Parameters: + /// - `cluster_id`: cluster id of a cluster + /// - `dac_nodes`: List of DAC nodes + pub(crate) fn get_era_for_validation( + // todo! this needs to be rewriten - too complex and inefficient + cluster_id: &ClusterId, + dac_nodes: &[(NodePubKey, StorageNodeParams)], + ) -> Result, OCWError> { + let current_validator_data = Self::fetch_current_validator()?; + + let current_validator = T::AccountId::decode(&mut ¤t_validator_data[..]).unwrap(); + + let last_validated_era = Self::get_last_validated_era(cluster_id, current_validator)? + .unwrap_or_else(DdcEra::default); + + log::info!("🚀 last_validated_era for cluster_id: {:?}", last_validated_era); + let all_ids = Self::fetch_processed_era_for_node(cluster_id, dac_nodes)?; + + let ids_greater_than_last_validated_era: Vec = all_ids + .iter() + .flat_map(|eras| eras.iter().filter(|&ids| ids.id > last_validated_era).cloned()) + .sorted() + .collect::>(); + + let mut grouped_data: Vec<(u32, EraActivity)> = Vec::new(); + for (key, chunk) in + &ids_greater_than_last_validated_era.into_iter().chunk_by(|elt| elt.clone()) + { + grouped_data.push((chunk.count() as u32, key)); + } + + let all_node_eras = grouped_data + .into_iter() + .filter(|(v, _)| *v == dac_nodes.len() as u32) + .map(|(_, id)| id) + .collect::>(); + + Ok(all_node_eras.iter().cloned().min_by_key(|n| n.id)) + } + + /// Computes the consensus for a set of activities across multiple nodes within a given + /// cluster and era. + /// + /// This function collects activities from various nodes, groups them by their consensus ID, + /// and then determines if a consensus is reached for each group based on the minimum number + /// of nodes and a given threshold. If the consensus is reached, the activity is included + /// in the result. Otherwise, appropriate errors are returned. + /// + /// # Input Parameters + /// - `cluster_id: &ClusterId`: The ID of the cluster for which consensus is being computed. + /// - `era_id: DdcEra`: The era ID within the cluster. + /// - `nodes_aggregates_by_aggregator: &[(NodePubKey, Vec)]`: A list of tuples, where + /// each tuple contains a node's public key and a vector of activities reported by that + /// node. + /// - `redundancy_factor: u16`: The number of aggregators that should report total activity + /// for a node or a bucket + /// - `quorum: Percent`: The threshold percentage that determines if an activity is in + /// consensus. + /// + /// # Output + /// - `Result, Vec>`: + /// - `Ok(Vec)`: A vector of activities that have reached consensus. + /// - `Err(Vec)`: A vector of errors indicating why consensus was not reached + /// for some activities. + pub(crate) fn group_nodes_aggregates_by_consistency( + cluster_id: &ClusterId, + era_id: DdcEra, + nodes_aggregates_by_aggregator: Vec<(AggregatorInfo, Vec)>, + redundancy_factor: u16, + quorum: Percent, + ) -> ConsistentGroups { + let mut nodes_aggregates: Vec = Vec::new(); + + log::info!( + "🏠⏳ Starting fetching node aggregates for cluster_id: {:?} for era_id: {:?}", + cluster_id, + era_id + ); + + for (aggregator_info, nodes_aggregates_resp) in nodes_aggregates_by_aggregator.clone() { + for node_aggregate_resp in nodes_aggregates_resp.clone() { + let node_aggregate = NodeAggregate { + node_id: node_aggregate_resp.node_id, + stored_bytes: node_aggregate_resp.stored_bytes, + transferred_bytes: node_aggregate_resp.transferred_bytes, + number_of_puts: node_aggregate_resp.number_of_puts, + number_of_gets: node_aggregate_resp.number_of_gets, + aggregator: aggregator_info.clone(), + }; + nodes_aggregates.push(node_aggregate); + } + + log::info!("🏠🚀 Fetched Node-aggregates for cluster_id: {:?} for era_id: {:?} :::Node Aggregates are {:?}", cluster_id, era_id, nodes_aggregates); + } + + let nodes_aggregates_groups = + Self::group_by_consistency(nodes_aggregates, redundancy_factor, quorum); + + log::info!("🏠🌕 Node Aggregates, which are in consensus for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, nodes_aggregates_groups.in_consensus); + log::info!("🏠🌗 Node Aggregates, which are in quorum for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, nodes_aggregates_groups.in_quorum); + log::info!("🏠🌘 Node Aggregates, which are neither in consensus nor in quorum for cluster_id: {:?} for era_id: {:?}::: {:?}", cluster_id, era_id, nodes_aggregates_groups.in_others); + + nodes_aggregates_groups + } + + pub(crate) fn group_by_consistency( + aggregates: Vec, + redundancy_factor: u16, + quorum: Percent, + ) -> ConsistentGroups + where + A: Aggregate + Clone, + { + let mut consistent_aggregates: BTreeMap> = BTreeMap::new(); + + for aggregate in aggregates.iter() { + consistent_aggregates + .entry(aggregate.hash::()) + .or_default() + .push(aggregate.clone()); + } + + let mut in_consensus = Vec::new(); + let mut in_quorum = Vec::new(); + let mut in_others = Vec::new(); + + let max_aggregates_count = redundancy_factor; + let quorum_threshold = quorum * max_aggregates_count; + + for (hash, group) in consistent_aggregates { + if group.len() == usize::from(max_aggregates_count) { + in_consensus.push(ConsistentGroup(hash, group)); + } else if group.len() >= quorum_threshold.into() { + in_quorum.push(ConsistentGroup(hash, group)); + } else { + in_others.push(ConsistentGroup(hash, group)); + } + } + + ConsistentGroups { in_consensus, in_quorum, in_others } + } + + /// Fetch cluster to validate. + fn get_cluster_to_validate() -> Result> { + // todo! to implement + Self::cluster_to_validate().ok_or(Error::ClusterToValidateRetrievalError) + } + + /// Fetch Challenge node aggregate or bucket sub-aggregate. + pub(crate) fn _fetch_challenge_responses( + cluster_id: &ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + merkle_node_identifiers: Vec, + aggregator: AggregatorInfo, + ) -> Result { + let response = Self::_fetch_challenge_response( + era_id, + aggregate_key.clone(), + merkle_node_identifiers.clone(), + &aggregator.node_params, + ) + .map_err(|_| OCWError::ChallengeResponseRetrievalError { + cluster_id: *cluster_id, + era_id, + aggregate_key, + aggregator: aggregator.node_pub_key, + })?; + + Ok(response) + } + + /// Fetch challenge response. + /// + /// Parameters: + /// - `era_id`: era id + /// - `aggregate_key`: key of the aggregate to challenge + /// - `merkle_node_identifiers`: set of merkle node identifiers to challenge + /// - `node_params`: aggregator node parameters + pub(crate) fn _fetch_challenge_response( + era_id: DdcEra, + aggregate_key: AggregateKey, + merkle_node_identifiers: Vec, + node_params: &StorageNodeParams, + ) -> Result { + let scheme = "http"; + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + + let ids = merkle_node_identifiers + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + + let url = match aggregate_key { + AggregateKey::NodeAggregateKey(node_id) => format!( + "{}://{}:{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", + scheme, host, node_params.http_port, node_id, era_id, ids + ), + AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => format!( + "{}://{}:{}/activity/buckets/{}/challenge?eraId={}&nodeId={}&merkleTreeNodeId={}", + scheme, host, node_params.http_port, bucket_id, era_id, node_id, ids + ), + }; + + let request = http::Request::get(&url); + let timeout = sp_io::offchain::timestamp() + .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); + let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; + + let response = + pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; + if response.code != SUCCESS_CODE { + return Err(http::Error::Unknown); + } + + let body = response.body().collect::>(); + serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + } + + /// Fetch traverse response. + /// + /// Parameters: + /// - `era_id`: era id + /// - `aggregate_key`: key of the aggregate to challenge + /// - `merkle_node_identifiers`: set of merkle node identifiers to challenge + /// - `levels`: a number of levels to raverse + /// - `node_params`: aggregator node parameters + pub(crate) fn _fetch_traverse_response( + era_id: DdcEra, + aggregate_key: AggregateKey, + merkle_node_identifiers: Vec, + levels: u16, + node_params: &StorageNodeParams, + ) -> Result, http::Error> { + let scheme = "http"; + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + + let ids = merkle_node_identifiers + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + + let url = match aggregate_key { + AggregateKey::NodeAggregateKey(node_id) => format!( + "{}://{}:{}/activity/nodes/{}/traverse?eraId={}&merkleTreeNodeId={}&levels={}", + scheme, host, node_params.http_port, node_id, era_id, ids, levels + ), + AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => format!( + "{}://{}:{}/activity/buckets/{}/traverse?eraId={}&nodeId={}&merkleTreeNodeId={}&levels={}", + scheme, host, node_params.http_port, bucket_id, era_id, node_id, ids, levels + ), + }; + + let request = http::Request::get(&url); + let timeout = sp_io::offchain::timestamp() + .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); + let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; + + let response = + pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; + if response.code != SUCCESS_CODE { + return Err(http::Error::Unknown); + } + + let body = response.body().collect::>(); + serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + } + + /// Fetch processed era. + /// + /// Parameters: + /// - `node_params`: DAC node parameters + #[allow(dead_code)] + pub(crate) fn fetch_processed_era( + node_params: &StorageNodeParams, + ) -> Result, http::Error> { + let scheme = "http"; + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + let url = format!("{}://{}:{}/activity/eras", scheme, host, node_params.http_port); + let request = http::Request::get(&url); + let timeout = sp_io::offchain::timestamp() + .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); + let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; + + // todo! filter by status == PROCESSED + + let response = + pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; + if response.code != SUCCESS_CODE { + return Err(http::Error::Unknown); + } + + let body = response.body().collect::>(); + + serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + } + /// Fetch customer usage. + /// + /// Parameters: + /// - `cluster_id`: cluster id of a cluster + /// - `era_id`: era id + /// - `node_params`: DAC node parameters + pub(crate) fn fetch_bucket_aggregates( + _cluster_id: &ClusterId, + era_id: DdcEra, + node_params: &StorageNodeParams, + ) -> Result, http::Error> { + let scheme = "http"; + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + let url = format!( + "{}://{}:{}/activity/buckets?eraId={}", + scheme, host, node_params.http_port, era_id + ); + + let request = http::Request::get(&url); + let timeout = sp_io::offchain::timestamp() + .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); + let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; + + let response = + pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; + if response.code != SUCCESS_CODE { + return Err(http::Error::Unknown); + } + + let body = response.body().collect::>(); + serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + } + + /// Fetch node usage. + /// + /// Parameters: + /// - `cluster_id`: cluster id of a cluster + /// - `era_id`: era id + /// - `node_params`: DAC node parameters + pub(crate) fn fetch_node_aggregates( + _cluster_id: &ClusterId, + era_id: DdcEra, + node_params: &StorageNodeParams, + ) -> Result, http::Error> { + let scheme = "http"; + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + let url = format!( + "{}://{}:{}/activity/nodes?eraId={}", + scheme, host, node_params.http_port, era_id + ); + + let request = http::Request::get(&url); + let timeout = sp_io::offchain::timestamp() + .add(rt_offchain::Duration::from_millis(RESPONSE_TIMEOUT)); + let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; + + let response = + pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; + if response.code != SUCCESS_CODE { + return Err(http::Error::Unknown); + } + + let body = response.body().collect::>(); + serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + } + + /// Fetch DAC nodes of a cluster. + /// Parameters: + /// - `cluster_id`: Cluster id of a cluster. + fn get_dac_nodes( + cluster_id: &ClusterId, + ) -> Result, Error> { + let mut dac_nodes = Vec::new(); + + let nodes = T::ClusterManager::get_nodes(cluster_id) + .map_err(|_| Error::::NodeRetrievalError)?; + + // Iterate over each node + for node_pub_key in nodes { + // Get the node parameters + if let Ok(NodeParams::StorageParams(storage_params)) = + T::NodeVisitor::get_node_params(&node_pub_key) + { + let NodePubKey::StoragePubKey(key) = node_pub_key.clone(); + let node_pub_key_ref: &[u8; 32] = key.as_ref(); + let node_pub_key_string = hex::encode(node_pub_key_ref); + log::info!( + "🏭📝Get DAC Node for cluster_id: {:?} and node_pub_key: {:?}", + cluster_id, + node_pub_key_string + ); + + // Add to the results if the mode matches + dac_nodes.push((node_pub_key, storage_params)); + } + } + + Ok(dac_nodes) + } + + fn get_node_provider_id(node_pub_key: &NodePubKey) -> Result { + T::NodeVisitor::get_node_provider_id(node_pub_key) + .map_err(|_| OCWError::FailedToFetchNodeProvider) + } + + /// Fetch node usage of an era. + /// + /// Parameters: + /// - `cluster_id`: cluster id of a cluster + /// - `era_id`: era id + /// - `node_params`: DAC node parameters + pub(crate) fn fetch_nodes_aggregates_for_era( + cluster_id: &ClusterId, + era_id: DdcEra, + dac_nodes: &[(NodePubKey, StorageNodeParams)], + ) -> Result)>, OCWError> { + let mut nodes_aggregates = Vec::new(); + + for (node_pub_key, node_params) in dac_nodes { + // todo! probably shouldn't stop when some DAC is not responding as we can still + // work with others + let aggregates = Self::fetch_node_aggregates(cluster_id, era_id, node_params) + .map_err(|_| OCWError::NodeUsageRetrievalError { + cluster_id: *cluster_id, + era_id, + node_pub_key: node_pub_key.clone(), + })?; + + for aggregate in aggregates.clone() { + let provider_id = Self::get_node_provider_id(node_pub_key).unwrap(); + Self::store_provider_id(aggregate.node_id, provider_id); // todo! this is not good - needs to be + // moved payout pallet + } + + let aggregator_info = AggregatorInfo { + node_pub_key: node_pub_key.clone(), + node_params: node_params.clone(), + }; + + nodes_aggregates.push((aggregator_info, aggregates)); + } + + Ok(nodes_aggregates) + } + + /// Fetch customer usage for an era. + /// + /// Parameters: + /// - `cluster_id`: cluster id of a cluster + /// - `era_id`: era id + /// - `node_params`: DAC node parameters + pub(crate) fn fetch_buckets_aggregates_for_era( + cluster_id: &ClusterId, + era_id: DdcEra, + dac_nodes: &[(NodePubKey, StorageNodeParams)], + ) -> Result)>, OCWError> { + let mut bucket_aggregates: Vec<(AggregatorInfo, Vec)> = + Vec::new(); + + for (node_pub_key, node_params) in dac_nodes { + // todo! probably shouldn't stop when some DAC is not responding as we can still + // work with others + let aggregates = Self::fetch_bucket_aggregates(cluster_id, era_id, node_params) + .map_err(|_| OCWError::BucketAggregatesRetrievalError { + cluster_id: *cluster_id, + era_id, + node_pub_key: node_pub_key.clone(), + })?; + + let aggregator_info = AggregatorInfo { + node_pub_key: node_pub_key.clone(), + node_params: node_params.clone(), + }; + + bucket_aggregates.push((aggregator_info, aggregates)); + } + + Ok(bucket_aggregates) + } + + /// Fetch processed era for across all nodes. + /// + /// Parameters: + /// - `cluster_id`: Cluster id + /// - `node_params`: DAC node parameters + fn fetch_processed_era_for_node( + cluster_id: &ClusterId, + dac_nodes: &[(NodePubKey, StorageNodeParams)], + ) -> Result>, OCWError> { + let mut eras = Vec::new(); + + for (node_pub_key, node_params) in dac_nodes { + // todo! probably shouldn't stop when some DAC is not responding as we can still + // work with others + + let ids = Self::fetch_processed_era(node_params).map_err(|_| { + OCWError::EraRetrievalError { + cluster_id: *cluster_id, + node_pub_key: node_pub_key.clone(), + } + })?; + + eras.push(ids); + } + + Ok(eras) + } + } + + #[pallet::call] + impl Pallet { + /// Create billing reports from a public origin. + /// + /// The origin must be Signed. + /// + /// Parameters: + /// - `cluster_id`: Cluster id of a cluster. + /// - `era`: Era id. + /// - `payers_merkle_root_hash`: Merkle root hash of payers + /// - `payees_merkle_root_hash`: Merkle root hash of payees + /// + /// Emits `BillingReportCreated` event when successful. + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn set_prepare_era_for_payout( + origin: OriginFor, + cluster_id: ClusterId, + era_activity: EraActivity, + payers_merkle_root_hash: ActivityHash, + payees_merkle_root_hash: ActivityHash, + payers_batch_merkle_root_hashes: Vec, + payees_batch_merkle_root_hashes: Vec, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + + ensure!(Self::is_ocw_validator(caller.clone()), Error::::Unauthorised); + let mut era_validation = { + let era_validations = >::get(cluster_id, era_activity.id); + + if era_validations.is_none() { + EraValidation { + payers_merkle_root_hash: ActivityHash::default(), + payees_merkle_root_hash: ActivityHash::default(), + start_era: Default::default(), + end_era: Default::default(), + validators: Default::default(), + status: EraValidationStatus::ValidatingData, + } + } else { + era_validations.unwrap() + } + }; + + // disallow signatures after era status change + ensure!( + era_validation.status == EraValidationStatus::ValidatingData, + Error::::NotExpectedState + ); + + // Ensure the validators entry exists for the specified (payers_merkle_root_hash, + // payees_merkle_root_hash) + let signed_validators = era_validation + .validators + .entry((payers_merkle_root_hash, payees_merkle_root_hash)) + .or_insert_with(Vec::new); + + ensure!(!signed_validators.contains(&caller.clone()), Error::::AlreadySignedEra); + signed_validators.push(caller.clone()); + + let percent = Percent::from_percent(T::MAJORITY); + let threshold = percent * >::get().len(); + + let mut should_deposit_ready_event = false; + if threshold <= signed_validators.len() { + // Update payers_merkle_root_hash and payees_merkle_root_hash as ones passed the + // threshold + era_validation.payers_merkle_root_hash = payers_merkle_root_hash; + era_validation.payees_merkle_root_hash = payees_merkle_root_hash; + era_validation.start_era = era_activity.start; // todo! start/end is set by the last validator and is not in consensus + era_validation.end_era = era_activity.end; + + if payers_merkle_root_hash == ActivityHash::default() && + payees_merkle_root_hash == payers_merkle_root_hash + { + era_validation.status = EraValidationStatus::PayoutSuccess; + } else { + era_validation.status = EraValidationStatus::ReadyForPayout; + } + + should_deposit_ready_event = true; + } + + // Update the EraValidations storage + >::insert(cluster_id, era_activity.id, era_validation); + Self::deposit_event(Event::::EraValidationRootsPosted { + cluster_id, + era_id: era_activity.id, + validator: caller, + payers_merkle_root_hash, + payees_merkle_root_hash, + payers_batch_merkle_root_hashes, + payees_batch_merkle_root_hashes, + }); + if should_deposit_ready_event { + Self::deposit_event(Event::::EraValidationReady { + cluster_id, + era_id: era_activity.id, + }); + } else { + Self::deposit_event(Event::::EraValidationNotReady { + cluster_id, + era_id: era_activity.id, + }); + } + + Ok(()) + } + + /// Emit consensus errors. + /// + /// The origin must be a validator. + /// + /// Parameters: + /// - errors`: List of consensus errors + /// + /// Emits `NotEnoughNodesForConsensus` OR `ActivityNotInConsensus` event depend of error + /// type, when successful. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn emit_consensus_errors( + origin: OriginFor, + errors: Vec, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(caller.clone()), Error::::Unauthorised); + + for error in errors { + match error { + OCWError::NodeUsageRetrievalError { cluster_id, era_id, node_pub_key } => { + Self::deposit_event(Event::NodeUsageRetrievalError { + cluster_id, + era_id, + node_pub_key, + validator: caller.clone(), + }); + }, + OCWError::BucketAggregatesRetrievalError { + cluster_id, + era_id, + node_pub_key, + } => { + Self::deposit_event(Event::BucketAggregatesRetrievalError { + cluster_id, + era_id, + node_pub_key, + validator: caller.clone(), + }); + }, + OCWError::EraRetrievalError { cluster_id, node_pub_key } => { + Self::deposit_event(Event::EraRetrievalError { + cluster_id, + node_pub_key, + validator: caller.clone(), + }); + }, + OCWError::PrepareEraTransactionError { + cluster_id, + era_id, + payers_merkle_root_hash, + payees_merkle_root_hash, + } => { + Self::deposit_event(Event::PrepareEraTransactionError { + cluster_id, + era_id, + payers_merkle_root_hash, + payees_merkle_root_hash, + validator: caller.clone(), + }); + }, + OCWError::BeginBillingReportTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::BeginBillingReportTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::BeginChargingCustomersTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::BeginChargingCustomersTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::SendChargingCustomersBatchTransactionError { + cluster_id, + era_id, + batch_index, + } => { + Self::deposit_event(Event::SendChargingCustomersBatchTransactionError { + cluster_id, + era_id, + batch_index, + validator: caller.clone(), + }); + }, + OCWError::SendRewardingProvidersBatchTransactionError { + cluster_id, + era_id, + batch_index, + } => { + Self::deposit_event(Event::SendRewardingProvidersBatchTransactionError { + cluster_id, + era_id, + batch_index, + validator: caller.clone(), + }); + }, + OCWError::EndChargingCustomersTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::EndChargingCustomersTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::BeginRewardingProvidersTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::BeginRewardingProvidersTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::EndRewardingProvidersTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::EndRewardingProvidersTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::EndBillingReportTransactionError { cluster_id, era_id } => { + Self::deposit_event(Event::EndBillingReportTransactionError { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::BillingReportDoesNotExist { cluster_id, era_id } => { + Self::deposit_event(Event::BillingReportDoesNotExist { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::EmptyCustomerActivity { cluster_id, era_id } => { + Self::deposit_event(Event::EmptyCustomerActivity { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::BatchIndexConversionFailed { cluster_id, era_id } => { + Self::deposit_event(Event::BatchIndexConversionFailed { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::NoAvailableSigner => { + Self::deposit_event(Event::NoAvailableSigner { validator: caller.clone() }); + }, + OCWError::NotEnoughDACNodes { num_nodes } => { + Self::deposit_event(Event::NotEnoughDACNodes { + num_nodes, + validator: caller.clone(), + }); + }, + OCWError::FailedToCreateMerkleRoot { cluster_id, era_id } => { + Self::deposit_event(Event::FailedToCreateMerkleRoot { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::FailedToCreateMerkleProof { cluster_id, era_id } => { + Self::deposit_event(Event::FailedToCreateMerkleProof { + cluster_id, + era_id, + validator: caller.clone(), + }); + }, + OCWError::FailedToFetchCurrentValidator => { + Self::deposit_event(Event::FailedToFetchCurrentValidator { + validator: caller.clone(), + }); + }, + OCWError::FailedToFetchNodeProvider => { + Self::deposit_event(Event::FailedToFetchNodeProvider { + validator: caller.clone(), + }); + }, + OCWError::FailedToFetchNodeTotalUsage { cluster_id, node_pub_key } => { + Self::deposit_event(Event::FailedToFetchNodeTotalUsage { + cluster_id, + node_pub_key, + validator: caller.clone(), + }); + }, + OCWError::BucketAggregateRetrievalError { + cluster_id, + era_id, + bucket_id, + node_pub_key, + } => { + Self::deposit_event(Event::BucketAggregateRetrievalError { + cluster_id, + era_id, + bucket_id, + node_pub_key, + validator: caller.clone(), + }); + }, + OCWError::ChallengeResponseRetrievalError { + cluster_id, + era_id, + aggregate_key, + aggregator, + } => { + Self::deposit_event(Event::ChallengeResponseRetrievalError { + cluster_id, + era_id, + aggregate_key, + aggregator, + validator: caller.clone(), + }); + }, + OCWError::TraverseResponseRetrievalError { + cluster_id, + era_id, + aggregate_key, + aggregator, + } => { + Self::deposit_event(Event::TraverseResponseRetrievalError { + cluster_id, + era_id, + aggregate_key, + aggregator, + validator: caller.clone(), + }); + }, + OCWError::FailedToFetchClusterNodes => { + Self::deposit_event(Event::FailedToFetchClusterNodes { + validator: caller.clone(), + }); + }, + OCWError::FailedToFetchDacNodes => { + Self::deposit_event(Event::FailedToFetchDacNodes { + validator: caller.clone(), + }); + }, + OCWError::EmptyConsistentGroup => { + Self::deposit_event(Event::EmptyConsistentGroup); + }, + } + } + + Ok(()) + } + + /// Set validator key. + /// + /// The origin must be a validator. + /// + /// Parameters: + /// - `ddc_validator_pub`: validator Key + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn set_validator_key( + origin: OriginFor, + ddc_validator_pub: T::AccountId, + ) -> DispatchResult { + let controller = ensure_signed(origin)?; + + let stash = T::StakingVisitor::stash_by_ctrl(&controller) + .map_err(|_| Error::::NotController)?; + + ensure!( + >::get().contains(&ddc_validator_pub), + Error::::NotValidatorStash + ); + + ValidatorToStashKey::::insert(&ddc_validator_pub, &stash); + Self::deposit_event(Event::::ValidatorKeySet { validator: ddc_validator_pub }); + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn begin_billing_report( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + start_era: i64, + end_era: i64, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + + T::PayoutVisitor::begin_billing_report(sender, cluster_id, era_id, start_era, end_era)?; + + EraValidations::::try_mutate( + cluster_id, + era_id, + |maybe_era_validations| -> DispatchResult { + maybe_era_validations.as_mut().ok_or(Error::::NoEraValidation)?.status = + EraValidationStatus::PayoutInProgress; + Ok(()) + }, + )?; + + Ok(()) + } + + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn begin_charging_customers( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::begin_charging_customers(sender, cluster_id, era_id, max_batch_index) + } + + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + // todo! remove clippy::too_many_arguments + pub fn send_charging_customers_batch( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payers: Vec<(T::AccountId, BucketId, CustomerUsage)>, + batch_proof: MMRProof, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::send_charging_customers_batch( + sender, + cluster_id, + era_id, + batch_index, + &payers, + batch_proof, + ) + } + + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn end_charging_customers( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::end_charging_customers(sender, cluster_id, era_id) + } + + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn begin_rewarding_providers( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + total_node_usage: NodeUsage, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::begin_rewarding_providers( + sender, + cluster_id, + era_id, + max_batch_index, + total_node_usage, + ) + } + + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn send_rewarding_providers_batch( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payees: Vec<(T::AccountId, NodeUsage)>, + batch_proof: MMRProof, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::send_rewarding_providers_batch( + sender, + cluster_id, + era_id, + batch_index, + &payees, + batch_proof, + ) + } + + #[pallet::call_index(9)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn end_rewarding_providers( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::end_rewarding_providers(sender, cluster_id, era_id) + } + + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn end_billing_report( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(Self::is_ocw_validator(sender.clone()), Error::::Unauthorised); + T::PayoutVisitor::end_billing_report(sender, cluster_id, era_id)?; + + let mut era_validation = >::get(cluster_id, era_id).unwrap(); // should exist + era_validation.status = EraValidationStatus::PayoutSuccess; + >::insert(cluster_id, era_id, era_validation); + + T::ClusterValidator::set_last_validated_era(&cluster_id, era_id) + } + + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn set_cluster_to_validate( + origin: OriginFor, + cluster_id: ClusterId, + ) -> DispatchResult { + ensure_root(origin)?; + ClusterToValidate::::put(cluster_id); + + Ok(()) + } + + // todo! Need to remove this + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn set_current_validator(origin: OriginFor) -> DispatchResult { + let validator = ensure_signed(origin)?; + + if !>::get().contains(&validator) { + ValidatorSet::::append(validator); + } + + Ok(()) + } + + // todo! remove this after devnet testing + #[pallet::call_index(13)] + #[pallet::weight(::WeightInfo::create_billing_reports())] // todo! implement weights + pub fn set_era_validations( + origin: OriginFor, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult { + ensure_root(origin)?; + let era_validations = >::get(cluster_id, era_id); + + if era_validations.is_none() { + let mut era_validation = EraValidation { + payers_merkle_root_hash: ActivityHash::default(), + payees_merkle_root_hash: ActivityHash::default(), + start_era: Default::default(), + end_era: Default::default(), + validators: Default::default(), + status: EraValidationStatus::PayoutSkipped, + }; + + let signed_validators = era_validation + .validators + .entry((ActivityHash::default(), ActivityHash::default())) + .or_insert_with(Vec::new); + + let validators = >::get(); + + signed_validators.extend(validators); + + >::insert(cluster_id, era_id, era_validation); + } + + Self::deposit_event(Event::::EraValidationReady { cluster_id, era_id }); + + Ok(()) + } + } + + impl ValidatorVisitor for Pallet { + fn setup_validators(validators: Vec) { + ValidatorSet::::put(validators); + } + fn is_ocw_validator(caller: T::AccountId) -> bool { + if ValidatorToStashKey::::contains_key(caller.clone()) { + >::get().contains(&caller) + } else { + false + } + } + + // todo! use batch_index and payers as part of the validation + fn is_customers_batch_valid( + cluster_id: ClusterId, + era_id: DdcEra, + _batch_index: BatchIndex, + _payers: &[(T::AccountId, BucketId, CustomerUsage)], + batch_proof: &MMRProof, + ) -> bool { + let validation_era = EraValidations::::get(cluster_id, era_id); + + match validation_era { + Some(valid_era) => { + //Self::create_merkle_root(leaves) + + let root = valid_era.payers_merkle_root_hash; + Self::proof_merkle_leaf(root, batch_proof).unwrap_or(false) + }, + None => false, + } + } + + // todo! use batch_index and payees as part of the validation + fn is_providers_batch_valid( + cluster_id: ClusterId, + era_id: DdcEra, + _batch_index: BatchIndex, + _payees: &[(T::AccountId, NodeUsage)], + batch_proof: &MMRProof, + ) -> bool { + let validation_era = EraValidations::::get(cluster_id, era_id); + + match validation_era { + Some(valid_era) => { + let root = valid_era.payees_merkle_root_hash; + Self::proof_merkle_leaf(root, batch_proof).unwrap_or(false) + }, + None => false, + } + } + } + + impl sp_application_crypto::BoundToRuntimeAppPublic for Pallet { + type Public = T::AuthorityId; + } + + impl OneSessionHandler for Pallet { + type Key = T::AuthorityId; + + fn on_genesis_session<'a, I: 'a>(validators: I) + where + I: Iterator, + { + log::info!("🙌Adding Validator from genesis session."); + let validators = validators + .map(|(_, k)| T::AccountId::decode(&mut &k.into().encode()[..]).unwrap()) + .collect::>(); + + ValidatorSet::::put(validators); // only active validators in session - this is NOT all the + // validators + } + + fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, _queued_authorities: I) + where + I: Iterator, + { + log::info!("🙌Adding Validator from new session."); + let validators = validators + .map(|(_, k)| T::AccountId::decode(&mut &k.into().encode()[..]).unwrap()) + .collect::>(); + log::info!("🙌Total validator from new session. {:?}", validators.len()); + ValidatorSet::::put(validators); + } + + fn on_disabled(_i: u32) {} + } +} diff --git a/pallets/ddc-verification/src/mock.rs b/pallets/ddc-verification/src/mock.rs new file mode 100644 index 000000000..787391186 --- /dev/null +++ b/pallets/ddc-verification/src/mock.rs @@ -0,0 +1,718 @@ +use ddc_primitives::{ + crypto, sr25519, + traits::{ClusterManager, ClusterQuery}, + BucketId, ClusterNodeKind, ClusterNodeState, ClusterNodeStatus, ClusterNodesStats, + ClusterStatus, PayoutError, PayoutState, StorageNodeMode, StorageNodePubKey, + MAX_PAYOUT_BATCH_COUNT, MAX_PAYOUT_BATCH_SIZE, +}; +use frame_election_provider_support::{ + bounds::{ElectionBounds, ElectionBoundsBuilder}, + onchain, SequentialPhragmen, +}; +use frame_support::{ + pallet_prelude::ConstU32, + parameter_types, + traits::{ConstU16, ConstU64}, + PalletId, +}; +use frame_system::mocking::MockBlock; +use pallet_staking::BalanceOf; +use sp_core::{ByteArray, H256}; +use sp_runtime::{ + curve::PiecewiseLinear, + testing::{TestXt, UintAuthorityId}, + traits::{BlakeTwo256, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify, Zero}, + BuildStorage, MultiSignature, Perbill, Percent, +}; +use sp_staking::{EraIndex, SessionIndex}; + +use crate::{self as pallet_ddc_verification, *}; + +type Block = MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub struct Test { + System: frame_system, + DdcVerification: pallet_ddc_verification, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Staking: pallet_staking, + Session: pallet_session, + } +); + +pub type Extrinsic = TestXt; +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +type Balance = u64; +type BlockNumber = u64; + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type MaxHolds = (); + type RuntimeFreezeReason = (); +} + +pallet_staking_reward_curve::build! { + const REWARD_CURVE: PiecewiseLinear<'static> = curve!( + min_inflation: 0_025_000u64, + max_inflation: 0_100_000, + ideal_stake: 0_500_000, + falloff: 0_050_000, + max_piece_count: 40, + test_precision: 0_005_000, + ); +} + +parameter_types! { + pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build(); +} + +pub struct OnChainSeqPhragmen; +impl onchain::Config for OnChainSeqPhragmen { + type System = Test; + type Solver = SequentialPhragmen; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = ConstU32<100>; + type Bounds = ElectionsBounds; +} +parameter_types! { + pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; + pub static Offset: BlockNumber = 0; + pub const Period: BlockNumber = 1; + pub static SessionsPerEra: SessionIndex = 6; + pub static SlashDeferDuration: EraIndex = 2; + pub const BondingDuration: EraIndex = 3; + pub static LedgerSlashPerEra: (BalanceOf, BTreeMap>) = (Zero::zero(), BTreeMap::new()); + pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); +} + +impl pallet_staking::Config for Test { + type Currency = Balances; + type CurrencyBalance = ::Balance; + type UnixTime = Timestamp; + type CurrencyToVote = (); + type RewardRemainder = (); + type RuntimeEvent = RuntimeEvent; + type Slash = (); + type Reward = (); + type SessionsPerEra = SessionsPerEra; + type SlashDeferDuration = SlashDeferDuration; + type AdminOrigin = frame_system::EnsureRoot; + type BondingDuration = BondingDuration; + type SessionInterface = Self; + type EraPayout = pallet_staking::ConvertCurve; + type NextNewSession = Session; + type MaxExposurePageSize = ConstU32<64>; + type OffendingValidatorsThreshold = OffendingValidatorsThreshold; + type ElectionProvider = onchain::OnChainExecution; + type GenesisElectionProvider = Self::ElectionProvider; + type TargetList = pallet_staking::UseValidatorsMap; + type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; + type MaxUnlockingChunks = ConstU32<32>; + type HistoryDepth = ConstU32<84>; + type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; + type EventListeners = (); + type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; + type WeightInfo = (); +} + +pub struct OtherSessionHandler; +impl OneSessionHandler for OtherSessionHandler { + type Key = UintAuthorityId; + + fn on_genesis_session<'a, I: 'a>(_: I) + where + I: Iterator, + AccountId: 'a, + { + } + + fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I) + where + I: Iterator, + AccountId: 'a, + { + } + + fn on_disabled(_validator_index: u32) {} +} + +impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { + type Public = UintAuthorityId; +} + +impl pallet_session::historical::Config for Test { + type FullIdentification = pallet_staking::Exposure; + type FullIdentificationOf = pallet_staking::ExposureOf; +} + +sp_runtime::impl_opaque_keys! { + pub struct SessionKeys { + pub other: OtherSessionHandler, + } +} + +impl pallet_session::Config for Test { + type SessionManager = pallet_session::historical::NoteHistoricalRoot; + type Keys = SessionKeys; + type ShouldEndSession = pallet_session::PeriodicSessions; + type SessionHandler = (OtherSessionHandler,); + type RuntimeEvent = RuntimeEvent; + type ValidatorId = AccountId; + type ValidatorIdOf = pallet_staking::StashOf; + type NextSessionRotation = pallet_session::PeriodicSessions; + type WeightInfo = (); +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<5>; + type WeightInfo = (); +} +parameter_types! { + pub const VerificationPalletId: PalletId = PalletId(*b"verifypa"); + pub const MajorityOfAggregators: Percent = Percent::from_percent(67); +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type PalletId = VerificationPalletId; + type WeightInfo = (); + type ClusterManager = TestClusterManager; + type ClusterValidator = TestClusterValidator; + type NodeVisitor = MockNodeVisitor; + type PayoutVisitor = MockPayoutVisitor; + type AuthorityId = sr25519::AuthorityId; + type OffchainIdentifierId = crypto::OffchainIdentifierId; + type ActivityHasher = sp_runtime::traits::BlakeTwo256; + const MAJORITY: u8 = 67; + const BLOCK_TO_START: u16 = 100; + const DAC_REDUNDANCY_FACTOR: u16 = 3; + type AggregatorsQuorum = MajorityOfAggregators; + const MAX_PAYOUT_BATCH_SIZE: u16 = MAX_PAYOUT_BATCH_SIZE; + const MAX_PAYOUT_BATCH_COUNT: u16 = MAX_PAYOUT_BATCH_COUNT; + type ActivityHash = H256; + type StakingVisitor = Staking; + type AccountIdConverter = AccountId; + type CustomerVisitor = MockCustomerVisitor; + const MAX_MERKLE_NODE_IDENTIFIER: u16 = 4; +} + +pub struct MockCustomerVisitor; +impl CustomerVisitor for MockCustomerVisitor { + fn get_bucket_owner(_bucket_id: &BucketId) -> Result { + let temp: AccountId = AccountId::from([0xa; 32]); + let account_1 = T::AccountId::decode(&mut &temp.as_slice()[..]).unwrap(); + + Ok(account_1) + } +} +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + let balances = vec![ + // validator1 stash; has to be equal to the OCW key in the current implementation + (AccountId::from([0xa; 32]), 10000), + // validator1 controller + (AccountId::from([0xaa; 32]), 10000), + // validator2 stash + (AccountId::from([0xb; 32]), 10000), + // validator2 controller + (AccountId::from([0xbb; 32]), 10000), + // validator3 stash + (AccountId::from([0xc; 32]), 10000), + // validator3 controller + (AccountId::from([0xcc; 32]), 10000), + // validator4 stash + (AccountId::from([0xd; 32]), 10000), + // validator4 controller + (AccountId::from([0xdd; 32]), 10000), + // validator5 stash + (AccountId::from([0xe; 32]), 10000), + // validator5 controller + (AccountId::from([0xee; 32]), 10000), + // validator6 stash + (AccountId::from([0xf; 32]), 10000), + // validator6 controller + (AccountId::from([0xff; 32]), 10000), + ]; + let _ = pallet_balances::GenesisConfig:: { balances }.assimilate_storage(&mut storage); + + let stakers = vec![ + ( + AccountId::from([0xa; 32]), + AccountId::from([0xaa; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ( + AccountId::from([0xb; 32]), + AccountId::from([0xbb; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ( + AccountId::from([0xc; 32]), + AccountId::from([0xcc; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ( + AccountId::from([0xd; 32]), + AccountId::from([0xdd; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ( + AccountId::from([0xe; 32]), + AccountId::from([0xee; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ( + AccountId::from([0xf; 32]), + AccountId::from([0xff; 32]), + 1000, + pallet_staking::StakerStatus::Validator, + ), + ]; + let _ = + pallet_staking::GenesisConfig:: { stakers: stakers.clone(), ..Default::default() } + .assimilate_storage(&mut storage); + + sp_io::TestExternalities::new(storage) +} + +pub struct TestClusterValidator; +impl ClusterValidator for TestClusterValidator { + fn set_last_validated_era( + _cluster_id: &ClusterId, + _era_id: DdcEra, + ) -> Result<(), DispatchError> { + unimplemented!() + } +} + +pub struct MockPayoutVisitor; +impl PayoutVisitor for MockPayoutVisitor { + fn get_next_customer_batch_for_payment( + _cluster_id: &ClusterId, + _era_id: DdcEra, + ) -> Result, PayoutError> { + Ok(None) + } + + fn get_next_provider_batch_for_payment( + _cluster_id: &ClusterId, + _era_id: DdcEra, + ) -> Result, PayoutError> { + Ok(None) + } + + fn all_customer_batches_processed(_cluster_id: &ClusterId, _era_id: DdcEra) -> bool { + true + } + + fn all_provider_batches_processed(_cluster_id: &ClusterId, _era_id: DdcEra) -> bool { + true + } + + fn get_billing_report_status(_cluster_id: &ClusterId, _era_id: DdcEra) -> PayoutState { + PayoutState::NotInitialized + } + + fn begin_billing_report( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + _start_era: i64, + _end_era: i64, + ) -> DispatchResult { + Ok(()) + } + + fn begin_charging_customers( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + _max_batch_index: BatchIndex, + ) -> DispatchResult { + Ok(()) + } + + fn send_charging_customers_batch( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + _batch_index: BatchIndex, + _payers: &[(T::AccountId, BucketId, CustomerUsage)], + _batch_proof: MMRProof, + ) -> DispatchResult { + Ok(()) + } + + fn end_charging_customers( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + ) -> DispatchResult { + Ok(()) + } + + fn begin_rewarding_providers( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + _max_batch_index: BatchIndex, + _total_node_usage: NodeUsage, + ) -> DispatchResult { + Ok(()) + } + + fn send_rewarding_providers_batch( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + _batch_index: BatchIndex, + _payees: &[(T::AccountId, NodeUsage)], + _batch_proof: MMRProof, + ) -> DispatchResult { + Ok(()) + } + + fn end_rewarding_providers( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + ) -> DispatchResult { + Ok(()) + } + + fn end_billing_report( + _origin: T::AccountId, + _cluster_id: ClusterId, + _era_id: DdcEra, + ) -> DispatchResult { + Ok(()) + } +} + +pub struct MockNodeVisitor; +impl NodeVisitor for MockNodeVisitor { + fn get_total_usage(_node_pub_key: &NodePubKey) -> Result, DispatchError> { + Ok(None) // todo! add more complex mock + } + + fn get_node_params(node_pub_key: &NodePubKey) -> Result { + let key1 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a", + ))); + let key2 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e", + ))); + let key3 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "dcb83f51e6554fb3fca04807f98336d160419bf0c54f479d760b76df1e04bda2", + ))); + + let key4 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "48dbb875df3f77816cd01b5a8ce6f32944ae4ac3b4453b9345c3320689445e88", + ))); + let key5 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "302f937df3a0ec4c658e8122439e748d227442ebd493cef521a1e14943844395", + ))); + let key6 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "f2f521014e436b426e4277b23267655ae04d1858c84756d9ed970d17271d19e4", + ))); + + let key7 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72", + ))); + let key8 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "69b1897f5f7a8a775ee3a4e00f32e20bb9d30e1cdd42149ce1bd50a9aa206040", + ))); + let _key9 = + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "bf5ca1c9406094b4dea7981ba076f1520c218f18ace853300a3300c5cfe9c2af", + ))); + + let storage_node_params = if node_pub_key == &key1 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "178.251.228.236".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key2 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "95.217.8.119".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key3 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "178.251.228.42".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key4 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "37.27.30.47".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key5 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "178.251.228.49".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key6 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "159.69.207.65".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key7 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "178.251.228.165".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else if node_pub_key == &key8 { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "49.13.211.157".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + } else { + StorageNodeParams { + mode: StorageNodeMode::DAC, + host: "178.251.228.44".as_bytes().to_vec(), + domain: vec![2u8; 255], + ssl: false, + http_port: 8080u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + } + }; + + Ok(NodeParams::StorageParams(storage_node_params)) + } + + fn get_cluster_id(_node_pub_key: &NodePubKey) -> Result, DispatchError> { + unimplemented!() + } + fn exists(_node_pub_key: &NodePubKey) -> bool { + unimplemented!() + } + + fn get_node_provider_id(_node_pub_key: &NodePubKey) -> Result { + let temp: AccountId = AccountId::from([0xa; 32]); + let account_1 = T::AccountId::decode(&mut &temp.as_slice()[..]).unwrap(); + + Ok(account_1) + } +} + +pub struct TestClusterManager; +impl ClusterQuery for TestClusterManager { + fn cluster_exists(_cluster_id: &ClusterId) -> bool { + unimplemented!() + } + fn get_cluster_status(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + fn get_manager_and_reserve_id( + _cluster_id: &ClusterId, + ) -> Result<(T::AccountId, T::AccountId), DispatchError> { + unimplemented!() + } +} + +impl ClusterManager for TestClusterManager { + fn contains_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + _validation_status: Option, + ) -> bool { + unimplemented!() + } + + fn get_nodes(_cluster_id: &ClusterId) -> Result, DispatchError> { + Ok(vec![ + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "dcb83f51e6554fb3fca04807f98336d160419bf0c54f479d760b76df1e04bda2", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "48dbb875df3f77816cd01b5a8ce6f32944ae4ac3b4453b9345c3320689445e88", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "302f937df3a0ec4c658e8122439e748d227442ebd493cef521a1e14943844395", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "f2f521014e436b426e4277b23267655ae04d1858c84756d9ed970d17271d19e4", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "69b1897f5f7a8a775ee3a4e00f32e20bb9d30e1cdd42149ce1bd50a9aa206040", + ))), + NodePubKey::StoragePubKey(StorageNodePubKey::new(array_bytes::hex_n_into_unchecked( + "bf5ca1c9406094b4dea7981ba076f1520c218f18ace853300a3300c5cfe9c2af", + ))), + ]) + } + + fn add_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + _node_kind: &ClusterNodeKind, + ) -> Result<(), DispatchError> { + unimplemented!() + } + + fn remove_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result<(), DispatchError> { + unimplemented!() + } + + fn get_manager_account_id(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn get_node_state( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result>, DispatchError> { + unimplemented!() + } + + fn get_nodes_stats(_cluster_id: &ClusterId) -> Result { + unimplemented!() + } + + fn validate_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + _succeeded: bool, + ) -> Result<(), DispatchError> { + unimplemented!() + } +} + +impl frame_system::offchain::SigningTypes for Test { + type Public = ::Signer; + type Signature = Signature; +} + +impl frame_system::offchain::SendTransactionTypes for Test +where + RuntimeCall: From, +{ + type OverarchingCall = RuntimeCall; + type Extrinsic = Extrinsic; +} + +impl frame_system::offchain::CreateSignedTransaction for Test +where + RuntimeCall: From, +{ + fn create_transaction>( + call: RuntimeCall, + _public: ::Signer, + _account: AccountId, + nonce: u64, + ) -> Option<(RuntimeCall, ::SignaturePayload)> { + Some((call, (nonce, ()))) + } +} diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs new file mode 100644 index 000000000..0396a6dee --- /dev/null +++ b/pallets/ddc-verification/src/tests.rs @@ -0,0 +1,2938 @@ +use ddc_primitives::{ + AggregatorInfo, ClusterId, MergeActivityHash, StorageNodeMode, StorageNodeParams, + StorageNodePubKey, KEY_TYPE, +}; +use frame_support::{assert_noop, assert_ok}; +use sp_core::{ + offchain::{ + testing::{PendingRequest, TestOffchainExt, TestTransactionPoolExt}, + OffchainDbExt, OffchainStorage, OffchainWorkerExt, Timestamp, TransactionPoolExt, + }, + Pair, +}; +use sp_io::TestExternalities; +use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; +use sp_runtime::AccountId32; + +use crate::{mock::*, Error, NodeAggregateResponse, *}; + +#[allow(dead_code)] +fn register_validators(validators: Vec) { + ValidatorSet::::put(validators.clone()); + + for validator in validators { + assert_noop!( + DdcVerification::set_validator_key( + RuntimeOrigin::signed(validator.clone()), + validator.clone(), + ), + Error::::NotController + ); + } +} + +fn get_validators() -> Vec { + let validator1: AccountId32 = [1; 32].into(); + let validator2: AccountId32 = [2; 32].into(); + let validator3: AccountId32 = [3; 32].into(); + let validator4: AccountId32 = [4; 32].into(); + let validator5: AccountId32 = [5; 32].into(); + + vec![validator1, validator2, validator3, validator4, validator5] +} + +fn get_node_activities() -> Vec { + let aggregator = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example.com".to_vec(), + }, + }; + + let node1 = NodeAggregate { + node_id: "0".to_string(), + stored_bytes: -100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }; + let node2 = NodeAggregate { + node_id: "1".to_string(), + stored_bytes: -101, + transferred_bytes: 51, + number_of_puts: 11, + number_of_gets: 21, + aggregator: aggregator.clone(), + }; + let node3 = NodeAggregate { + node_id: "2".to_string(), + stored_bytes: 102, + transferred_bytes: 52, + number_of_puts: 12, + number_of_gets: 22, + aggregator: aggregator.clone(), + }; + let node4 = NodeAggregate { + node_id: "3".to_string(), + stored_bytes: 103, + transferred_bytes: 53, + number_of_puts: 13, + number_of_gets: 23, + aggregator: aggregator.clone(), + }; + let node5 = NodeAggregate { + node_id: "4".to_string(), + stored_bytes: 104, + transferred_bytes: 54, + number_of_puts: 14, + number_of_gets: 24, + aggregator: aggregator.clone(), + }; + vec![node1, node2, node3, node4, node5] +} + +#[test] +fn fetch_node_aggregates_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _) = TestTransactionPoolExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + ext.register_extension(TransactionPoolExt::new(pool)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host = "example.com"; + let port = 80; + let era_id = 1; + + // Create a sample NodeAggregateResponse instance + let node_activity1 = NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }; + let node_activity2 = NodeAggregateResponse { + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 510, + number_of_puts: 110, + number_of_gets: 210, + }; + let nodes_activity_json = + serde_json::to_string(&vec![node_activity1.clone(), node_activity2.clone()]).unwrap(); + + // Mock HTTP request and response + let pending_request = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId={}", host, port, era_id), + response: Some(nodes_activity_json.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(pending_request); + drop(offchain_state); + + let era_id = 1; + let cluster_id = ClusterId::from([1; 20]); + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let result = Pallet::::fetch_node_aggregates(&cluster_id, era_id, &node_params); + assert!(result.is_ok()); + let activities = result.unwrap(); + assert_eq!(activities[0].number_of_gets, node_activity1.number_of_gets); + assert_eq!(activities[0].number_of_puts, node_activity1.number_of_puts); + assert_eq!(activities[0].transferred_bytes, node_activity1.transferred_bytes); + assert_eq!(activities[0].stored_bytes, node_activity1.stored_bytes); + + assert_eq!(activities[1].number_of_gets, node_activity2.number_of_gets); + assert_eq!(activities[1].number_of_puts, node_activity2.number_of_puts); + assert_eq!(activities[1].transferred_bytes, node_activity2.transferred_bytes); + assert_eq!(activities[1].stored_bytes, node_activity2.stored_bytes); + }); +} + +#[test] +fn fetch_bucket_aggregates_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _) = TestTransactionPoolExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + ext.register_extension(TransactionPoolExt::new(pool)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host = "example.com"; + let port = 80; + let era_id = 1; + + // Create a sample NodeAggregateResponse instance + let bucket_aggregate1 = BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 111, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }; + let bucket_aggregate2 = BucketAggregateResponse { + stored_bytes: 1000, + transferred_bytes: 500, + number_of_puts: 100, + number_of_gets: 200, + bucket_id: 222, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 1000, + transferred_bytes: 500, + number_of_puts: 100, + number_of_gets: 200, + }], + }; + let customers_activity_json = + serde_json::to_string(&vec![bucket_aggregate1.clone(), bucket_aggregate2.clone()]) + .unwrap(); + + // Mock HTTP request and response + let pending_request = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId={}", host, port, era_id), + response: Some(customers_activity_json.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(pending_request); + drop(offchain_state); + + let era_id = 1; + let cluster_id = ClusterId::from([1; 20]); + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let result = Pallet::::fetch_bucket_aggregates(&cluster_id, era_id, &node_params); + assert!(result.is_ok()); + let activities = result.unwrap(); + assert_eq!( + activities[0].sub_aggregates[0].number_of_gets, + bucket_aggregate1.sub_aggregates[0].number_of_gets + ); + assert_eq!( + activities[0].sub_aggregates[0].number_of_puts, + bucket_aggregate1.sub_aggregates[0].number_of_puts + ); + assert_eq!( + activities[0].sub_aggregates[0].transferred_bytes, + bucket_aggregate1.sub_aggregates[0].transferred_bytes + ); + assert_eq!( + activities[0].sub_aggregates[0].stored_bytes, + bucket_aggregate1.sub_aggregates[0].stored_bytes + ); + + assert_eq!( + activities[1].sub_aggregates[0].number_of_gets, + bucket_aggregate2.sub_aggregates[0].number_of_gets + ); + assert_eq!( + activities[1].sub_aggregates[0].number_of_puts, + bucket_aggregate2.sub_aggregates[0].number_of_puts + ); + assert_eq!( + activities[1].sub_aggregates[0].transferred_bytes, + bucket_aggregate2.sub_aggregates[0].transferred_bytes + ); + assert_eq!( + activities[1].sub_aggregates[0].stored_bytes, + bucket_aggregate2.sub_aggregates[0].stored_bytes + ); + }); +} + +#[test] +fn buckets_sub_aggregates_in_consensus_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp2 = ( + aggregator2, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp3 = ( + aggregator3, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let groups = DdcVerification::group_buckets_sub_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + assert_eq!(groups.in_consensus.len(), 1); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + let usage = usages.first().unwrap(); + assert_eq!(usage.stored_bytes, 100); + assert_eq!(usage.transferred_bytes, 50); + assert_eq!(usage.number_of_puts, 10); + assert_eq!(usage.number_of_gets, 20); +} + +#[test] +fn buckets_sub_aggregates_in_quorum_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp2 = ( + aggregator2, + vec![BucketAggregateResponse { + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp3 = ( + aggregator3, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let groups = DdcVerification::group_buckets_sub_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + assert_eq!(groups.in_consensus.len(), 0); + assert_eq!(groups.in_quorum.len(), 1); + assert_eq!(groups.in_others.len(), 1); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + let usage = usages.first().unwrap(); + assert_eq!(usage.stored_bytes, 100); + assert_eq!(usage.transferred_bytes, 50); + assert_eq!(usage.number_of_puts, 10); + assert_eq!(usage.number_of_gets, 20); +} + +#[test] +fn buckets_sub_aggregates_in_others_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(100); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp2 = ( + aggregator2, + vec![BucketAggregateResponse { + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let resp3 = ( + aggregator3, + vec![BucketAggregateResponse { + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + bucket_id: 1, + sub_aggregates: vec![BucketSubAggregateResponse { + NodeID: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + }], + ); + + let groups = DdcVerification::group_buckets_sub_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + + assert_eq!(groups.in_consensus.len(), 0); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 2); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + + let usage1 = usages.first().unwrap(); + assert_eq!(usage1.stored_bytes, 100); + assert_eq!(usage1.transferred_bytes, 50); + assert_eq!(usage1.number_of_puts, 10); + assert_eq!(usage1.number_of_gets, 20); + + let usage2 = usages.get(1).unwrap(); + assert_eq!(usage2.stored_bytes, 200); + assert_eq!(usage2.transferred_bytes, 50); + assert_eq!(usage2.number_of_puts, 10); + assert_eq!(usage2.number_of_gets, 20); +} + +#[test] +fn nodes_aggregates_in_consensus_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp2 = ( + aggregator2, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp3 = ( + aggregator3, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let groups = DdcVerification::group_nodes_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + assert_eq!(groups.in_consensus.len(), 1); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + let usage = usages.first().unwrap(); + assert_eq!(usage.stored_bytes, 100); + assert_eq!(usage.transferred_bytes, 50); + assert_eq!(usage.number_of_puts, 10); + assert_eq!(usage.number_of_gets, 20); +} + +#[test] +fn nodes_aggregates_in_quorum_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp2 = ( + aggregator2, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp3 = ( + aggregator3, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let groups = DdcVerification::group_nodes_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + assert_eq!(groups.in_consensus.len(), 0); + assert_eq!(groups.in_quorum.len(), 1); + assert_eq!(groups.in_others.len(), 1); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + let usage = usages.first().unwrap(); + assert_eq!(usage.stored_bytes, 100); + assert_eq!(usage.transferred_bytes, 50); + assert_eq!(usage.number_of_puts, 10); + assert_eq!(usage.number_of_gets, 20); +} + +#[test] +fn nodes_aggregates_in_others_merged() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(100); + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + + let aggregator1 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example1.com".to_vec(), + }, + }; + + let aggregator2 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "95.217.8.119".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }, + }; + + let aggregator3 = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: StorageNodeParams { + ssl: false, + host: "178.251.228.42".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }, + }; + + let resp1 = ( + aggregator1, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp2 = ( + aggregator2, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 200, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let resp3 = ( + aggregator3, + vec![NodeAggregateResponse { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + }], + ); + + let groups = DdcVerification::group_nodes_aggregates_by_consistency( + &cluster_id, + era_id, + vec![resp1, resp2, resp3], + redundancy_factor, + quorum, + ); + + assert_eq!(groups.in_consensus.len(), 0); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 2); + + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + + assert!(result.is_ok()); + let usages = result.unwrap(); + + let usage1 = usages.first().unwrap(); + assert_eq!(usage1.stored_bytes, 200); + assert_eq!(usage1.transferred_bytes, 50); + assert_eq!(usage1.number_of_puts, 10); + assert_eq!(usage1.number_of_gets, 20); + + let usage2 = usages.get(1).unwrap(); + assert_eq!(usage2.stored_bytes, 100); + assert_eq!(usage2.transferred_bytes, 50); + assert_eq!(usage2.number_of_puts, 10); + assert_eq!(usage2.number_of_gets, 20); +} + +#[test] +fn buckets_sub_aggregates_grouped_by_consistency() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let host = "example1.com"; + let port = 80; + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + let aggregator = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params.clone(), + }; + + let buckets_sub_aggregates = vec![ + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + ]; + + let groups = + DdcVerification::group_by_consistency(buckets_sub_aggregates, redundancy_factor, quorum); + + assert_eq!(groups.in_consensus.len(), 1); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let agg1 = groups.in_consensus[0].get(0).unwrap(); + let agg2 = groups.in_consensus[0].get(1).unwrap(); + let agg3 = groups.in_consensus[0].get(2).unwrap(); + + assert_eq!(agg1.stored_bytes, 100); + assert_eq!(agg1.transferred_bytes, 50); + assert_eq!(agg1.number_of_puts, 10); + assert_eq!(agg1.number_of_gets, 20); + + assert_eq!(agg2.stored_bytes, 100); + assert_eq!(agg2.transferred_bytes, 50); + assert_eq!(agg2.number_of_puts, 10); + assert_eq!(agg2.number_of_gets, 20); + + assert_eq!(agg3.stored_bytes, 100); + assert_eq!(agg3.transferred_bytes, 50); + assert_eq!(agg3.number_of_puts, 10); + assert_eq!(agg3.number_of_gets, 20); +} + +#[test] +fn buckets_sub_aggregates_grouped_by_consistency_2() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + + let host = "example1.com"; + let port = 80; + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + let aggregator = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params.clone(), + }; + + let buckets_sub_aggregates = vec![ + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 1, + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 2, + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 2, + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + BucketSubAggregate { + bucket_id: 2, + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + ]; + + let groups = + DdcVerification::group_by_consistency(buckets_sub_aggregates, redundancy_factor, quorum); + + assert_eq!(groups.in_consensus.len(), 2); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let g1_agg1 = groups.in_consensus[0].get(0).unwrap(); + let g1_agg2 = groups.in_consensus[0].get(1).unwrap(); + let g1_agg3 = groups.in_consensus[0].get(2).unwrap(); + + assert_eq!(g1_agg1.bucket_id, 1); + assert_eq!(g1_agg1.stored_bytes, 100); + assert_eq!(g1_agg1.transferred_bytes, 50); + assert_eq!(g1_agg1.number_of_puts, 10); + assert_eq!(g1_agg1.number_of_gets, 20); + + assert_eq!(g1_agg2.bucket_id, 1); + assert_eq!(g1_agg2.stored_bytes, 100); + assert_eq!(g1_agg2.transferred_bytes, 50); + assert_eq!(g1_agg2.number_of_puts, 10); + assert_eq!(g1_agg2.number_of_gets, 20); + + assert_eq!(g1_agg3.bucket_id, 1); + assert_eq!(g1_agg3.stored_bytes, 100); + assert_eq!(g1_agg3.transferred_bytes, 50); + assert_eq!(g1_agg3.number_of_puts, 10); + assert_eq!(g1_agg3.number_of_gets, 20); + + let g2_agg1 = groups.in_consensus[1].get(0).unwrap(); + let g2_agg2 = groups.in_consensus[1].get(1).unwrap(); + let g2_agg3 = groups.in_consensus[1].get(2).unwrap(); + + assert_eq!(g2_agg1.bucket_id, 2); + assert_eq!(g2_agg1.stored_bytes, 110); + assert_eq!(g2_agg1.transferred_bytes, 50); + assert_eq!(g2_agg1.number_of_puts, 10); + assert_eq!(g2_agg1.number_of_gets, 20); + + assert_eq!(g2_agg2.bucket_id, 2); + assert_eq!(g2_agg2.stored_bytes, 110); + assert_eq!(g2_agg2.transferred_bytes, 50); + assert_eq!(g2_agg2.number_of_puts, 10); + assert_eq!(g2_agg2.number_of_gets, 20); + + assert_eq!(g2_agg3.bucket_id, 2); + assert_eq!(g2_agg3.stored_bytes, 110); + assert_eq!(g2_agg3.transferred_bytes, 50); + assert_eq!(g2_agg3.number_of_puts, 10); + assert_eq!(g2_agg3.number_of_gets, 20); +} + +#[test] +fn nodes_aggregates_grouped_by_consistency() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + let host = "example1.com"; + let port = 80; + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let aggregator = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params.clone(), + }; + + let nodes_aggregates = vec![ + NodeAggregate { + node_id: "0".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "0".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "0".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + ]; + + let groups = DdcVerification::group_by_consistency(nodes_aggregates, redundancy_factor, quorum); + + assert_eq!(groups.in_consensus.len(), 1); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let agg1 = groups.in_consensus[0].get(0).unwrap(); + let agg2 = groups.in_consensus[0].get(1).unwrap(); + let agg3 = groups.in_consensus[0].get(2).unwrap(); + + assert_eq!(agg1.stored_bytes, 100); + assert_eq!(agg1.transferred_bytes, 50); + assert_eq!(agg1.number_of_puts, 10); + assert_eq!(agg1.number_of_gets, 20); + + assert_eq!(agg2.stored_bytes, 100); + assert_eq!(agg2.transferred_bytes, 50); + assert_eq!(agg2.number_of_puts, 10); + assert_eq!(agg2.number_of_gets, 20); + + assert_eq!(agg3.stored_bytes, 100); + assert_eq!(agg3.transferred_bytes, 50); + assert_eq!(agg3.number_of_puts, 10); + assert_eq!(agg3.number_of_gets, 20); +} + +#[test] +fn nodes_aggregates_grouped_by_consistency_2() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + + let host = "example1.com"; + let port = 80; + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + let aggregator = AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params.clone(), + }; + + let nodes_aggregates = vec![ + NodeAggregate { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "1".to_string(), + stored_bytes: 100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + NodeAggregate { + node_id: "2".to_string(), + stored_bytes: 110, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: aggregator.clone(), + }, + ]; + + let groups = DdcVerification::group_by_consistency(nodes_aggregates, redundancy_factor, quorum); + + assert_eq!(groups.in_consensus.len(), 2); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); + + let g1_agg1 = groups.in_consensus[0].get(0).unwrap(); + let g1_agg2 = groups.in_consensus[0].get(1).unwrap(); + let g1_agg3 = groups.in_consensus[0].get(2).unwrap(); + + assert_eq!(g1_agg1.node_id, "2".to_string()); + assert_eq!(g1_agg1.stored_bytes, 110); + assert_eq!(g1_agg1.transferred_bytes, 50); + assert_eq!(g1_agg1.number_of_puts, 10); + assert_eq!(g1_agg1.number_of_gets, 20); + + assert_eq!(g1_agg2.node_id, "2".to_string()); + assert_eq!(g1_agg2.stored_bytes, 110); + assert_eq!(g1_agg2.transferred_bytes, 50); + assert_eq!(g1_agg2.number_of_puts, 10); + assert_eq!(g1_agg2.number_of_gets, 20); + + assert_eq!(g1_agg3.node_id, "2".to_string()); + assert_eq!(g1_agg3.stored_bytes, 110); + assert_eq!(g1_agg3.transferred_bytes, 50); + assert_eq!(g1_agg3.number_of_puts, 10); + assert_eq!(g1_agg3.number_of_gets, 20); + + let g2_agg1 = groups.in_consensus[1].get(0).unwrap(); + let g2_agg2 = groups.in_consensus[1].get(1).unwrap(); + let g2_agg3 = groups.in_consensus[1].get(2).unwrap(); + + assert_eq!(g2_agg1.node_id, "1".to_string()); + assert_eq!(g2_agg1.stored_bytes, 100); + assert_eq!(g2_agg1.transferred_bytes, 50); + assert_eq!(g2_agg1.number_of_puts, 10); + assert_eq!(g2_agg1.number_of_gets, 20); + + assert_eq!(g2_agg2.node_id, "1".to_string()); + assert_eq!(g2_agg2.stored_bytes, 100); + assert_eq!(g2_agg2.transferred_bytes, 50); + assert_eq!(g2_agg2.number_of_puts, 10); + assert_eq!(g2_agg2.number_of_gets, 20); + + assert_eq!(g2_agg3.node_id, "1".to_string()); + assert_eq!(g2_agg3.stored_bytes, 100); + assert_eq!(g2_agg3.transferred_bytes, 50); + assert_eq!(g2_agg3.number_of_puts, 10); + assert_eq!(g2_agg3.number_of_gets, 20); +} + +#[test] +fn empty_bucket_sub_aggregates() { + let redundancy_factor = 3; + let quorum = Percent::from_percent(67); + + let empty = Vec::::new(); + let groups = DdcVerification::group_by_consistency(empty, redundancy_factor, quorum); + + assert_eq!(groups.in_consensus.len(), 0); + assert_eq!(groups.in_quorum.len(), 0); + assert_eq!(groups.in_others.len(), 0); +} + +#[test] +fn bucket_sub_aggregates_are_fetched_and_grouped() { + let mut ext = new_test_ext(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _pool_state) = TestTransactionPoolExt::new(); + + let (pair, _seed) = sp_core::sr25519::Pair::from_phrase( + "spider sell nice animal border success square soda stem charge caution echo", + None, + ) + .unwrap(); + let keystore = MemoryKeystore::new(); + keystore + .insert( + KEY_TYPE, + "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318", + pair.public().as_ref(), + ) + .unwrap(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt::new(keystore)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + offchain_state.persistent_storage.set( + b"", + &key, + b"9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a".as_ref(), + ); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host1 = "178.251.228.236"; + let host2 = "95.217.8.119"; + let host3 = "178.251.228.42"; + let host4 = "37.27.30.47"; + let host5 = "178.251.228.49"; + + let port = 8080; + + let pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476817", host1, port), + response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":505,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476817", host2, port), + response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":506,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476817", host3, port), + response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":505,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476817", host4, port), + response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request5 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476817", host5, port), + response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa320","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + offchain_state.expect_request(pending_request1); + offchain_state.expect_request(pending_request2); + offchain_state.expect_request(pending_request3); + offchain_state.expect_request(pending_request4); + offchain_state.expect_request(pending_request5); + + drop(offchain_state); + + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + let redundancy_factor = 3; + let aggregators_quorum = Percent::from_percent(67); + + let node_params1 = StorageNodeParams { + ssl: false, + host: host1.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let node_params2 = StorageNodeParams { + ssl: false, + host: host2.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }; + + let node_params3 = StorageNodeParams { + ssl: false, + host: host3.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example4.com".to_vec(), + }; + + let node_params4 = StorageNodeParams { + ssl: false, + host: host4.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example5.com".to_vec(), + }; + + let node_params5 = StorageNodeParams { + ssl: false, + host: host5.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example6.com".to_vec(), + }; + + let dac_nodes: Vec<(NodePubKey, StorageNodeParams)> = vec![ + (NodePubKey::StoragePubKey(StorageNodePubKey::new([1; 32])), node_params1.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])), node_params2.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), node_params3.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([4; 32])), node_params4.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), node_params5.clone()), + ]; + + let bucket_aggregates_by_aggregator = + DdcVerification::fetch_buckets_aggregates_for_era(&cluster_id, era_id, &dac_nodes) + .unwrap(); + + let groups = + DdcVerification::group_buckets_sub_aggregates_by_consistency(&cluster_id, era_id, bucket_aggregates_by_aggregator, redundancy_factor, aggregators_quorum); + + + // Sub aggregates which are in consensus + let bucket_sub_aggregate_in_consensus = BucketSubAggregate { + bucket_id: 90235, + node_id: "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318" + .to_string(), + stored_bytes: 578, + transferred_bytes: 578, + number_of_puts: 2, + number_of_gets: 0, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params1.clone(), + }, + }; + + assert_eq!( + groups.in_consensus, + vec![ + ConsistentGroup(bucket_sub_aggregate_in_consensus.hash::(), vec![ + bucket_sub_aggregate_in_consensus.clone(), + BucketSubAggregate { + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: node_params2.clone(), + }, + ..bucket_sub_aggregate_in_consensus.clone() + }, + BucketSubAggregate { + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: node_params3.clone(), + }, + ..bucket_sub_aggregate_in_consensus.clone() + }, + ]) + ] + ); + + // Sub aggregates which are in quorum + let bucket_sub_aggregate_in_quorum = BucketSubAggregate { + bucket_id: 90235, + node_id: "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319" + .to_string(), + stored_bytes: 0, + transferred_bytes: 505, + number_of_puts: 0, + number_of_gets: 1, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params1.clone(), + }, + }; + + assert_eq!( + groups.in_quorum, + vec![ + ConsistentGroup(bucket_sub_aggregate_in_quorum.hash::(), vec![bucket_sub_aggregate_in_quorum.clone(), BucketSubAggregate {aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), + node_params: node_params3.clone(), + }, + ..bucket_sub_aggregate_in_quorum.clone() + }, + ]), + ] + ); + + // Others sub aggregates + let bucket_sub_aggregate1_in_others = BucketSubAggregate { + bucket_id: 90235, + node_id: "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319" + .to_string(), + stored_bytes: 0, + transferred_bytes: 506, + number_of_puts: 0, + number_of_gets: 1, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: node_params2.clone(), + }, + }; + + let bucket_sub_aggregate2_in_others = BucketSubAggregate { + bucket_id: 90235, + node_id: "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa320" + .to_string(), + stored_bytes: 578, + transferred_bytes: 578, + number_of_puts: 2, + number_of_gets: 0, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([5; 32])), + node_params: node_params5.clone(), + }, + }; + + assert_eq!( + groups.in_others, + vec![ + ConsistentGroup(bucket_sub_aggregate2_in_others.hash::(), vec![bucket_sub_aggregate2_in_others]), + ConsistentGroup(bucket_sub_aggregate1_in_others.hash::(), vec![bucket_sub_aggregate1_in_others]), + ] + ); + }); +} + +#[test] +fn node_aggregates_are_fetched_and_grouped() { + let mut ext = new_test_ext(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _pool_state) = TestTransactionPoolExt::new(); + + let (pair, _seed) = sp_core::sr25519::Pair::from_phrase( + "spider sell nice animal border success square soda stem charge caution echo", + None, + ) + .unwrap(); + let keystore = MemoryKeystore::new(); + keystore + .insert( + KEY_TYPE, + "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318", + pair.public().as_ref(), + ) + .unwrap(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt::new(keystore)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + offchain_state.persistent_storage.set( + b"", + &key, + b"9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a".as_ref(), + ); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host1 = "178.251.228.236"; + let host2 = "95.217.8.119"; + let host3 = "178.251.228.42"; + let host4 = "37.27.30.47"; + let host5 = "178.251.228.49"; + + let port = 8080; + + let pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476817", host1, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476817", host2, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 48,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476817", host3, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476817", host4, port), + response: Some(br#"[{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request5 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476817", host5, port), + response: Some(br#"[{"node_id": "0xfc28d5f5bb10212077a8654f62c4f8f0b5ab985fc322a51f5a3c75943b29194b","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + offchain_state.expect_request(pending_request1); + offchain_state.expect_request(pending_request2); + offchain_state.expect_request(pending_request3); + offchain_state.expect_request(pending_request4); + offchain_state.expect_request(pending_request5); + + drop(offchain_state); + + let cluster_id = ClusterId::from([1; 20]); + let era_id = 476817; + let redundancy_factor = 3; + let aggregators_quorum = Percent::from_percent(67); + + let node_params1 = StorageNodeParams { + ssl: false, + host: host1.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let node_params2 = StorageNodeParams { + ssl: false, + host: host2.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }; + + let node_params3 = StorageNodeParams { + ssl: false, + host: host3.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example4.com".to_vec(), + }; + + let node_params4 = StorageNodeParams { + ssl: false, + host: host4.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example5.com".to_vec(), + }; + + let node_params5 = StorageNodeParams { + ssl: false, + host: host5.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example6.com".to_vec(), + }; + + let dac_nodes: Vec<(NodePubKey, StorageNodeParams)> = vec![ + (NodePubKey::StoragePubKey(StorageNodePubKey::new([1; 32])), node_params1.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])), node_params2.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), node_params3.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([4; 32])), node_params4.clone()), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([5; 32])), node_params5.clone()), + ]; + + let aggregates_by_aggregator = + DdcVerification::fetch_nodes_aggregates_for_era(&cluster_id, era_id, &dac_nodes) + .unwrap(); + + let groups = + DdcVerification::group_nodes_aggregates_by_consistency(&cluster_id, era_id, aggregates_by_aggregator, redundancy_factor, aggregators_quorum); + // Node aggregates which are in consensus + let node_aggregate_in_consensus = NodeAggregate { + node_id: "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e" + .to_string(), + stored_bytes: 675613289, + transferred_bytes: 1097091579, + number_of_puts: 889, + number_of_gets: 97, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params1.clone(), + }, + }; + + assert_eq!( + groups.in_consensus, + vec![ConsistentGroup(node_aggregate_in_consensus.hash::(), vec![node_aggregate_in_consensus.clone(), NodeAggregate { aggregator: AggregatorInfo { node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), node_params: node_params2.clone(), }, ..node_aggregate_in_consensus.clone() }, NodeAggregate { aggregator: AggregatorInfo { node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([3; 32])), node_params: node_params3.clone(), }, ..node_aggregate_in_consensus.clone() } ])] + ); + + // Node aggregates which are in quorum + let node_aggregate_in_quorum = NodeAggregate { + node_id: "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a" + .to_string(), + stored_bytes: 0, + transferred_bytes: 38, + number_of_puts: 0, + number_of_gets: 1, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params1.clone(), + }, + }; + + assert_eq!( + groups.in_quorum, vec![ConsistentGroup(node_aggregate_in_quorum.hash::(), vec![node_aggregate_in_quorum.clone(), NodeAggregate {aggregator: AggregatorInfo { node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([4; 32])), node_params: node_params4.clone(), }, ..node_aggregate_in_quorum.clone() }])] + ); + + // Others nodes aggregates + let node_aggregate1_in_others = NodeAggregate { + node_id: "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a" + .to_string(), + stored_bytes: 0, + transferred_bytes: 48, + number_of_puts: 0, + number_of_gets: 1, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([2; 32])), + node_params: node_params2.clone(), + }, + }; + + let node_aggregate2_in_others = NodeAggregate { + node_id: "0xfc28d5f5bb10212077a8654f62c4f8f0b5ab985fc322a51f5a3c75943b29194b" + .to_string(), + stored_bytes: 675613289, + transferred_bytes: 1097091579, + number_of_puts: 889, + number_of_gets: 97, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([5; 32])), + node_params: node_params5.clone(), + }, + }; + + assert_eq!( + groups.in_others, vec![ConsistentGroup(node_aggregate2_in_others.hash::(), vec![node_aggregate2_in_others]), ConsistentGroup(node_aggregate1_in_others.hash::(), vec![node_aggregate1_in_others])] + ); + }); +} + +#[test] +fn test_convert_to_batch_merkle_roots() { + let nodes = get_node_activities(); + let activities_batch_1 = vec![nodes[0].clone(), nodes[1].clone(), nodes[2].clone()]; + let activities_batch_2 = vec![nodes[3].clone(), nodes[4].clone()]; + let cluster_id = ClusterId::default(); + let era_id_1 = 1; + + let result_roots = DdcVerification::convert_to_batch_merkle_roots( + &cluster_id, + era_id_1, + vec![activities_batch_1.clone(), activities_batch_2.clone()], + ) + .unwrap(); + let expected_roots: Vec = vec![ + DdcVerification::create_merkle_root( + &cluster_id, + era_id_1, + &activities_batch_1.iter().map(|a| a.hash::()).collect::>(), + ) + .unwrap(), + DdcVerification::create_merkle_root( + &cluster_id, + era_id_1, + &activities_batch_2.iter().map(|a| a.hash::()).collect::>(), + ) + .unwrap(), + ]; + + assert_eq!(result_roots, expected_roots); +} + +#[test] +fn test_convert_to_batch_merkle_roots_empty() { + let cluster_id = ClusterId::default(); + let era_id_1 = 1; + let result_roots = DdcVerification::convert_to_batch_merkle_roots( + &cluster_id, + era_id_1, + Vec::>::new(), + ) + .unwrap(); + let expected_roots: Vec = Vec::::new(); + + assert_eq!(result_roots, expected_roots); +} + +#[test] +fn test_split_to_batches_empty_activities() { + let activities: Vec = vec![]; + let result = DdcVerification::split_to_batches(&activities, 3); + assert_eq!(result, Vec::>::new()); +} + +#[test] +fn test_split_to_batches_single_batch() { + let nodes = get_node_activities(); + let activities = vec![nodes[0].clone(), nodes[1].clone(), nodes[2].clone()]; + let mut sorted_activities = vec![nodes[0].clone(), nodes[1].clone(), nodes[2].clone()]; + + sorted_activities.sort(); + let result = DdcVerification::split_to_batches(&activities, 5); + assert_eq!(result, vec![sorted_activities]); +} + +#[test] +fn test_split_to_batches_exact_batches() { + let nodes = get_node_activities(); + let activities = vec![nodes[0].clone(), nodes[1].clone(), nodes[2].clone(), nodes[3].clone()]; + let mut sorted_activities = + vec![nodes[0].clone(), nodes[1].clone(), nodes[2].clone(), nodes[3].clone()]; + sorted_activities.sort(); + let result = DdcVerification::split_to_batches(&activities, 2); + assert_eq!( + result, + vec![ + [sorted_activities[0].clone(), sorted_activities[1].clone()], + [sorted_activities[2].clone(), sorted_activities[3].clone()] + ] + ); +} +#[test] +#[allow(clippy::vec_init_then_push)] +fn test_split_to_batches_non_exact_batches() { + let nodes = get_node_activities(); + let activities = vec![ + nodes[0].clone(), + nodes[1].clone(), + nodes[2].clone(), + nodes[3].clone(), + nodes[4].clone(), + ]; + let mut sorted_activities = vec![ + nodes[0].clone(), + nodes[1].clone(), + nodes[2].clone(), + nodes[3].clone(), + nodes[4].clone(), + ]; + sorted_activities.sort(); + let result = DdcVerification::split_to_batches(&activities, 2); + let mut expected: Vec> = Vec::new(); + expected.push(vec![sorted_activities[0].clone(), sorted_activities[1].clone()]); + expected.push(vec![sorted_activities[2].clone(), sorted_activities[3].clone()]); + expected.push(vec![sorted_activities[4].clone()]); + + assert_eq!(result, expected); +} + +#[test] +fn fetch_processed_era_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _) = TestTransactionPoolExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + ext.register_extension(TransactionPoolExt::new(pool)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host = "example1.com"; + let port = 80; + + // Create a sample EraActivity instance + let era_activity1 = EraActivity { id: 17, start: 1, end: 2 }; + let era_activity2 = EraActivity { id: 18, start: 1, end: 2 }; + let era_activity3 = EraActivity { id: 19, start: 1, end: 2 }; + let era_activity_json = serde_json::to_string(&vec![ + era_activity1.clone(), + era_activity2.clone(), + era_activity3, + ]) + .unwrap(); + + // Mock HTTP request and response + let pending_request = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host, port), + response: Some(era_activity_json.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(pending_request); + drop(offchain_state); + + let node_params = StorageNodeParams { + ssl: false, + host: host.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let result = Pallet::::fetch_processed_era(&node_params); + assert!(result.is_ok()); + let activities = result.unwrap(); + assert_eq!(activities[0].id, era_activity1.id); + assert_eq!(activities[1].id, era_activity2.id); + }); +} + +#[test] +fn get_era_for_validation_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _) = TestTransactionPoolExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain.clone()))); + ext.register_extension(TransactionPoolExt::new(pool)); + + ext.execute_with(|| { + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + + let mut offchain_state = offchain_state.write(); + offchain_state.persistent_storage.set( + b"", + &key, + b"9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a".as_ref(), + ); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host1 = "example1.com"; + let host2 = "example2.com"; + let host3 = "example3.com"; + let host4 = "example4.com"; + let port = 80; + let era_activity1 = EraActivity { id: 16, start: 1, end: 2 }; + let era_activity2 = EraActivity { id: 17, start: 1, end: 2 }; + let era_activity3 = EraActivity { id: 18, start: 1, end: 2 }; + let era_activity4 = EraActivity { id: 19, start: 1, end: 2 }; + let era_activity_json1 = serde_json::to_string(&vec![ + era_activity1.clone(), //16 + era_activity2.clone(), //17 + era_activity3.clone(), //18 + era_activity4.clone(), //19 + ]) + .unwrap(); + let era_activity_json2 = serde_json::to_string(&vec![ + era_activity1.clone(), //16 + era_activity2.clone(), //17 + era_activity3.clone(), //18 + ]) + .unwrap(); + let era_activity_json3 = serde_json::to_string(&vec![ + era_activity1.clone(), //16 + era_activity2.clone(), //17 + era_activity3.clone(), //18 + ]) + .unwrap(); + let era_activity_json4 = serde_json::to_string(&vec![ + era_activity1.clone(), //16 + era_activity2.clone(), //17 + era_activity3.clone(), //18 + ]) + .unwrap(); + let pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host1, port), + response: Some(era_activity_json1.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + let pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host2, port), + response: Some(era_activity_json2.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + let pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host3, port), + response: Some(era_activity_json3.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + let pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host4, port), + response: Some(era_activity_json4.as_bytes().to_vec()), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(pending_request1); + offchain_state.expect_request(pending_request2); + offchain_state.expect_request(pending_request3); + offchain_state.expect_request(pending_request4); + + drop(offchain_state); + + let node_params1 = StorageNodeParams { + ssl: false, + host: host1.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let node_params2 = StorageNodeParams { + ssl: false, + host: host2.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example3.com".to_vec(), + }; + + let node_params3 = StorageNodeParams { + ssl: false, + host: host3.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example4.com".to_vec(), + }; + + let node_params4 = StorageNodeParams { + ssl: false, + host: host4.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example5.com".to_vec(), + }; + + let dac_nodes: Vec<(NodePubKey, StorageNodeParams)> = vec![ + (NodePubKey::StoragePubKey(StorageNodePubKey::new([1; 32])), node_params1), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])), node_params2), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), node_params3), + (NodePubKey::StoragePubKey(StorageNodePubKey::new([4; 32])), node_params4), + ]; + + let cluster_id = ClusterId::from([12; 20]); + let result = Pallet::::get_era_for_validation(&cluster_id, &dac_nodes); + assert_eq!(result.unwrap().unwrap(), era_activity1); //16 + }); +} + +#[test] +fn test_get_last_validated_era() { + let cluster_id1 = ClusterId::from([12; 20]); + let cluster_id2 = ClusterId::from([13; 20]); + let era_1 = 1; + let era_2 = 2; + let payers_root: ActivityHash = [1; 32]; + let payees_root: ActivityHash = [2; 32]; + let validators = get_validators(); + + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::get_last_validated_era(&cluster_id1, validators[0].clone()) + .map(|era| { + assert_eq!(era, None); + })); + + let mut validators_map_1 = BTreeMap::new(); + validators_map_1.insert( + (payers_root, payees_root), + vec![validators[1].clone(), validators[2].clone(), validators[3].clone()], + ); + + let validation_1 = EraValidation { + validators: validators_map_1, + start_era: 1, + end_era: 2, + payers_merkle_root_hash: payers_root, + payees_merkle_root_hash: payees_root, + status: EraValidationStatus::ValidatingData, + }; + + >::insert(cluster_id1, era_1, validation_1); + + // still no - different accountid + assert_ok!(Pallet::::get_last_validated_era(&cluster_id1, validators[0].clone()) + .map(|era| { + assert_eq!(era, None); + })); + + // still no - different cluster id + assert_ok!(Pallet::::get_last_validated_era(&cluster_id2, validators[1].clone()) + .map(|era| { + assert_eq!(era, None); + })); + + let mut validators_map_2 = BTreeMap::new(); + validators_map_2 + .insert((payers_root, payees_root), vec![validators[2].clone(), validators[3].clone()]); + + let validation_2 = EraValidation { + validators: validators_map_2, + start_era: 1, + end_era: 2, + payers_merkle_root_hash: payers_root, + payees_merkle_root_hash: payees_root, + status: EraValidationStatus::ValidatingData, + }; + + >::insert(cluster_id1, era_2, validation_2); + + // Now the last validated era should be ERA_2 + assert_ok!(Pallet::::get_last_validated_era(&cluster_id1, validators[2].clone()) + .map(|era| { + assert_eq!(era, Some(era_2)); + })); + + assert_ok!(Pallet::::get_last_validated_era(&cluster_id1, validators[1].clone()) + .map(|era| { + assert_eq!(era, Some(era_1)); + })); + }); +} + +#[test] +fn test_get_era_for_payout() { + // Initialize test data + let cluster_id = ClusterId::default(); // Replace with actual initialization + let status = EraValidationStatus::ReadyForPayout; // Test with different statuses + + // Insert some era validations into storage + let era_id_1 = 1; + let era_id_2 = 2; + let era_validation_1 = EraValidation:: { + validators: Default::default(), + start_era: 0, + end_era: 0, + payers_merkle_root_hash: Default::default(), + payees_merkle_root_hash: Default::default(), + status: EraValidationStatus::ReadyForPayout, + }; + let era_validation_2 = EraValidation:: { + validators: Default::default(), + start_era: 0, + end_era: 0, + payers_merkle_root_hash: Default::default(), + payees_merkle_root_hash: Default::default(), + status: EraValidationStatus::PayoutInProgress, + }; + + new_test_ext().execute_with(|| { + EraValidations::::insert(cluster_id, era_id_1, &era_validation_1); + EraValidations::::insert(cluster_id, era_id_2, &era_validation_2); + + let mut result = Pallet::::get_era_for_payout(&cluster_id, status); + assert_eq!(result, Some((era_id_1, 0, 0))); + + result = + Pallet::::get_era_for_payout(&cluster_id, EraValidationStatus::PayoutSuccess); + assert_eq!(result, None); + }); +} + +#[test] +fn create_merkle_root_works() { + new_test_ext().execute_with(|| { + let a: ActivityHash = [0; 32]; + let b: ActivityHash = [1; 32]; + let c: ActivityHash = [2; 32]; + let d: ActivityHash = [3; 32]; + let e: ActivityHash = [4; 32]; + let cluster_id = ClusterId::default(); + let era_id_1 = 1; + + let leaves = vec![a, b, c, d, e]; + + let root = DdcVerification::create_merkle_root(&cluster_id, era_id_1, &leaves).unwrap(); + + assert_eq!( + root, + [ + 205, 34, 92, 22, 66, 39, 53, 146, 126, 111, 191, 174, 107, 224, 161, 127, 150, 69, + 255, 15, 237, 252, 116, 39, 186, 26, 40, 154, 180, 110, 185, 7 + ] + ); + }); +} + +#[test] +fn create_merkle_root_empty() { + new_test_ext().execute_with(|| { + let cluster_id = ClusterId::default(); + let era_id_1 = 1; + let leaves = Vec::::new(); + let root = DdcVerification::create_merkle_root(&cluster_id, era_id_1, &leaves).unwrap(); + + assert_eq!(root, ActivityHash::default()); + }); +} + +#[test] +fn proof_merkle_leaf_works() { + new_test_ext().execute_with(|| { + let a: ActivityHash = [0; 32]; + let b: ActivityHash = [1; 32]; + let c: ActivityHash = [2; 32]; + let d: ActivityHash = [3; 32]; + let e: ActivityHash = [4; 32]; + let f: ActivityHash = [5; 32]; + + let leaves = [a, b, c, d, e]; + let store = MemStore::default(); + let mut mmr: MMR> = + MemMMR::<_, MergeActivityHash>::new(0, &store); + let leaf_position_map: Vec<(ActivityHash, u64)> = + leaves.iter().map(|a| (*a, mmr.push(*a).unwrap())).collect(); + + let leaf_position: Vec<(u64, ActivityHash)> = leaf_position_map + .iter() + .filter(|&(l, _)| l == &c) + .map(|&(ref l, p)| (p, *l)) + .collect(); + let position: Vec = leaf_position.clone().into_iter().map(|(p, _)| p).collect(); + let root = mmr.get_root().unwrap(); + + assert_eq!(leaf_position.len(), 1); + assert_eq!(position.len(), 1); + assert!(DdcVerification::proof_merkle_leaf( + root, + &MMRProof { + mmr_size: mmr.mmr_size(), + proof: mmr.gen_proof(position.clone()).unwrap().proof_items().to_vec(), + leaf_with_position: leaf_position[0] + } + ) + .unwrap()); + + assert_noop!( + DdcVerification::proof_merkle_leaf( + root, + &MMRProof { + mmr_size: 0, + proof: mmr.gen_proof(position).unwrap().proof_items().to_vec(), + leaf_with_position: (6, f) + } + ), + Error::::FailToVerifyMerkleProof + ); + }); +} + +#[test] +fn test_single_ocw_pallet_integration() { + let mut ext = new_test_ext(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _pool_state) = TestTransactionPoolExt::new(); + + let (pair, _seed) = sp_core::sr25519::Pair::from_phrase( + "spider sell nice animal border success square soda stem charge caution echo", + None, + ) + .unwrap(); + let keystore = MemoryKeystore::new(); + keystore + .insert( + KEY_TYPE, + "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318", + pair.public().as_ref(), + ) + .unwrap(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt::new(keystore)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + offchain_state.persistent_storage.set( + b"", + &key, + b"9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a".as_ref(), + ); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host1 = "178.251.228.236"; + let host2 = "95.217.8.119"; + let host3 = "178.251.228.42"; + let host4 = "37.27.30.47"; + let host5 = "178.251.228.49"; + let host6 = "159.69.207.65"; + let host7 = "178.251.228.165"; + let host8 = "49.13.211.157"; + let host9 = "178.251.228.44"; + let port = 8080; + + let pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host1, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host2, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host3, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host4, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request5 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host5, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request6 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host6, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request7 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host7, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request8 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host8, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + let pending_request9 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/eras", host9, port), + response: Some(br#"[{"id":476814,"start":0,"end":1716533999999,"processing_time_ms":0,"total_records":0,"total_buckets":0},{"id":476815,"start":1716534000000,"end":1716537599999,"processing_time_ms":2,"total_records":54,"total_buckets":2},{"id":476816,"start":1716537600000,"end":1716541199999,"processing_time_ms":10,"total_records":803,"total_buckets":29},{"id":476817,"start":1716541200000,"end":1716544799999,"processing_time_ms":11,"total_records":986,"total_buckets":28}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + + let node_pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host1, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host2, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host3, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host4, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request5 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host5, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request6 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host6, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request7 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host7, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request8 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host8, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let node_pending_request9 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/nodes?eraId=476814", host9, port), + response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host1, port), + response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host2, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request3 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host3, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request4 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host4, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request5 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host5, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request6 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host6, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request7 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host7, port), + response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request8 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host8, port), + response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + let bucket_pending_request9 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets?eraId=476814", host9, port), + response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + offchain_state.expect_request(pending_request1); + offchain_state.expect_request(pending_request2); + offchain_state.expect_request(pending_request3); + offchain_state.expect_request(pending_request4); + offchain_state.expect_request(pending_request5); + offchain_state.expect_request(pending_request6); + offchain_state.expect_request(pending_request7); + offchain_state.expect_request(pending_request8); + offchain_state.expect_request(pending_request9); + offchain_state.expect_request(node_pending_request1); + offchain_state.expect_request(node_pending_request2); + offchain_state.expect_request(node_pending_request3); + offchain_state.expect_request(node_pending_request4); + offchain_state.expect_request(node_pending_request5); + offchain_state.expect_request(node_pending_request6); + offchain_state.expect_request(node_pending_request7); + offchain_state.expect_request(node_pending_request8); + offchain_state.expect_request(node_pending_request9); + offchain_state.expect_request(bucket_pending_request1); + offchain_state.expect_request(bucket_pending_request2); + offchain_state.expect_request(bucket_pending_request3); + offchain_state.expect_request(bucket_pending_request4); + offchain_state.expect_request(bucket_pending_request5); + offchain_state.expect_request(bucket_pending_request6); + offchain_state.expect_request(bucket_pending_request7); + offchain_state.expect_request(bucket_pending_request8); + offchain_state.expect_request(bucket_pending_request9); + drop(offchain_state); + + // // Offchain worker should be triggered if block number is divided by 100 + let block = 500; + System::set_block_number(block); + let cluster_id = ClusterId::from([12; 20]); + + ClusterToValidate::::put(cluster_id); + DdcVerification::offchain_worker(block); + }); +} + +#[test] +fn fetch_reward_activities_works() { + let cluster_id = ClusterId::from([12; 20]); + let a: ActivityHash = [0; 32]; + let b: ActivityHash = [1; 32]; + let c: ActivityHash = [2; 32]; + let d: ActivityHash = [3; 32]; + let e: ActivityHash = [4; 32]; + + let leaves = [a, b, c, d, e]; + let era_id = 1; + let total_usage: i64 = 56; + + let node_params = StorageNodeParams { + ssl: false, + host: "178.251.228.236".as_bytes().to_vec(), + http_port: 8080, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example.com".to_vec(), + }; + + let result = DdcVerification::fetch_reward_activities( + &cluster_id, + era_id, + vec![ + NodeAggregate { + node_id: "0".to_string(), + stored_bytes: -100, + transferred_bytes: 50, + number_of_puts: 10, + number_of_gets: 20, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params.clone(), + }, + }, + NodeAggregate { + node_id: "1".to_string(), + stored_bytes: -101, + transferred_bytes: 51, + number_of_puts: 11, + number_of_gets: 21, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params.clone(), + }, + }, + NodeAggregate { + node_id: "2".to_string(), + stored_bytes: 102, + transferred_bytes: 52, + number_of_puts: 12, + number_of_gets: 22, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params.clone(), + }, + }, + NodeAggregate { + node_id: "3".to_string(), + stored_bytes: 103, + transferred_bytes: 53, + number_of_puts: 13, + number_of_gets: 23, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params.clone(), + }, + }, + NodeAggregate { + node_id: "4".to_string(), + stored_bytes: 104, + transferred_bytes: 54, + number_of_puts: 14, + number_of_gets: 24, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([1; 32])), + node_params: node_params.clone(), + }, + }, + ], + leaves.to_vec(), + total_usage, + ); + + let usage = get_node_activities().iter().fold( + NodeUsage { transferred_bytes: 0, stored_bytes: 0, number_of_puts: 0, number_of_gets: 0 }, + |mut acc, activity| { + acc.transferred_bytes += activity.transferred_bytes; + acc.stored_bytes += activity.stored_bytes; + acc.number_of_puts += activity.number_of_puts; + acc.number_of_gets += activity.number_of_gets; + acc + }, + ); + + let ex_result = NodeUsage { + stored_bytes: total_usage + usage.stored_bytes, + number_of_puts: usage.number_of_puts, + number_of_gets: usage.number_of_gets, + transferred_bytes: usage.transferred_bytes, + }; + + assert_eq!(result.unwrap(), Some((era_id, (leaves.len() - 1) as u16, ex_result))); +} + +#[test] +fn test_find_random_merkle_node_ids() { + let mut ext = TestExternalities::default(); + let (offchain, _offchain_state) = TestOffchainExt::new(); + let (pool, _) = TestTransactionPoolExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + ext.register_extension(TransactionPoolExt::new(pool)); + let host1 = "178.251.228.236"; + + let port = 8080; + let node_params1 = StorageNodeParams { + ssl: false, + host: host1.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + ext.execute_with(|| { + let deffective_bucket_sub_aggregate = BucketSubAggregate { + bucket_id: 90235, + node_id: "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319" + .to_string(), + stored_bytes: 0, + transferred_bytes: 505, + number_of_puts: 12, + number_of_gets: 13, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params1.clone(), + }, + }; + + let number_of_leaves = deffective_bucket_sub_aggregate.get_number_of_leaves(); + + let ids = DdcVerification::_find_random_merkle_node_ids( + 3, + number_of_leaves, + deffective_bucket_sub_aggregate.get_key(), + ); + + for id in ids { + assert!(id < number_of_leaves); + } + }); +} + +#[test] +fn challenge_bucket_sub_aggregate_works() { + let mut ext = new_test_ext(); + let (offchain, offchain_state) = TestOffchainExt::new(); + let (pool, _pool_state) = TestTransactionPoolExt::new(); + + let (pair, _seed) = sp_core::sr25519::Pair::from_phrase( + "spider sell nice animal border success square soda stem charge caution echo", + None, + ) + .unwrap(); + let keystore = MemoryKeystore::new(); + keystore + .insert( + KEY_TYPE, + "0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318", + pair.public().as_ref(), + ) + .unwrap(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt::new(keystore)); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes(); + offchain_state.persistent_storage.set( + b"", + &key, + b"9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a".as_ref(), + ); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + let host1 = "178.251.228.165"; + + let port = 8080; + + //todo! put them in resource file + let pending_request1 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets/123229/challenge?eraId=5757773&nodeId=0x1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72&merkleTreeNodeId=0,2,1,3", host1, port), + response: Some(br#"{"proofs":[{"merkle_tree_node_id":3,"usage":{"stored_bytes":2097152,"transferred_bytes":1048576,"number_of_puts":1,"number_of_gets":1},"path":["hFnZfjnS5bAzgm5tHcWTxuJa5waDcaiU7OhBRofylhQ="],"leafs":[{"record":{"id":"17Z3vSjjRm6mWN3Swpw3Cw==","upstream":{"request":{"requestId":"e9920157-6c6a-485e-9f5a-1685ea6d4ef5","requestType":"REQUEST_TYPE_GET","contentType":"CONTENT_TYPE_PIECE","bucketId":"1","pieceCid":"AQIeIKLbs3OibO5qbLJ/PLCo1m02oFHWCl4s7S59GWgxDUbk","offset":"0","size":"0","timestamp":"1727346880632","signature":{"algorithm":"ED_25519","signer":"iNw0F9UFjsS0UD4MEuoaCom+IA/piSJCPUM0AU+msO4=","value":"KPDnQH5KZZQ2hksJ8F/w3GHwWloAm1QKoLt+SuUNYt3HxsGrh3r3q77COiu0jrwQ7mEsp/FFJp4pDp2Y1j2sDA=="}}},"downstream":[{"request":{"requestId":"a5bcaa37-97a4-45d2-beb9-c11cc955fb78","requestType":"REQUEST_TYPE_GET","contentType":"CONTENT_TYPE_MERKLE_TREE","bucketId":"0","pieceCid":"AQIeIKLbs3OibO5qbLJ/PLCo1m02oFHWCl4s7S59GWgxDUbk","offset":"0","size":"0","timestamp":"1727346880633","signature":{"algorithm":"ED_25519","signer":"CsfLnFNZTp9TjZlQxrzyjwwMe4OF3uouviQGK8ZA574=","value":"ulpjaksvopDDRRfYnrccUg5spkoRpfZlDARbjgfL4Y/X4HZNUp2cL5qQMHUosREB6PSMXr9rQvXYGA9kmrUBDg=="}}},{"request":{"requestId":"8af9ba14-4c49-438c-957d-d1a108a58b85","requestType":"REQUEST_TYPE_GET","contentType":"CONTENT_TYPE_SEGMENT","bucketId":"0","pieceCid":"AQIeIKLbs3OibO5qbLJ/PLCo1m02oFHWCl4s7S59GWgxDUbk","offset":"0","size":"524288","timestamp":"1727346880633","signature":{"algorithm":"ED_25519","signer":"CsfLnFNZTp9TjZlQxrzyjwwMe4OF3uouviQGK8ZA574=","value":"CLdw3HaQWVWdDHeog2SZjiEA4NZN6PD8vyw58JuQI7gMDpDXLFslMOcI7p/uNEyeDfNoKTAgNZpWbNR4vSZ/AA=="}}},{"request":{"requestId":"b3dc8833-d5aa-4e33-9afa-54584da29cda","requestType":"REQUEST_TYPE_GET","contentType":"CONTENT_TYPE_SEGMENT","bucketId":"0","pieceCid":"AQIeIKLbs3OibO5qbLJ/PLCo1m02oFHWCl4s7S59GWgxDUbk","offset":"0","size":"524288","timestamp":"1727346880633","signature":{"algorithm":"ED_25519","signer":"CsfLnFNZTp9TjZlQxrzyjwwMe4OF3uouviQGK8ZA574=","value":"5XTnDU/85DqWWpMy1kGRVK6ZHe/EYDeg2p07UbFnIr6xLX7n50k9MslwuF8jMl2/QoBrPnndHdCd5ssqV90kDg=="}}}],"timestamp":"1727346880633","signature":{"algorithm":"ED_25519","signer":"CsfLnFNZTp9TjZlQxrzyjwwMe4OF3uouviQGK8ZA574=","value":"8WWGHaL3n8+bkuYQhTua3l+i3W//XXhlnzCpQ7VJ/BmfXQPFGEjIZsXw0kKr4+VXh/kWAncF3VrvW9nEi6G2CQ=="}},"transferred_bytes":1048576,"stored_bytes":0},{"record":{"id":"8Rg6VlRrSE65NsCY02OnlA==","upstream":{"request":{"requestId":"aacf30c4-b2e9-4f37-826d-0016c280f39b","requestType":"REQUEST_TYPE_PUT","contentType":"CONTENT_TYPE_METADATA","bucketId":"0","pieceCid":"AAAAAAAAAAEBAh4gaLfPG3AA1QwNFQc3VvJYsMAINAN6mMkvo5vk5HP8g/0=","offset":"0","size":"385","timestamp":"1727346880673","signature":{"algorithm":"ED_25519","signer":"xHUfclv0KTLyCz1NjsLAdMrEBfKdlta130WiEBvB14s=","value":"yPZt7Fyfp1aiJL+hYOg5rRtPPTNDMZwgReX2RX4bWbP8+ivreh1cNvSwnM5ln0EFqxTn53iVQpZeMWXUSiJeCw=="}}},"downstream":[],"timestamp":"1727346880673","signature":{"algorithm":"ED_25519","signer":"CsfLnFNZTp9TjZlQxrzyjwwMe4OF3uouviQGK8ZA574=","value":"zX0aGW/FuhddMAtGvN4Gjf6P1JaFGasrwf5yCrQPFv4qUB1GyACynb1s1+Mv0zpMAGOtIOcwaemoPu4fnOByBA=="}},"transferred_bytes":1048576,"stored_bytes":1048576}]}]}"#.to_vec()), + sent: true, + ..Default::default() + }; + + let pending_request2 = PendingRequest { + method: "GET".to_string(), + uri: format!("http://{}:{}/activity/buckets/123229/traverse?eraId=5757773&nodeId=0x1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72&merkleTreeNodeId=1&levels=1", host1, port), + response: Some(br#"[{"merkle_tree_node_id":2,"hash":"hkujtYgWP21CrXdRP1rhRPrYR2ooIYCnP5zwCERTePI=","stored_bytes":20913291,"transferred_bytes":20913291,"number_of_puts":61,"number_of_gets":3},{"merkle_tree_node_id":3,"hash":"ZgWwK2LgWkHpx5JlXZn/Rouq6uE9DhOnRH6EA1+QO6o=","stored_bytes":23778084,"transferred_bytes":23778084,"number_of_puts":46,"number_of_gets":2}]"#.to_vec()), + sent: true, + ..Default::default() + }; + + offchain_state.expect_request(pending_request1); + offchain_state.expect_request(pending_request2); + + drop(offchain_state); + + let cluster_id = ClusterId::from([1; 20]); + let era_id = 5757773; + let host1 = "178.251.228.165"; + + + let port = 8080; + let node_params1 = StorageNodeParams { + ssl: false, + host: host1.as_bytes().to_vec(), + http_port: port, + mode: StorageNodeMode::DAC, + p2p_port: 5555, + grpc_port: 4444, + domain: b"example2.com".to_vec(), + }; + + let deffective_bucket_sub_aggregate = BucketSubAggregate { + bucket_id: 123229, + node_id: "0x1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72" + .to_string(), + stored_bytes: 0, + transferred_bytes: 25143977, + number_of_puts: 0, + number_of_gets: 10, + aggregator: AggregatorInfo { + node_pub_key: NodePubKey::StoragePubKey(AccountId32::new([0; 32])), + node_params: node_params1.clone(), + }, + }; + + let result = + DdcVerification::_challenge_aggregate(&cluster_id, era_id, &deffective_bucket_sub_aggregate); + + assert!(result.is_ok()); + + }); +} diff --git a/pallets/ddc-verification/src/weights.rs b/pallets/ddc-verification/src/weights.rs new file mode 100644 index 000000000..6603465a4 --- /dev/null +++ b/pallets/ddc-verification/src/weights.rs @@ -0,0 +1,59 @@ +//! Autogenerated weights for pallet_ddc_verification +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-05-21, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `192.168.1.4`, CPU: `` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/cere +// benchmark +// pallet +// --chain +// dev +// --wasm-execution=compiled +// --pallet +// pallet_ddc_verification +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --output=./pallets/ddc-verification/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![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_ddc_verification. +pub trait WeightInfo { + fn create_billing_reports() -> Weight; +} + +/// Weights for pallet_ddc_verification using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: `DdcVerification::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcVerification::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn create_billing_reports() -> Weight { + Weight::from_parts(11_000_000_u64, 0) + .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: `DdcVerification::ActiveBillingReports` (r:1 w:1) + // Proof: `DdcVerification::ActiveBillingReports` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn create_billing_reports() -> Weight { + Weight::from_parts(11_000_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/erc20/src/tests.rs b/pallets/erc20/src/tests.rs index 8ca47443b..5643698da 100644 --- a/pallets/erc20/src/tests.rs +++ b/pallets/erc20/src/tests.rs @@ -1,9 +1,9 @@ #![cfg(test)] use super::mock::{ - assert_events, balances, event_exists, expect_event, new_test_ext, Balances, Bridge, Call, - Erc721, Erc721Id, Event, Example, HashId, NativeTokenId, Origin, ProposalLifetime, Test, - ENDOWED_BALANCE, RELAYER_A, RELAYER_B, RELAYER_C, + assert_events, balances, event_exists, expect_event, new_test_ext, Balances, Bridge, Call, + Erc721, Erc721Id, Event, Example, HashId, NativeTokenId, Origin, ProposalLifetime, Test, + ENDOWED_BALANCE, RELAYER_A, RELAYER_B, RELAYER_C, }; use super::*; use frame_support::dispatch::DispatchError; @@ -16,323 +16,286 @@ use sp_core::{blake2_256, H256}; const TEST_THRESHOLD: u32 = 2; fn make_remark_proposal(hash: H256) -> Call { - Call::Example(crate::Call::remark(hash)) + Call::Example(crate::Call::remark(hash)) } fn make_transfer_proposal(to: u64, amount: u64) -> Call { - Call::Example(crate::Call::transfer(to, amount.into())) + Call::Example(crate::Call::transfer(to, amount.into())) } #[test] fn transfer_hash() { - new_test_ext().execute_with(|| { - let dest_chain = 0; - let resource_id = HashId::get(); - let hash: H256 = "ABC".using_encoded(blake2_256).into(); - - assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); - - assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); - assert_ok!(Example::transfer_hash( - Origin::signed(1), - hash.clone(), - dest_chain, - )); - - expect_event(bridge::RawEvent::GenericTransfer( - dest_chain, - 1, - resource_id, - hash.as_ref().to_vec(), - )); - }) + new_test_ext().execute_with(|| { + let dest_chain = 0; + let resource_id = HashId::get(); + let hash: H256 = "ABC".using_encoded(blake2_256).into(); + + assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); + + assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); + assert_ok!(Example::transfer_hash(Origin::signed(1), hash.clone(), dest_chain,)); + + expect_event(bridge::RawEvent::GenericTransfer( + dest_chain, + 1, + resource_id, + hash.as_ref().to_vec(), + )); + }) } /* #[test] fn transfer_native() { - new_test_ext().execute_with(|| { - let dest_chain = 0; - let resource_id = NativeTokenId::get(); - let amount: u64 = 100; - let recipient = vec![99]; - - assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); - assert_ok!(Example::transfer_native( - Origin::signed(RELAYER_A), - amount.clone(), - recipient.clone(), - dest_chain, - )); - - expect_event(bridge::RawEvent::FungibleTransfer( - dest_chain, - 1, - resource_id, - amount.into(), - recipient, - )); - }) + new_test_ext().execute_with(|| { + let dest_chain = 0; + let resource_id = NativeTokenId::get(); + let amount: u64 = 100; + let recipient = vec![99]; + + assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); + assert_ok!(Example::transfer_native( + Origin::signed(RELAYER_A), + amount.clone(), + recipient.clone(), + dest_chain, + )); + + expect_event(bridge::RawEvent::FungibleTransfer( + dest_chain, + 1, + resource_id, + amount.into(), + recipient, + )); + }) } */ #[test] fn transfer_erc721() { - new_test_ext().execute_with(|| { - let dest_chain = 0; - let resource_id = Erc721Id::get(); - let token_id: U256 = U256::from(100); - let token_id_slice: &mut [u8] = &mut [0; 32]; - token_id.to_big_endian(token_id_slice); - let metadata: Vec = vec![1, 2, 3, 4]; - let recipient = vec![99]; - - // Create a token - assert_ok!(Erc721::mint( - Origin::root(), - RELAYER_A, - token_id, - metadata.clone() - )); - assert_eq!( - Erc721::tokens(token_id).unwrap(), - Erc721Token { - id: token_id, - metadata: metadata.clone() - } - ); - - // Whitelist destination and transfer - assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); - assert_ok!(Example::transfer_erc721( - Origin::signed(RELAYER_A), - recipient.clone(), - token_id, - dest_chain, - )); - - expect_event(bridge::RawEvent::NonFungibleTransfer( - dest_chain, - 1, - resource_id, - token_id_slice.to_vec(), - recipient.clone(), - metadata, - )); - - // Ensure token no longer exists - assert_eq!(Erc721::tokens(token_id), None); - - // Transfer should fail as token doesn't exist - assert_noop!( - Example::transfer_erc721( - Origin::signed(RELAYER_A), - recipient.clone(), - token_id, - dest_chain, - ), - Error::::InvalidTransfer - ); - }) + new_test_ext().execute_with(|| { + let dest_chain = 0; + let resource_id = Erc721Id::get(); + let token_id: U256 = U256::from(100); + let token_id_slice: &mut [u8] = &mut [0; 32]; + token_id.to_big_endian(token_id_slice); + let metadata: Vec = vec![1, 2, 3, 4]; + let recipient = vec![99]; + + // Create a token + assert_ok!(Erc721::mint(Origin::root(), RELAYER_A, token_id, metadata.clone())); + assert_eq!( + Erc721::tokens(token_id).unwrap(), + Erc721Token { id: token_id, metadata: metadata.clone() } + ); + + // Whitelist destination and transfer + assert_ok!(Bridge::whitelist_chain(Origin::root(), dest_chain.clone())); + assert_ok!(Example::transfer_erc721( + Origin::signed(RELAYER_A), + recipient.clone(), + token_id, + dest_chain, + )); + + expect_event(bridge::RawEvent::NonFungibleTransfer( + dest_chain, + 1, + resource_id, + token_id_slice.to_vec(), + recipient.clone(), + metadata, + )); + + // Ensure token no longer exists + assert_eq!(Erc721::tokens(token_id), None); + + // Transfer should fail as token doesn't exist + assert_noop!( + Example::transfer_erc721( + Origin::signed(RELAYER_A), + recipient.clone(), + token_id, + dest_chain, + ), + Error::::InvalidTransfer + ); + }) } #[test] fn execute_remark() { - new_test_ext().execute_with(|| { - let hash: H256 = "ABC".using_encoded(blake2_256).into(); - let proposal = make_remark_proposal(hash.clone()); - let prop_id = 1; - let src_id = 1; - let r_id = bridge::derive_resource_id(src_id, b"hash"); - let resource = b"Example.remark".to_vec(); - - assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); - assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_A)); - assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_B)); - assert_ok!(Bridge::whitelist_chain(Origin::root(), src_id)); - assert_ok!(Bridge::set_resource(Origin::root(), r_id, resource)); - - assert_ok!(Bridge::acknowledge_proposal( - Origin::signed(RELAYER_A), - prop_id, - src_id, - r_id, - Box::new(proposal.clone()) - )); - assert_ok!(Bridge::acknowledge_proposal( - Origin::signed(RELAYER_B), - prop_id, - src_id, - r_id, - Box::new(proposal.clone()) - )); - - event_exists(RawEvent::Remark(hash)); - }) + new_test_ext().execute_with(|| { + let hash: H256 = "ABC".using_encoded(blake2_256).into(); + let proposal = make_remark_proposal(hash.clone()); + let prop_id = 1; + let src_id = 1; + let r_id = bridge::derive_resource_id(src_id, b"hash"); + let resource = b"Example.remark".to_vec(); + + assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); + assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_A)); + assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_B)); + assert_ok!(Bridge::whitelist_chain(Origin::root(), src_id)); + assert_ok!(Bridge::set_resource(Origin::root(), r_id, resource)); + + assert_ok!(Bridge::acknowledge_proposal( + Origin::signed(RELAYER_A), + prop_id, + src_id, + r_id, + Box::new(proposal.clone()) + )); + assert_ok!(Bridge::acknowledge_proposal( + Origin::signed(RELAYER_B), + prop_id, + src_id, + r_id, + Box::new(proposal.clone()) + )); + + event_exists(RawEvent::Remark(hash)); + }) } #[test] fn execute_remark_bad_origin() { - new_test_ext().execute_with(|| { - let hash: H256 = "ABC".using_encoded(blake2_256).into(); - - assert_ok!(Example::remark(Origin::signed(Bridge::account_id()), hash)); - // Don't allow any signed origin except from bridge addr - assert_noop!( - Example::remark(Origin::signed(RELAYER_A), hash), - DispatchError::BadOrigin - ); - // Don't allow root calls - assert_noop!( - Example::remark(Origin::root(), hash), - DispatchError::BadOrigin - ); - }) + new_test_ext().execute_with(|| { + let hash: H256 = "ABC".using_encoded(blake2_256).into(); + + assert_ok!(Example::remark(Origin::signed(Bridge::account_id()), hash)); + // Don't allow any signed origin except from bridge addr + assert_noop!(Example::remark(Origin::signed(RELAYER_A), hash), DispatchError::BadOrigin); + // Don't allow root calls + assert_noop!(Example::remark(Origin::root(), hash), DispatchError::BadOrigin); + }) } #[test] fn transfer() { - new_test_ext().execute_with(|| { - // Check inital state - let bridge_id: u64 = Bridge::account_id(); - assert_eq!(Balances::free_balance(&bridge_id), ENDOWED_BALANCE); - // Transfer and check result - assert_ok!(Example::transfer( - Origin::signed(Bridge::account_id()), - RELAYER_A, - 10 - )); - assert_eq!(Balances::free_balance(&bridge_id), ENDOWED_BALANCE - 10); - assert_eq!(Balances::free_balance(RELAYER_A), ENDOWED_BALANCE + 10); - - assert_events(vec![Event::balances(balances::RawEvent::Transfer( - Bridge::account_id(), - RELAYER_A, - 10, - ))]); - }) + new_test_ext().execute_with(|| { + // Check inital state + let bridge_id: u64 = Bridge::account_id(); + assert_eq!(Balances::free_balance(&bridge_id), ENDOWED_BALANCE); + // Transfer and check result + assert_ok!(Example::transfer(Origin::signed(Bridge::account_id()), RELAYER_A, 10)); + assert_eq!(Balances::free_balance(&bridge_id), ENDOWED_BALANCE - 10); + assert_eq!(Balances::free_balance(RELAYER_A), ENDOWED_BALANCE + 10); + + assert_events(vec![Event::balances(balances::RawEvent::Transfer( + Bridge::account_id(), + RELAYER_A, + 10, + ))]); + }) } #[test] fn mint_erc721() { - new_test_ext().execute_with(|| { - let token_id = U256::from(99); - let recipient = RELAYER_A; - let metadata = vec![1, 1, 1, 1]; - let bridge_id: u64 = Bridge::account_id(); - - // Token doesn't yet exist - assert_eq!(Erc721::tokens(token_id), None); - // Mint - assert_ok!(Example::mint_erc721( - Origin::signed(bridge_id), - recipient, - token_id, - metadata.clone() - )); - // Ensure token exists - assert_eq!( - Erc721::tokens(token_id).unwrap(), - Erc721Token { - id: token_id, - metadata: metadata.clone() - } - ); - // Cannot mint same token - assert_noop!( - Example::mint_erc721( - Origin::signed(bridge_id), - recipient, - token_id, - metadata.clone() - ), - erc721::Error::::TokenAlreadyExists - ); - }) + new_test_ext().execute_with(|| { + let token_id = U256::from(99); + let recipient = RELAYER_A; + let metadata = vec![1, 1, 1, 1]; + let bridge_id: u64 = Bridge::account_id(); + + // Token doesn't yet exist + assert_eq!(Erc721::tokens(token_id), None); + // Mint + assert_ok!(Example::mint_erc721( + Origin::signed(bridge_id), + recipient, + token_id, + metadata.clone() + )); + // Ensure token exists + assert_eq!( + Erc721::tokens(token_id).unwrap(), + Erc721Token { id: token_id, metadata: metadata.clone() } + ); + // Cannot mint same token + assert_noop!( + Example::mint_erc721(Origin::signed(bridge_id), recipient, token_id, metadata.clone()), + erc721::Error::::TokenAlreadyExists + ); + }) } #[test] fn create_sucessful_transfer_proposal() { - new_test_ext().execute_with(|| { - let prop_id = 1; - let src_id = 1; - let r_id = bridge::derive_resource_id(src_id, b"transfer"); - let resource = b"Example.transfer".to_vec(); - let proposal = make_transfer_proposal(RELAYER_A, 10); - - assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); - assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_A)); - assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_B)); - assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_C)); - assert_ok!(Bridge::whitelist_chain(Origin::root(), src_id)); - assert_ok!(Bridge::set_resource(Origin::root(), r_id, resource)); - - // Create proposal (& vote) - assert_ok!(Bridge::acknowledge_proposal( - Origin::signed(RELAYER_A), - prop_id, - src_id, - r_id, - Box::new(proposal.clone()) - )); - let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); - let expected = bridge::ProposalVotes { - votes_for: vec![RELAYER_A], - votes_against: vec![], - status: bridge::ProposalStatus::Initiated, - expiry: ProposalLifetime::get() + 1, - }; - assert_eq!(prop, expected); - - // Second relayer votes against - assert_ok!(Bridge::reject_proposal( - Origin::signed(RELAYER_B), - prop_id, - src_id, - r_id, - Box::new(proposal.clone()) - )); - let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); - let expected = bridge::ProposalVotes { - votes_for: vec![RELAYER_A], - votes_against: vec![RELAYER_B], - status: bridge::ProposalStatus::Initiated, - expiry: ProposalLifetime::get() + 1, - }; - assert_eq!(prop, expected); - - // Third relayer votes in favour - assert_ok!(Bridge::acknowledge_proposal( - Origin::signed(RELAYER_C), - prop_id, - src_id, - r_id, - Box::new(proposal.clone()) - )); - let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); - let expected = bridge::ProposalVotes { - votes_for: vec![RELAYER_A, RELAYER_C], - votes_against: vec![RELAYER_B], - status: bridge::ProposalStatus::Approved, - expiry: ProposalLifetime::get() + 1, - }; - assert_eq!(prop, expected); - - assert_eq!(Balances::free_balance(RELAYER_A), ENDOWED_BALANCE + 10); - assert_eq!( - Balances::free_balance(Bridge::account_id()), - ENDOWED_BALANCE - 10 - ); - - assert_events(vec![ - Event::bridge(bridge::RawEvent::VoteFor(src_id, prop_id, RELAYER_A)), - Event::bridge(bridge::RawEvent::VoteAgainst(src_id, prop_id, RELAYER_B)), - Event::bridge(bridge::RawEvent::VoteFor(src_id, prop_id, RELAYER_C)), - Event::bridge(bridge::RawEvent::ProposalApproved(src_id, prop_id)), - Event::balances(balances::RawEvent::Transfer( - Bridge::account_id(), - RELAYER_A, - 10, - )), - Event::bridge(bridge::RawEvent::ProposalSucceeded(src_id, prop_id)), - ]); - }) + new_test_ext().execute_with(|| { + let prop_id = 1; + let src_id = 1; + let r_id = bridge::derive_resource_id(src_id, b"transfer"); + let resource = b"Example.transfer".to_vec(); + let proposal = make_transfer_proposal(RELAYER_A, 10); + + assert_ok!(Bridge::set_threshold(Origin::root(), TEST_THRESHOLD,)); + assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_A)); + assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_B)); + assert_ok!(Bridge::add_relayer(Origin::root(), RELAYER_C)); + assert_ok!(Bridge::whitelist_chain(Origin::root(), src_id)); + assert_ok!(Bridge::set_resource(Origin::root(), r_id, resource)); + + // Create proposal (& vote) + assert_ok!(Bridge::acknowledge_proposal( + Origin::signed(RELAYER_A), + prop_id, + src_id, + r_id, + Box::new(proposal.clone()) + )); + let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); + let expected = bridge::ProposalVotes { + votes_for: vec![RELAYER_A], + votes_against: vec![], + status: bridge::ProposalStatus::Initiated, + expiry: ProposalLifetime::get() + 1, + }; + assert_eq!(prop, expected); + + // Second relayer votes against + assert_ok!(Bridge::reject_proposal( + Origin::signed(RELAYER_B), + prop_id, + src_id, + r_id, + Box::new(proposal.clone()) + )); + let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); + let expected = bridge::ProposalVotes { + votes_for: vec![RELAYER_A], + votes_against: vec![RELAYER_B], + status: bridge::ProposalStatus::Initiated, + expiry: ProposalLifetime::get() + 1, + }; + assert_eq!(prop, expected); + + // Third relayer votes in favour + assert_ok!(Bridge::acknowledge_proposal( + Origin::signed(RELAYER_C), + prop_id, + src_id, + r_id, + Box::new(proposal.clone()) + )); + let prop = Bridge::votes(src_id, (prop_id.clone(), proposal.clone())).unwrap(); + let expected = bridge::ProposalVotes { + votes_for: vec![RELAYER_A, RELAYER_C], + votes_against: vec![RELAYER_B], + status: bridge::ProposalStatus::Approved, + expiry: ProposalLifetime::get() + 1, + }; + assert_eq!(prop, expected); + + assert_eq!(Balances::free_balance(RELAYER_A), ENDOWED_BALANCE + 10); + assert_eq!(Balances::free_balance(Bridge::account_id()), ENDOWED_BALANCE - 10); + + assert_events(vec![ + Event::bridge(bridge::RawEvent::VoteFor(src_id, prop_id, RELAYER_A)), + Event::bridge(bridge::RawEvent::VoteAgainst(src_id, prop_id, RELAYER_B)), + Event::bridge(bridge::RawEvent::VoteFor(src_id, prop_id, RELAYER_C)), + Event::bridge(bridge::RawEvent::ProposalApproved(src_id, prop_id)), + Event::balances(balances::RawEvent::Transfer(Bridge::account_id(), RELAYER_A, 10)), + Event::bridge(bridge::RawEvent::ProposalSucceeded(src_id, prop_id)), + ]); + }) } diff --git a/pallets/erc721/src/mock.rs b/pallets/erc721/src/mock.rs index 8c346db88..5b2227cc3 100644 --- a/pallets/erc721/src/mock.rs +++ b/pallets/erc721/src/mock.rs @@ -65,6 +65,7 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type ReserveIdentifier = (); type FreezeIdentifier = (); + type RuntimeFreezeReason = (); type MaxFreezes = (); type MaxHolds = (); type RuntimeHoldReason = (); diff --git a/pallets/origins/Cargo.toml b/pallets/origins/Cargo.toml new file mode 100644 index 000000000..e29cae4fc --- /dev/null +++ b/pallets/origins/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "pallet-origins" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true + +[dependencies] +# 3rd-party dependencies +cere-runtime-common = { workspace = true } +codec = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true } + +# Substrate dependencies +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-system/try-runtime", +] diff --git a/runtime/cere-dev/src/governance/origins.rs b/pallets/origins/src/lib.rs similarity index 90% rename from runtime/cere-dev/src/governance/origins.rs rename to pallets/origins/src/lib.rs index 57e2c98cc..a8ae6d921 100644 --- a/runtime/cere-dev/src/governance/origins.rs +++ b/pallets/origins/src/lib.rs @@ -1,13 +1,14 @@ //! Custom origins for governance interventions. -pub use pallet_custom_origins::*; +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; #[frame_support::pallet] -pub mod pallet_custom_origins { +pub mod pallet { + use cere_runtime_common::constants::currency::{Balance, DOLLARS, GRAND}; use frame_support::pallet_prelude::*; - use crate::{Balance, DOLLARS, GRAND}; - #[pallet::config] pub trait Config: frame_system::Config {} @@ -22,8 +23,6 @@ pub mod pallet_custom_origins { /// Origin for spending up to $10,000,000 DOT from the treasury as well as generally /// administering it. Treasurer, - /// Origin for managing the composition of the fellowship. - FellowshipAdmin, /// Origin for managing the registrar. GeneralAdmin, /// Origin able to cancel referenda. @@ -42,6 +41,10 @@ pub mod pallet_custom_origins { BigSpender, /// Origin able to dispatch a whitelisted call. WhitelistedCaller, + /// Origin for activating clusters protocols. + ClusterProtocolActivator, + /// Origin for updating clusters protocols. + ClusterProtocolUpdater, } macro_rules! decl_unit_ensures { @@ -77,11 +80,12 @@ pub mod pallet_custom_origins { decl_unit_ensures!( StakingAdmin, Treasurer, - FellowshipAdmin, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller, + ClusterProtocolActivator, + ClusterProtocolUpdater, ); macro_rules! decl_ensure { diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml deleted file mode 100644 index 00c245367..000000000 --- a/pallets/staking/Cargo.toml +++ /dev/null @@ -1,73 +0,0 @@ -[package] -name = "pallet-staking" -version = "4.0.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -homepage = "https://substrate.io" -license = "Apache-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet staking" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -frame-election-provider-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -log = { version = "0.4.17", default-features = false } -pallet-authorship = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -pallet-session = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false, features = ["historical"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive", "serde"] } -serde = { version = "1.0.163", default-features = false, features = ["alloc", "derive"] } -sp-application-crypto = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false, features = ["serde"] } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -sp-staking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false } - -# Optional imports for benchmarking -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false, optional = true } -rand_chacha = { version = "0.2", default-features = false, optional = true } - -[dev-dependencies] -frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -frame-election-provider-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -pallet-bags-list = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -pallet-staking-reward-curve = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -pallet-timestamp = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -rand_chacha = { version = "0.2" } -sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -sp-npos-elections = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -sp-tracing = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -substrate-test-utils = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } - -[features] -default = ["std"] -std = [ - "frame-benchmarking?/std", - "serde/std", - "codec/std", - "scale-info/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "sp-runtime/std", - "sp-staking/std", - "pallet-session/std", - "frame-system/std", - "pallet-authorship/std", - "sp-application-crypto/std", - "log/std", - "frame-election-provider-support/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-election-provider-support/runtime-benchmarks", - "rand_chacha", - "sp-staking/runtime-benchmarks", -] -try-runtime = ["frame-support/try-runtime", "frame-election-provider-support/try-runtime"] diff --git a/pallets/staking/README.md b/pallets/staking/README.md deleted file mode 100644 index ccb9901a6..000000000 --- a/pallets/staking/README.md +++ /dev/null @@ -1,261 +0,0 @@ -# Staking Module - -The Staking module is used to manage funds at stake by network maintainers. - -- [`staking::Config`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Config.html) -- [`Call`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html) -- [`Module`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Module.html) - -## Overview - -The Staking module is the means by which a set of network maintainers (known as _authorities_ in -some contexts and _validators_ in others) are chosen based upon those who voluntarily place -funds under deposit. Under deposit, those funds are rewarded under normal operation but are held -at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging -its duties properly. - -### Terminology - - -- Staking: The process of locking up funds for some time, placing them at risk of slashing - (loss) in order to become a rewarded maintainer of the network. -- Validating: The process of running a node to actively maintain the network, either by - producing blocks or guaranteeing finality of the chain. -- Nominating: The process of placing staked funds behind one or more validators in order to - share in any reward, and punishment, they take. -- Stash account: The account holding an owner's funds used for staking. -- Controller account: The account that controls an owner's funds for staking. -- Era: A (whole) number of sessions, which is the period that the validator set (and each - validator's active nominator set) is recalculated and where rewards are paid out. -- Slash: The punishment of a staker by reducing its funds. - -### Goals - - -The staking system in Substrate NPoS is designed to make the following possible: - -- Stake funds that are controlled by a cold wallet. -- Withdraw some, or deposit more, funds without interrupting the role of an entity. -- Switch between roles (nominator, validator, idle) with minimal overhead. - -### Scenarios - -#### Staking - -Almost any interaction with the Staking module requires a process of _**bonding**_ (also known -as being a _staker_). To become *bonded*, a fund-holding account known as the _stash account_, -which holds some or all of the funds that become frozen in place as part of the staking process, -is paired with an active **controller** account, which issues instructions on how they shall be -used. - -An account pair can become bonded using the [`bond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.bond) call. - -Stash accounts can update their associated controller back to their stash account using the -[`set_controller`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.set_controller) -call. - -Note: Controller accounts are being deprecated in favor of proxy accounts, so it is no longer -possible to set a unique address for a stash's controller. - -There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` -and `Idle` (defined in [`StakerStatus`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.StakerStatus.html)). There are three -corresponding instructions to change between roles, namely: -[`validate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.validate), -[`nominate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.nominate), and [`chill`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.chill). - -#### Validating - -A **validator** takes the role of either validating blocks or ensuring their finality, -maintaining the veracity of the network. A validator should avoid both any sort of malicious -misbehavior and going offline. Bonded accounts that state interest in being a validator do NOT -get immediately chosen as a validator. Instead, they are declared as a _candidate_ and they -_might_ get elected at the _next era_ as a validator. The result of the election is determined -by nominators and their votes. - -An account can become a validator candidate via the -[`validate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.validate) call. - -#### Nomination - -A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on -a set of validators to be elected. Once interest in nomination is stated by an account, it -takes effect at the next election round. The funds in the nominator's stash account indicate the -_weight_ of its vote. Both the rewards and any punishment that a validator earns are shared -between the validator and its nominators. This rule incentivizes the nominators to NOT vote for -the misbehaving/offline validators as much as possible, simply because the nominators will also -lose funds if they vote poorly. - -An account can become a nominator via the [`nominate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.nominate) call. - -#### Rewards and Slash - -The **reward and slashing** procedure is the core of the Staking module, attempting to _embrace -valid behavior_ while _punishing any misbehavior or lack of availability_. - -Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the -`payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the -validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] -biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each -nominator's account. - -Slashing can occur at any point in time, once misbehavior is reported. Once slashing is -determined, a value is deducted from the balance of the validator and all the nominators who -voted for this validator (values are deducted from the _stash_ account of the slashed entity). - -Slashing logic is further described in the documentation of the `slashing` module. - -Similar to slashing, rewards are also shared among a validator and its associated nominators. -Yet, the reward funds are not always transferred to the stash account and can be configured. See -[Reward Calculation](https://docs.rs/pallet-staking/latest/pallet_staking/#reward-calculation) for more details. - -#### Chilling - -Finally, any of the roles above can choose to step back temporarily and just chill for a while. -This means that if they are a nominator, they will not be considered as voters anymore and if -they are validators, they will no longer be a candidate for the next election. - -An account can step back via the [`chill`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.chill) call. - -### Session managing - -The module implement the trait `SessionManager`. Which is the only API to query new validator -set and allowing these validator set to be rewarded once their era is ended. - -## Interface - -### Dispatchable Functions - -The dispatchable functions of the Staking module enable the steps needed for entities to accept -and change their role, alongside some helper functions to get/set the metadata of the module. - -### Public Functions - -The Staking module contains many public storage items and (im)mutable functions. - -## Usage - -### Example: Rewarding a validator by id. - -```rust -use pallet_staking::{self as staking}; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + staking::Config {} - - #[pallet::call] - impl Pallet { - /// Reward a validator. - #[pallet::weight(0)] - pub fn reward_myself(origin: OriginFor) -> DispatchResult { - let reported = ensure_signed(origin)?; - >::reward_by_ids(vec![(reported, 10)]); - Ok(()) - } - } -} -``` - -## Implementation Details - -### Era payout - -The era payout is computed using yearly inflation curve defined at -[`T::RewardCurve`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Config.html#associatedtype.RewardCurve) as such: - -```nocompile -staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year -``` -This payout is used to reward stakers as defined in next section - -```nocompile -remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout -``` -The remaining reward is send to the configurable end-point -[`T::RewardRemainder`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Config.html#associatedtype.RewardRemainder). - -### Reward Calculation - -Validators and nominators are rewarded at the end of each era. The total reward of an era is -calculated using the era duration and the staking rate (the total amount of tokens staked by -nominators and validators, divided by the total token supply). It aims to incentivize toward a -defined staking rate. The full specification can be found -[here](https://research.web3.foundation/en/latest/polkadot/economics/1-token-economics.html#inflation-model). - -Total reward is split among validators and their nominators depending on the number of points -they received during the era. Points are added to a validator using -[`reward_by_ids`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.reward_by_ids) or -[`reward_by_indices`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.reward_by_indices). - -[`Module`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Module.html) implements -[`pallet_authorship::EventHandler`](https://docs.rs/pallet-authorship/latest/pallet_authorship/trait.EventHandler.html) to add reward -points to block producer and block producer of referenced uncles. - -The validator and its nominator split their reward as following: - -The validator can declare an amount, named -[`commission`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.ValidatorPrefs.html#structfield.commission), that does not get shared -with the nominators at each reward payout through its -[`ValidatorPrefs`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.ValidatorPrefs.html). This value gets deducted from the total reward -that is paid to the validator and its nominators. The remaining portion is split among the -validator and all of the nominators that nominated the validator, proportional to the value -staked behind this validator (_i.e._ dividing the -[`own`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html#structfield.own) or -[`others`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html#structfield.others) by -[`total`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html#structfield.total) in [`Exposure`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html)). - -All entities who receive a reward have the option to choose their reward destination through the -[`Payee`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Payee.html) storage item (see -[`set_payee`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.set_payee)), to be one of the following: - -- Controller account, (obviously) not increasing the staked value. -- Stash account, not increasing the staked value. -- Stash account, also increasing the staked value. - -### Additional Fund Management Operations - -Any funds already placed into stash can be the target of the following operations: - -The controller account can free a portion (or all) of the funds using the -[`unbond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.unbond) call. Note that the funds are not immediately -accessible. Instead, a duration denoted by [`BondingDuration`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Config.html#associatedtype.BondingDuration) -(in number of eras) must pass until the funds can actually be removed. Once the -`BondingDuration` is over, the [`withdraw_unbonded`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.withdraw_unbonded) -call can be used to actually withdraw the funds. - -Note that there is a limitation to the number of fund-chunks that can be scheduled to be -unlocked in the future via [`unbond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.unbond). In case this maximum -(`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful -call to `withdraw_unbonded` to remove some of the chunks. - -### Election Algorithm - -The current election algorithm is implemented based on Phragmén. The reference implementation -can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). - -The election algorithm, aside from electing the validators with the most stake value and votes, -tries to divide the nominator votes among candidates in an equal manner. To further assure this, -an optional post-processing can be applied that iteratively normalizes the nominator staked -values until the total difference among votes of a particular nominator are less than a -threshold. - -## GenesisConfig - -The Staking module depends on the [`GenesisConfig`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.GenesisConfig.html). The -`GenesisConfig` is optional and allow to set some initial stakers. - -## Related Modules - -- [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/): Used to manage values at stake. -- [Session](https://docs.rs/pallet-session/latest/pallet_session/): Used to manage sessions. Also, a list of new - validators is stored in the Session module's `Validators` at the end of each era. - -License: Apache-2.0 diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs deleted file mode 100644 index 6ef782012..000000000 --- a/pallets/staking/src/benchmarking.rs +++ /dev/null @@ -1,1066 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Staking pallet benchmarking. - -use codec::Decode; -pub use frame_benchmarking::v1::{ - account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, -}; -use frame_election_provider_support::SortedListProvider; -use frame_support::{ - dispatch::UnfilteredDispatchable, - pallet_prelude::*, - traits::{Currency, Get, Imbalance}, -}; -use frame_system::RawOrigin; -use sp_runtime::{ - traits::{Bounded, One, StaticLookup, TrailingZeroInput, Zero}, - Perbill, Percent, -}; -use sp_staking::{currency_to_vote::CurrencyToVote, SessionIndex}; -use sp_std::prelude::*; -use testing_utils::*; - -use super::*; -use crate::{ConfigOp, Pallet as Staking}; - -const SEED: u32 = 0; -const MAX_SPANS: u32 = 100; -const MAX_SLASHES: u32 = 1000; - -type MaxValidators = <::BenchmarkingConfig as BenchmarkingConfig>::MaxValidators; -type MaxNominators = <::BenchmarkingConfig as BenchmarkingConfig>::MaxNominators; - -// Add slashing spans to a user account. Not relevant for actual use, only to benchmark -// read and write operations. -pub fn add_slashing_spans(who: &T::AccountId, spans: u32) { - if spans == 0 { - return - } - - // For the first slashing span, we initialize - let mut slashing_spans = crate::slashing::SlashingSpans::new(0); - SpanSlash::::insert((who, 0), crate::slashing::SpanRecord::default()); - - for i in 1..spans { - assert!(slashing_spans.end_span(i)); - SpanSlash::::insert((who, i), crate::slashing::SpanRecord::default()); - } - SlashingSpans::::insert(who, slashing_spans); -} - -// This function clears all existing validators and nominators from the set, and generates one new -// validator being nominated by n nominators, and returns the validator stash account and the -// nominators' stash and controller. It also starts an era and creates pending payouts. -pub fn create_validator_with_nominators( - n: u32, - upper_bound: u32, - dead_controller: bool, - unique_controller: bool, - destination: RewardDestination, -) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> { - // Clean up any existing state. - clear_validators_and_nominators::(); - let mut points_total = 0; - let mut points_individual = Vec::new(); - - let (v_stash, v_controller) = if unique_controller { - create_unique_stash_controller::(0, 100, destination.clone(), false)? - } else { - create_stash_controller::(0, 100, destination.clone())? - }; - - let validator_prefs = - ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?; - let stash_lookup = T::Lookup::unlookup(v_stash.clone()); - - points_total += 10; - points_individual.push((v_stash.clone(), 10)); - - let original_nominator_count = Nominators::::count(); - let mut nominators = Vec::new(); - - // Give the validator n nominators, but keep total users in the system the same. - for i in 0..upper_bound { - let (n_stash, n_controller) = if !dead_controller { - create_stash_controller::(u32::MAX - i, 100, destination.clone())? - } else { - create_unique_stash_controller::(u32::MAX - i, 100, destination.clone(), true)? - }; - if i < n { - Staking::::nominate( - RawOrigin::Signed(n_controller.clone()).into(), - vec![stash_lookup.clone()], - )?; - nominators.push((n_stash, n_controller)); - } - } - - ValidatorCount::::put(1); - - // Start a new Era - let new_validators = Staking::::try_trigger_new_era(SessionIndex::one(), true).unwrap(); - - assert_eq!(new_validators.len(), 1); - assert_eq!(new_validators[0], v_stash, "Our validator was not selected!"); - assert_ne!(Validators::::count(), 0); - assert_eq!(Nominators::::count(), original_nominator_count + nominators.len() as u32); - - // Give Era Points - let reward = EraRewardPoints:: { - total: points_total, - individual: points_individual.into_iter().collect(), - }; - - let current_era = CurrentEra::::get().unwrap(); - ErasRewardPoints::::insert(current_era, reward); - - // Create reward pool - let total_payout = T::Currency::minimum_balance() - .saturating_mul(upper_bound.into()) - .saturating_mul(1000u32.into()); - >::insert(current_era, total_payout); - - Ok((v_stash, nominators)) -} - -struct ListScenario { - /// Stash that is expected to be moved. - origin_stash1: T::AccountId, - /// Controller of the Stash that is expected to be moved. - origin_controller1: T::AccountId, - dest_weight: BalanceOf, -} - -impl ListScenario { - /// An expensive scenario for bags-list implementation: - /// - /// - the node to be updated (r) is the head of a bag that has at least one other node. The bag - /// itself will need to be read and written to update its head. The node pointed to by r.next - /// will need to be read and written as it will need to have its prev pointer updated. Note - /// that there are two other worst case scenarios for bag removal: 1) the node is a tail and - /// 2) the node is a middle node with prev and next; all scenarios end up with the same number - /// of storage reads and writes. - /// - /// - the destination bag has at least one node, which will need its next pointer updated. - /// - /// NOTE: while this scenario specifically targets a worst case for the bags-list, it should - /// also elicit a worst case for other known `VoterList` implementations; although - /// this may not be true against unknown `VoterList` implementations. - fn new(origin_weight: BalanceOf, is_increase: bool) -> Result { - ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0"); - - // burn the entire issuance. - let i = T::Currency::burn(T::Currency::total_issuance()); - sp_std::mem::forget(i); - - // create accounts with the origin weight - - let (origin_stash1, origin_controller1) = create_stash_controller_with_balance::( - USER_SEED + 2, - origin_weight, - Default::default(), - )?; - Staking::::nominate( - RawOrigin::Signed(origin_controller1.clone()).into(), - // NOTE: these don't really need to be validators. - vec![T::Lookup::unlookup(account("random_validator", 0, SEED))], - )?; - - let (_origin_stash2, origin_controller2) = create_stash_controller_with_balance::( - USER_SEED + 3, - origin_weight, - Default::default(), - )?; - Staking::::nominate( - RawOrigin::Signed(origin_controller2).into(), - vec![T::Lookup::unlookup(account("random_validator", 0, SEED))], - )?; - - // find a destination weight that will trigger the worst case scenario - let dest_weight_as_vote = - T::VoterList::score_update_worst_case(&origin_stash1, is_increase); - - let total_issuance = T::Currency::total_issuance(); - - let dest_weight = - T::CurrencyToVote::to_currency(dest_weight_as_vote as u128, total_issuance); - - // create an account with the worst case destination weight - let (_dest_stash1, dest_controller1) = create_stash_controller_with_balance::( - USER_SEED + 1, - dest_weight, - Default::default(), - )?; - Staking::::nominate( - RawOrigin::Signed(dest_controller1).into(), - vec![T::Lookup::unlookup(account("random_validator", 0, SEED))], - )?; - - Ok(ListScenario { origin_stash1, origin_controller1, dest_weight }) - } -} - -const USER_SEED: u32 = 999666; - -benchmarks! { - bond { - let stash = create_funded_user::("stash", USER_SEED, 100); - let reward_destination = RewardDestination::Staked; - let amount = T::Currency::minimum_balance() * 10u32.into(); - whitelist_account!(stash); - }: _(RawOrigin::Signed(stash.clone()), amount, reward_destination) - verify { - assert!(Bonded::::contains_key(stash.clone())); - assert!(Ledger::::contains_key(stash)); - } - - bond_extra { - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup the worst case list scenario. - - // the weight the nominator will start at. - let scenario = ListScenario::::new(origin_weight, true)?; - - let max_additional = scenario.dest_weight - origin_weight; - - let stash = scenario.origin_stash1.clone(); - let controller = scenario.origin_controller1; - let original_bonded: BalanceOf - = Ledger::::get(&controller).map(|l| l.active).ok_or("ledger not created after")?; - - T::Currency::deposit_into_existing(&stash, max_additional).unwrap(); - - whitelist_account!(stash); - }: _(RawOrigin::Signed(stash), max_additional) - verify { - let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; - let new_bonded: BalanceOf = ledger.active; - assert!(original_bonded < new_bonded); - } - - unbond { - // clean up any existing state. - clear_validators_and_nominators::(); - - // setup the worst case list scenario. - let total_issuance = T::Currency::total_issuance(); - // the weight the nominator will start at. The value used here is expected to be - // significantly higher than the first position in a list (e.g. the first bag threshold). - let origin_weight = BalanceOf::::try_from(952_994_955_240_703u128) - .map_err(|_| "balance expected to be a u128") - .unwrap(); - let scenario = ListScenario::::new(origin_weight, false)?; - - let stash = scenario.origin_stash1.clone(); - let controller = scenario.origin_controller1.clone(); - let amount = origin_weight - scenario.dest_weight; - let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; - let original_bonded: BalanceOf = ledger.active; - - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller.clone()), amount) - verify { - let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; - let new_bonded: BalanceOf = ledger.active; - assert!(original_bonded > new_bonded); - } - - // Withdraw only updates the ledger - withdraw_unbonded_update { - // Slashing Spans - let s in 0 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; - add_slashing_spans::(&stash, s); - let amount = T::Currency::minimum_balance() * 5u32.into(); // Half of total - Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; - CurrentEra::::put(EraIndex::max_value()); - let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; - let original_total: BalanceOf = ledger.total; - whitelist_account!(controller); - }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) - verify { - let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; - let new_total: BalanceOf = ledger.total; - assert!(original_total > new_total); - } - - // Worst case scenario, everything is removed after the bonding duration - withdraw_unbonded_kill { - // Slashing Spans - let s in 0 .. MAX_SPANS; - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note that we don't care about the setup of the - // destination position because we are doing a removal from the list but no insert. - let scenario = ListScenario::::new(origin_weight, true)?; - let controller = scenario.origin_controller1.clone(); - let stash = scenario.origin_stash1; - add_slashing_spans::(&stash, s); - assert!(T::VoterList::contains(&stash)); - - let ed = T::Currency::minimum_balance(); - let mut ledger = Ledger::::get(&controller).unwrap(); - ledger.active = ed - One::one(); - Ledger::::insert(&controller, ledger); - CurrentEra::::put(EraIndex::max_value()); - - whitelist_account!(controller); - }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) - verify { - assert!(!Ledger::::contains_key(controller)); - assert!(!T::VoterList::contains(&stash)); - } - - validate { - let (stash, controller) = create_stash_controller::( - T::MaxNominations::get() - 1, - 100, - Default::default(), - )?; - // because it is chilled. - assert!(!T::VoterList::contains(&stash)); - - let prefs = ValidatorPrefs::default(); - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller), prefs) - verify { - assert!(Validators::::contains_key(&stash)); - assert!(T::VoterList::contains(&stash)); - } - - kick { - // scenario: we want to kick `k` nominators from nominating us (we are a validator). - // we'll assume that `k` is under 128 for the purposes of determining the slope. - // each nominator should have `T::MaxNominations::get()` validators nominated, and our validator - // should be somewhere in there. - let k in 1 .. 128; - - // these are the other validators; there are `T::MaxNominations::get() - 1` of them, so - // there are a total of `T::MaxNominations::get()` validators in the system. - let rest_of_validators = create_validators_with_seed::(T::MaxNominations::get() - 1, 100, 415)?; - - // this is the validator that will be kicking. - let (stash, controller) = create_stash_controller::( - T::MaxNominations::get() - 1, - 100, - Default::default(), - )?; - let stash_lookup = T::Lookup::unlookup(stash.clone()); - - // they start validating. - Staking::::validate(RawOrigin::Signed(controller.clone()).into(), Default::default())?; - - // we now create the nominators. there will be `k` of them; each will nominate all - // validators. we will then kick each of the `k` nominators from the main validator. - let mut nominator_stashes = Vec::with_capacity(k as usize); - for i in 0 .. k { - // create a nominator stash. - let (n_stash, n_controller) = create_stash_controller::( - T::MaxNominations::get() + i, - 100, - Default::default(), - )?; - - // bake the nominations; we first clone them from the rest of the validators. - let mut nominations = rest_of_validators.clone(); - // then insert "our" validator somewhere in there (we vary it) to avoid accidental - // optimisations/pessimisations. - nominations.insert(i as usize % (nominations.len() + 1), stash_lookup.clone()); - // then we nominate. - Staking::::nominate(RawOrigin::Signed(n_controller.clone()).into(), nominations)?; - - nominator_stashes.push(n_stash); - } - - // all nominators now should be nominating our validator... - for n in nominator_stashes.iter() { - assert!(Nominators::::get(n).unwrap().targets.contains(&stash)); - } - - // we need the unlookuped version of the nominator stash for the kick. - let kicks = nominator_stashes.iter() - .map(|n| T::Lookup::unlookup(n.clone())) - .collect::>(); - - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller), kicks) - verify { - // all nominators now should *not* be nominating our validator... - for n in nominator_stashes.iter() { - assert!(!Nominators::::get(n).unwrap().targets.contains(&stash)); - } - } - - // Worst case scenario, T::MaxNominations::get() - nominate { - let n in 1 .. T::MaxNominations::get(); - - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note we don't care about the destination position, because - // we are just doing an insert into the origin position. - let scenario = ListScenario::::new(origin_weight, true)?; - let (stash, controller) = create_stash_controller_with_balance::( - SEED + T::MaxNominations::get() + 1, // make sure the account does not conflict with others - origin_weight, - Default::default(), - ).unwrap(); - - assert!(!Nominators::::contains_key(&stash)); - assert!(!T::VoterList::contains(&stash)); - - let validators = create_validators::(n, 100).unwrap(); - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller), validators) - verify { - assert!(Nominators::::contains_key(&stash)); - assert!(T::VoterList::contains(&stash)) - } - - chill { - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note that we don't care about the setup of the - // destination position because we are doing a removal from the list but no insert. - let scenario = ListScenario::::new(origin_weight, true)?; - let controller = scenario.origin_controller1.clone(); - let stash = scenario.origin_stash1; - assert!(T::VoterList::contains(&stash)); - - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller)) - verify { - assert!(!T::VoterList::contains(&stash)); - } - - set_payee { - let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; - assert_eq!(Payee::::get(&stash), RewardDestination::Staked); - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller), RewardDestination::Controller) - verify { - assert_eq!(Payee::::get(&stash), RewardDestination::Controller); - } - - set_controller { - let (stash, ctlr) = create_unique_stash_controller::(9000, 100, Default::default(), false)?; - // ensure `ctlr` is the currently stored controller. - assert!(!Ledger::::contains_key(&stash)); - assert!(Ledger::::contains_key(&ctlr)); - assert_eq!(Bonded::::get(&stash), Some(ctlr.clone())); - - whitelist_account!(stash); - }: _(RawOrigin::Signed(stash.clone())) - verify { - assert!(Ledger::::contains_key(&stash)); - } - - set_validator_count { - let validator_count = MaxValidators::::get(); - }: _(RawOrigin::Root, validator_count) - verify { - assert_eq!(ValidatorCount::::get(), validator_count); - } - - force_no_eras {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::::get(), Forcing::ForceNone); } - - force_new_era {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::::get(), Forcing::ForceNew); } - - force_new_era_always {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::::get(), Forcing::ForceAlways); } - - // Worst case scenario, the list of invulnerables is very long. - set_invulnerables { - let v in 0 .. MaxValidators::::get(); - let mut invulnerables = Vec::new(); - for i in 0 .. v { - invulnerables.push(account("invulnerable", i, SEED)); - } - }: _(RawOrigin::Root, invulnerables) - verify { - assert_eq!(Invulnerables::::get().len(), v as usize); - } - - force_unstake { - // Slashing Spans - let s in 0 .. MAX_SPANS; - // Clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note that we don't care about the setup of the - // destination position because we are doing a removal from the list but no insert. - let scenario = ListScenario::::new(origin_weight, true)?; - let controller = scenario.origin_controller1.clone(); - let stash = scenario.origin_stash1; - assert!(T::VoterList::contains(&stash)); - add_slashing_spans::(&stash, s); - - }: _(RawOrigin::Root, stash.clone(), s) - verify { - assert!(!Ledger::::contains_key(&controller)); - assert!(!T::VoterList::contains(&stash)); - } - - cancel_deferred_slash { - let s in 1 .. MAX_SLASHES; - let mut unapplied_slashes = Vec::new(); - let era = EraIndex::one(); - let dummy = || T::AccountId::decode(&mut TrailingZeroInput::zeroes()).unwrap(); - for _ in 0 .. MAX_SLASHES { - unapplied_slashes.push(UnappliedSlash::>::default_from(dummy())); - } - UnappliedSlashes::::insert(era, &unapplied_slashes); - - let slash_indices: Vec = (0 .. s).collect(); - }: _(RawOrigin::Root, era, slash_indices) - verify { - assert_eq!(UnappliedSlashes::::get(&era).len(), (MAX_SLASHES - s) as usize); - } - - payout_stakers_dead_controller { - let n in 0 .. T::MaxNominatorRewardedPerValidator::get() as u32; - let (validator, nominators) = create_validator_with_nominators::( - n, - T::MaxNominatorRewardedPerValidator::get() as u32, - true, - true, - RewardDestination::Controller, - )?; - - let current_era = CurrentEra::::get().unwrap(); - // set the commission for this particular era as well. - >::insert(current_era, validator.clone(), >::validators(&validator)); - - let caller = whitelisted_caller(); - let validator_controller = >::get(&validator).unwrap(); - let balance_before = T::Currency::free_balance(&validator_controller); - for (_, controller) in &nominators { - let balance = T::Currency::free_balance(controller); - ensure!(balance.is_zero(), "Controller has balance, but should be dead."); - } - }: payout_stakers(RawOrigin::Signed(caller), validator, current_era) - verify { - let balance_after = T::Currency::free_balance(&validator_controller); - ensure!( - balance_before < balance_after, - "Balance of validator controller should have increased after payout.", - ); - for (_, controller) in &nominators { - let balance = T::Currency::free_balance(controller); - ensure!(!balance.is_zero(), "Payout not given to controller."); - } - } - - payout_stakers_alive_staked { - let n in 0 .. T::MaxNominatorRewardedPerValidator::get() as u32; - let (validator, nominators) = create_validator_with_nominators::( - n, - T::MaxNominatorRewardedPerValidator::get() as u32, - false, - true, - RewardDestination::Staked, - )?; - - let current_era = CurrentEra::::get().unwrap(); - // set the commission for this particular era as well. - >::insert(current_era, validator.clone(), >::validators(&validator)); - - let caller = whitelisted_caller(); - let balance_before = T::Currency::free_balance(&validator); - let mut nominator_balances_before = Vec::new(); - for (stash, _) in &nominators { - let balance = T::Currency::free_balance(stash); - nominator_balances_before.push(balance); - } - }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) - verify { - let balance_after = T::Currency::free_balance(&validator); - ensure!( - balance_before < balance_after, - "Balance of validator stash should have increased after payout.", - ); - for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter()) { - let balance_after = T::Currency::free_balance(stash); - ensure!( - balance_before < &balance_after, - "Balance of nominator stash should have increased after payout.", - ); - } - } - - rebond { - let l in 1 .. T::MaxUnlockingChunks::get() as u32; - - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get() - .max(T::Currency::minimum_balance()) - // we use 100 to play friendly with the list threshold values in the mock - .max(100u32.into()); - - // setup a worst case list scenario. - let scenario = ListScenario::::new(origin_weight, true)?; - let dest_weight = scenario.dest_weight; - - // rebond an amount that will give the user dest_weight - let rebond_amount = dest_weight - origin_weight; - - // spread that amount to rebond across `l` unlocking chunks, - let value = rebond_amount / l.into(); - // if `value` is zero, we need a greater delta between dest <=> origin weight - assert_ne!(value, Zero::zero()); - // so the sum of unlocking chunks puts voter into the dest bag. - assert!(value * l.into() + origin_weight > origin_weight); - assert!(value * l.into() + origin_weight <= dest_weight); - let unlock_chunk = UnlockChunk::> { - value, - era: EraIndex::zero(), - }; - - let stash = scenario.origin_stash1.clone(); - let controller = scenario.origin_controller1; - let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); - - for _ in 0 .. l { - staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap() - } - Ledger::::insert(controller.clone(), staking_ledger.clone()); - let original_bonded: BalanceOf = staking_ledger.active; - - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller.clone()), rebond_amount) - verify { - let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; - let new_bonded: BalanceOf = ledger.active; - assert!(original_bonded < new_bonded); - } - - reap_stash { - let s in 1 .. MAX_SPANS; - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note that we don't care about the setup of the - // destination position because we are doing a removal from the list but no insert. - let scenario = ListScenario::::new(origin_weight, true)?; - let controller = scenario.origin_controller1.clone(); - let stash = scenario.origin_stash1; - - add_slashing_spans::(&stash, s); - let l = StakingLedger { - stash: stash.clone(), - active: T::Currency::minimum_balance() - One::one(), - total: T::Currency::minimum_balance() - One::one(), - unlocking: Default::default(), - claimed_rewards: Default::default(), - }; - Ledger::::insert(&controller, l); - - assert!(Bonded::::contains_key(&stash)); - assert!(T::VoterList::contains(&stash)); - - whitelist_account!(controller); - }: _(RawOrigin::Signed(controller), stash.clone(), s) - verify { - assert!(!Bonded::::contains_key(&stash)); - assert!(!T::VoterList::contains(&stash)); - } - - new_era { - let v in 1 .. 10; - let n in 0 .. 100; - - create_validators_with_nominators_for_era::( - v, - n, - ::MaxNominations::get() as usize, - false, - None, - )?; - let session_index = SessionIndex::one(); - }: { - let validators = Staking::::try_trigger_new_era(session_index, true) - .ok_or("`new_era` failed")?; - assert!(validators.len() == v as usize); - } - - #[extra] - payout_all { - let v in 1 .. 10; - let n in 0 .. 100; - create_validators_with_nominators_for_era::( - v, - n, - ::MaxNominations::get() as usize, - false, - None, - )?; - // Start a new Era - let new_validators = Staking::::try_trigger_new_era(SessionIndex::one(), true).unwrap(); - assert!(new_validators.len() == v as usize); - - let current_era = CurrentEra::::get().unwrap(); - let mut points_total = 0; - let mut points_individual = Vec::new(); - let mut payout_calls_arg = Vec::new(); - - for validator in new_validators.iter() { - points_total += 10; - points_individual.push((validator.clone(), 10)); - payout_calls_arg.push((validator.clone(), current_era)); - } - - // Give Era Points - let reward = EraRewardPoints:: { - total: points_total, - individual: points_individual.into_iter().collect(), - }; - - ErasRewardPoints::::insert(current_era, reward); - - // Create reward pool - let total_payout = T::Currency::minimum_balance() * 1000u32.into(); - >::insert(current_era, total_payout); - - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller); - let calls: Vec<_> = payout_calls_arg.iter().map(|arg| - Call::::payout_stakers { validator_stash: arg.0.clone(), era: arg.1 }.encode() - ).collect(); - }: { - for call in calls { - as Decode>::decode(&mut &*call) - .expect("call is encoded above, encoding must be correct") - .dispatch_bypass_filter(origin.clone().into())?; - } - } - - #[extra] - do_slash { - let l in 1 .. T::MaxUnlockingChunks::get() as u32; - let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; - let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); - let unlock_chunk = UnlockChunk::> { - value: 1u32.into(), - era: EraIndex::zero(), - }; - for _ in 0 .. l { - staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap(); - } - Ledger::::insert(controller, staking_ledger); - let slash_amount = T::Currency::minimum_balance() * 10u32.into(); - let balance_before = T::Currency::free_balance(&stash); - }: { - crate::slashing::do_slash::( - &stash, - slash_amount, - &mut BalanceOf::::zero(), - &mut NegativeImbalanceOf::::zero(), - EraIndex::zero() - ); - } verify { - let balance_after = T::Currency::free_balance(&stash); - assert!(balance_before > balance_after); - } - - get_npos_voters { - // number of validator intention. we will iterate all of them. - let v in (MaxValidators::::get() / 2) .. MaxValidators::::get(); - // number of nominator intention. we will iterate all of them. - let n in (MaxNominators::::get() / 2) .. MaxNominators::::get(); - - let validators = create_validators_with_nominators_for_era::( - v, n, T::MaxNominations::get() as usize, false, None - )? - .into_iter() - .map(|v| T::Lookup::lookup(v).unwrap()) - .collect::>(); - - assert_eq!(Validators::::count(), v); - assert_eq!(Nominators::::count(), n); - - let num_voters = (v + n) as usize; - }: { - let voters = >::get_npos_voters(None); - assert_eq!(voters.len(), num_voters); - } - - get_npos_targets { - // number of validator intention. - let v in (MaxValidators::::get() / 2) .. MaxValidators::::get(); - // number of nominator intention. - let n = MaxNominators::::get(); - - let _ = create_validators_with_nominators_for_era::( - v, n, T::MaxNominations::get() as usize, false, None - )?; - }: { - let targets = >::get_npos_targets(None); - assert_eq!(targets.len() as u32, v); - } - - set_staking_configs_all_set { - }: set_staking_configs( - RawOrigin::Root, - ConfigOp::Set(BalanceOf::::max_value()), - ConfigOp::Set(BalanceOf::::max_value()), - ConfigOp::Set(u32::MAX), - ConfigOp::Set(u32::MAX), - ConfigOp::Set(Percent::max_value()), - ConfigOp::Set(Perbill::max_value()) - ) verify { - assert_eq!(MinNominatorBond::::get(), BalanceOf::::max_value()); - assert_eq!(MinValidatorBond::::get(), BalanceOf::::max_value()); - assert_eq!(MaxNominatorsCount::::get(), Some(u32::MAX)); - assert_eq!(MaxValidatorsCount::::get(), Some(u32::MAX)); - assert_eq!(ChillThreshold::::get(), Some(Percent::from_percent(100))); - assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); - } - - set_staking_configs_all_remove { - }: set_staking_configs( - RawOrigin::Root, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove - ) verify { - assert!(!MinNominatorBond::::exists()); - assert!(!MinValidatorBond::::exists()); - assert!(!MaxNominatorsCount::::exists()); - assert!(!MaxValidatorsCount::::exists()); - assert!(!ChillThreshold::::exists()); - assert!(!MinCommission::::exists()); - } - - chill_other { - // clean up any existing state. - clear_validators_and_nominators::(); - - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); - - // setup a worst case list scenario. Note that we don't care about the setup of the - // destination position because we are doing a removal from the list but no insert. - let scenario = ListScenario::::new(origin_weight, true)?; - let controller = scenario.origin_controller1.clone(); - let stash = scenario.origin_stash1; - assert!(T::VoterList::contains(&stash)); - - Staking::::set_staking_configs( - RawOrigin::Root.into(), - ConfigOp::Set(BalanceOf::::max_value()), - ConfigOp::Set(BalanceOf::::max_value()), - ConfigOp::Set(0), - ConfigOp::Set(0), - ConfigOp::Set(Percent::from_percent(0)), - ConfigOp::Set(Zero::zero()), - )?; - - let caller = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), controller) - verify { - assert!(!T::VoterList::contains(&stash)); - } - - force_apply_min_commission { - // Clean up any existing state - clear_validators_and_nominators::(); - - // Create a validator with a commission of 50% - let (stash, controller) = - create_stash_controller::(1, 1, RewardDestination::Staked)?; - let validator_prefs = - ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; - - // Sanity check - assert_eq!( - Validators::::get(&stash), - ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() } - ); - - // Set the min commission to 75% - MinCommission::::set(Perbill::from_percent(75)); - let caller = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), stash.clone()) - verify { - // The validators commission has been bumped to 75% - assert_eq!( - Validators::::get(&stash), - ValidatorPrefs { commission: Perbill::from_percent(75), ..Default::default() } - ); - } - - set_min_commission { - let min_commission = Perbill::max_value(); - }: _(RawOrigin::Root, min_commission) - verify { - assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); - } - - impl_benchmark_test_suite!( - Staking, - crate::mock::ExtBuilder::default().has_stakers(true), - crate::mock::Test, - exec_name = build_and_execute - ); -} - -#[cfg(test)] -mod tests { - use frame_support::assert_ok; - - use super::*; - use crate::mock::{Balances, ExtBuilder, RuntimeOrigin, Staking, Test}; - - #[test] - fn create_validators_with_nominators_for_era_works() { - ExtBuilder::default().build_and_execute(|| { - let v = 10; - let n = 100; - - create_validators_with_nominators_for_era::( - v, - n, - ::MaxNominations::get() as usize, - false, - None, - ) - .unwrap(); - - let count_validators = Validators::::iter().count(); - let count_nominators = Nominators::::iter().count(); - - assert_eq!(count_validators, Validators::::count() as usize); - assert_eq!(count_nominators, Nominators::::count() as usize); - - assert_eq!(count_validators, v as usize); - assert_eq!(count_nominators, n as usize); - }); - } - - #[test] - fn create_validator_with_nominators_works() { - ExtBuilder::default().build_and_execute(|| { - let n = 10; - - let (validator_stash, nominators) = create_validator_with_nominators::( - n, - <::MaxNominatorRewardedPerValidator as Get<_>>::get(), - false, - false, - RewardDestination::Staked, - ) - .unwrap(); - - assert_eq!(nominators.len() as u32, n); - - let current_era = CurrentEra::::get().unwrap(); - - let original_free_balance = Balances::free_balance(&validator_stash); - assert_ok!(Staking::payout_stakers( - RuntimeOrigin::signed(1337), - validator_stash, - current_era - )); - let new_free_balance = Balances::free_balance(&validator_stash); - - assert!(original_free_balance < new_free_balance); - }); - } - - #[test] - fn add_slashing_spans_works() { - ExtBuilder::default().build_and_execute(|| { - let n = 10; - - let (validator_stash, _nominators) = create_validator_with_nominators::( - n, - <::MaxNominatorRewardedPerValidator as Get<_>>::get(), - false, - false, - RewardDestination::Staked, - ) - .unwrap(); - - // Add 20 slashing spans - let num_of_slashing_spans = 20; - add_slashing_spans::(&validator_stash, num_of_slashing_spans); - - let slashing_spans = SlashingSpans::::get(&validator_stash).unwrap(); - assert_eq!(slashing_spans.iter().count(), num_of_slashing_spans as usize); - for i in 0..num_of_slashing_spans { - assert!(SpanSlash::::contains_key((&validator_stash, i))); - } - - // Test everything is cleaned up - assert_ok!(Staking::kill_stash(&validator_stash, num_of_slashing_spans)); - assert!(SlashingSpans::::get(&validator_stash).is_none()); - for i in 0..num_of_slashing_spans { - assert!(!SpanSlash::::contains_key((&validator_stash, i))); - } - }); - } - - #[test] - fn test_payout_all() { - ExtBuilder::default().build_and_execute(|| { - let v = 10; - let n = 100; - - let selected_benchmark = SelectedBenchmark::payout_all; - let c = vec![ - (frame_benchmarking::BenchmarkParameter::v, v), - (frame_benchmarking::BenchmarkParameter::n, n), - ]; - let closure_to_benchmark = - >::instance( - &selected_benchmark, - &c, - true, - ) - .unwrap(); - - assert_ok!(closure_to_benchmark()); - }); - } -} diff --git a/pallets/staking/src/inflation.rs b/pallets/staking/src/inflation.rs deleted file mode 100644 index 8b4a85b6c..000000000 --- a/pallets/staking/src/inflation.rs +++ /dev/null @@ -1,108 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! This module expose one function `P_NPoS` (Payout NPoS) or `compute_total_payout` which returns -//! the total payout for the era given the era duration and the staking rate in NPoS. -//! The staking rate in NPoS is the total amount of tokens staked by nominators and validators, -//! divided by the total token supply. - -use sp_runtime::{curve::PiecewiseLinear, traits::AtLeast32BitUnsigned, Perbill}; - -/// The total payout to all validators (and their nominators) per era and maximum payout. -/// -/// Defined as such: -/// `staker-payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / -/// era_per_year` `maximum-payout = max_yearly_inflation * total_tokens / era_per_year` -/// -/// `era_duration` is expressed in millisecond. -pub fn compute_total_payout( - yearly_inflation: &PiecewiseLinear<'static>, - npos_token_staked: N, - total_tokens: N, - era_duration: u64, -) -> (N, N) -where - N: AtLeast32BitUnsigned + Clone, -{ - // Milliseconds per year for the Julian year (365.25 days). - const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; - - let portion = Perbill::from_rational(era_duration as u64, MILLISECONDS_PER_YEAR); - let payout = portion * - yearly_inflation - .calculate_for_fraction_times_denominator(npos_token_staked, total_tokens.clone()); - let maximum = portion * (yearly_inflation.maximum * total_tokens); - (payout, maximum) -} - -#[cfg(test)] -mod test { - use sp_runtime::curve::PiecewiseLinear; - - pallet_staking_reward_curve::build! { - const I_NPOS: PiecewiseLinear<'static> = curve!( - min_inflation: 0_025_000, - max_inflation: 0_100_000, - ideal_stake: 0_500_000, - falloff: 0_050_000, - max_piece_count: 40, - test_precision: 0_005_000, - ); - } - - #[test] - fn npos_curve_is_sensible() { - const YEAR: u64 = 365 * 24 * 60 * 60 * 1000; - - // check maximum inflation. - // not 10_000 due to rounding error. - assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR).1, 9_993); - - // super::I_NPOS.calculate_for_fraction_times_denominator(25, 100) - assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR).0, 2_498); - assert_eq!(super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR).0, 3_248); - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR).0, 6_246); - assert_eq!(super::compute_total_payout(&I_NPOS, 40_000, 100_000u64, YEAR).0, 8_494); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, YEAR).0, 9_993); - assert_eq!(super::compute_total_payout(&I_NPOS, 60_000, 100_000u64, YEAR).0, 4_379); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, YEAR).0, 2_733); - assert_eq!(super::compute_total_payout(&I_NPOS, 95_000, 100_000u64, YEAR).0, 2_513); - assert_eq!(super::compute_total_payout(&I_NPOS, 100_000, 100_000u64, YEAR).0, 2_505); - - const DAY: u64 = 24 * 60 * 60 * 1000; - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, DAY).0, 17); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, DAY).0, 27); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, DAY).0, 7); - - const SIX_HOURS: u64 = 6 * 60 * 60 * 1000; - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, SIX_HOURS).0, 4); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, SIX_HOURS).0, 7); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, SIX_HOURS).0, 2); - - const HOUR: u64 = 60 * 60 * 1000; - assert_eq!( - super::compute_total_payout( - &I_NPOS, - 2_500_000_000_000_000_000_000_000_000u128, - 5_000_000_000_000_000_000_000_000_000u128, - HOUR - ) - .0, - 57_038_500_000_000_000_000_000 - ); - } -} diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs deleted file mode 100644 index 57e4edcc5..000000000 --- a/pallets/staking/src/lib.rs +++ /dev/null @@ -1,958 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Staking Pallet -//! -//! The Staking pallet is used to manage funds at stake by network maintainers. -//! -//! - [`Config`] -//! - [`Call`] -//! - [`Pallet`] -//! -//! ## Overview -//! -//! The Staking pallet is the means by which a set of network maintainers (known as _authorities_ in -//! some contexts and _validators_ in others) are chosen based upon those who voluntarily place -//! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held -//! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging -//! its duties properly. -//! -//! ### Terminology -//! -//! -//! - Staking: The process of locking up funds for some time, placing them at risk of slashing -//! (loss) in order to become a rewarded maintainer of the network. -//! - Validating: The process of running a node to actively maintain the network, either by -//! producing blocks or guaranteeing finality of the chain. -//! - Nominating: The process of placing staked funds behind one or more validators in order to -//! share in any reward, and punishment, they take. -//! - Stash account: The account holding an owner's funds used for staking. -//! - Controller account: The account that controls an owner's funds for staking. -//! - Era: A (whole) number of sessions, which is the period that the validator set (and each -//! validator's active nominator set) is recalculated and where rewards are paid out. -//! - Slash: The punishment of a staker by reducing its funds. -//! -//! ### Goals -//! -//! -//! The staking system in Substrate NPoS is designed to make the following possible: -//! -//! - Stake funds that are controlled by a cold wallet. -//! - Withdraw some, or deposit more, funds without interrupting the role of an entity. -//! - Switch between roles (nominator, validator, idle) with minimal overhead. -//! -//! ### Scenarios -//! -//! #### Staking -//! -//! Almost any interaction with the Staking pallet requires a process of _**bonding**_ (also known -//! as being a _staker_). To become *bonded*, a fund-holding register known as the _stash account_, -//! which holds some or all of the funds that become frozen in place as part of the staking process, -//! is paired with an active **controller** account, which issues instructions on how they shall be -//! used. -//! -//! An account pair can become bonded using the [`bond`](Call::bond) call. -//! -//! Stash accounts can update their associated controller back to the stash account using the -//! [`set_controller`](Call::set_controller) call. -//! -//! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` -//! and `Idle` (defined in [`StakerStatus`]). There are three -//! corresponding instructions to change between roles, namely: -//! [`validate`](Call::validate), -//! [`nominate`](Call::nominate), and [`chill`](Call::chill). -//! -//! #### Validating -//! -//! A **validator** takes the role of either validating blocks or ensuring their finality, -//! maintaining the veracity of the network. A validator should avoid both any sort of malicious -//! misbehavior and going offline. Bonded accounts that state interest in being a validator do NOT -//! get immediately chosen as a validator. Instead, they are declared as a _candidate_ and they -//! _might_ get elected at the _next era_ as a validator. The result of the election is determined -//! by nominators and their votes. -//! -//! An account can become a validator candidate via the -//! [`validate`](Call::validate) call. -//! -//! #### Nomination -//! -//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on -//! a set of validators to be elected. Once interest in nomination is stated by an account, it -//! takes effect at the next election round. The funds in the nominator's stash account indicate the -//! _weight_ of its vote. Both the rewards and any punishment that a validator earns are shared -//! between the validator and its nominators. This rule incentivizes the nominators to NOT vote for -//! the misbehaving/offline validators as much as possible, simply because the nominators will also -//! lose funds if they vote poorly. -//! -//! An account can become a nominator via the [`nominate`](Call::nominate) call. -//! -//! #### Voting -//! -//! Staking is closely related to elections; actual validators are chosen from among all potential -//! validators via election by the potential validators and nominators. To reduce use of the phrase -//! "potential validators and nominators", we often use the term **voters**, who are simply -//! the union of potential validators and nominators. -//! -//! #### Rewards and Slash -//! -//! The **reward and slashing** procedure is the core of the Staking pallet, attempting to _embrace -//! valid behavior_ while _punishing any misbehavior or lack of availability_. -//! -//! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the -//! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the -//! validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] -//! biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each -//! nominator's account. -//! -//! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is -//! determined, a value is deducted from the balance of the validator and all the nominators who -//! voted for this validator (values are deducted from the _stash_ account of the slashed entity). -//! -//! Slashing logic is further described in the documentation of the `slashing` pallet. -//! -//! Similar to slashing, rewards are also shared among a validator and its associated nominators. -//! Yet, the reward funds are not always transferred to the stash account and can be configured. See -//! [Reward Calculation](#reward-calculation) for more details. -//! -//! #### Chilling -//! -//! Finally, any of the roles above can choose to step back temporarily and just chill for a while. -//! This means that if they are a nominator, they will not be considered as voters anymore and if -//! they are validators, they will no longer be a candidate for the next election. -//! -//! An account can step back via the [`chill`](Call::chill) call. -//! -//! ### Session managing -//! -//! The pallet implement the trait `SessionManager`. Which is the only API to query new validator -//! set and allowing these validator set to be rewarded once their era is ended. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! The dispatchable functions of the Staking pallet enable the steps needed for entities to accept -//! and change their role, alongside some helper functions to get/set the metadata of the pallet. -//! -//! ### Public Functions -//! -//! The Staking pallet contains many public storage items and (im)mutable functions. -//! -//! ## Usage -//! -//! ### Example: Rewarding a validator by id. -//! -//! ``` -//! use pallet_staking::{self as staking}; -//! -//! #[frame_support::pallet] -//! pub mod pallet { -//! use super::*; -//! use frame_support::pallet_prelude::*; -//! use frame_system::pallet_prelude::*; -//! -//! #[pallet::pallet] -//! pub struct Pallet(_); -//! -//! #[pallet::config] -//! pub trait Config: frame_system::Config + staking::Config {} -//! -//! #[pallet::call] -//! impl Pallet { -//! /// Reward a validator. -//! #[pallet::weight(0)] -//! pub fn reward_myself(origin: OriginFor) -> DispatchResult { -//! let reported = ensure_signed(origin)?; -//! >::reward_by_ids(vec![(reported, 10)]); -//! Ok(()) -//! } -//! } -//! } -//! # fn main() { } -//! ``` -//! -//! ## Implementation Details -//! -//! ### Era payout -//! -//! The era payout is computed using yearly inflation curve defined at -//! [`Config::EraPayout`] as such: -//! -//! ```nocompile -//! staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year -//! ``` -//! This payout is used to reward stakers as defined in next section -//! -//! ```nocompile -//! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout -//! ``` -//! The remaining reward is send to the configurable end-point -//! [`Config::RewardRemainder`]. -//! -//! ### Reward Calculation -//! -//! Validators and nominators are rewarded at the end of each era. The total reward of an era is -//! calculated using the era duration and the staking rate (the total amount of tokens staked by -//! nominators and validators, divided by the total token supply). It aims to incentivize toward a -//! defined staking rate. The full specification can be found -//! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model). -//! -//! Total reward is split among validators and their nominators depending on the number of points -//! they received during the era. Points are added to a validator using -//! [`reward_by_ids`](Pallet::reward_by_ids). -//! -//! [`Pallet`] implements -//! [`pallet_authorship::EventHandler`] to add reward -//! points to block producer and block producer of referenced uncles. -//! -//! The validator and its nominator split their reward as following: -//! -//! The validator can declare an amount, named [`commission`](ValidatorPrefs::commission), that does -//! not get shared with the nominators at each reward payout through its [`ValidatorPrefs`]. This -//! value gets deducted from the total reward that is paid to the validator and its nominators. The -//! remaining portion is split pro rata among the validator and the top -//! [`Config::MaxNominatorRewardedPerValidator`] nominators that nominated the validator, -//! proportional to the value staked behind the validator (_i.e._ dividing the -//! [`own`](Exposure::own) or [`others`](Exposure::others) by [`total`](Exposure::total) in -//! [`Exposure`]). Note that the pro rata division of rewards uses the total exposure behind the -//! validator, *not* just the exposure of the validator and the top -//! [`Config::MaxNominatorRewardedPerValidator`] nominators. -//! -//! All entities who receive a reward have the option to choose their reward destination through the -//! [`Payee`] storage item (see -//! [`set_payee`](Call::set_payee)), to be one of the following: -//! -//! - Controller account, (obviously) not increasing the staked value. -//! - Stash account, not increasing the staked value. -//! - Stash account, also increasing the staked value. -//! -//! ### Additional Fund Management Operations -//! -//! Any funds already placed into stash can be the target of the following operations: -//! -//! The controller account can free a portion (or all) of the funds using the -//! [`unbond`](Call::unbond) call. Note that the funds are not immediately -//! accessible. Instead, a duration denoted by -//! [`Config::BondingDuration`] (in number of eras) must -//! pass until the funds can actually be removed. Once the `BondingDuration` is over, the -//! [`withdraw_unbonded`](Call::withdraw_unbonded) call can be used to actually -//! withdraw the funds. -//! -//! Note that there is a limitation to the number of fund-chunks that can be scheduled to be -//! unlocked in the future via [`unbond`](Call::unbond). In case this maximum -//! (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful -//! call to `withdraw_unbonded` to remove some of the chunks. -//! -//! ### Election Algorithm -//! -//! The current election algorithm is implemented based on Phragmén. The reference implementation -//! can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). -//! -//! The election algorithm, aside from electing the validators with the most stake value and votes, -//! tries to divide the nominator votes among candidates in an equal manner. To further assure this, -//! an optional post-processing can be applied that iteratively normalizes the nominator staked -//! values until the total difference among votes of a particular nominator are less than a -//! threshold. -//! -//! ## GenesisConfig -//! -//! The Staking pallet depends on the [`GenesisConfig`]. The -//! `GenesisConfig` is optional and allow to set some initial stakers. -//! -//! ## Related Modules -//! -//! - [Balances](../pallet_balances/index.html): Used to manage values at stake. -//! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new -//! validators is stored in the Session pallet's `Validators` at the end of each era. - -#![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "256"] - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; -#[cfg(any(feature = "runtime-benchmarks", test))] -pub mod testing_utils; - -#[cfg(test)] -pub(crate) mod mock; -#[cfg(test)] -mod tests; - -pub mod inflation; -pub mod migrations; -pub mod slashing; -pub mod weights; - -mod pallet; - -use codec::{Decode, Encode, HasCompact, MaxEncodedLen}; -use frame_support::{ - traits::{Currency, Defensive, Get}, - weights::Weight, - BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, -}; -pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap}; -use scale_info::TypeInfo; -use sp_runtime::{ - curve::PiecewiseLinear, - traits::{AtLeast32BitUnsigned, Convert, Saturating, StaticLookup, Zero}, - Perbill, Perquintill, Rounding, RuntimeDebug, -}; -pub use sp_staking::StakerStatus; -use sp_staking::{ - offence::{Offence, OffenceError, ReportOffence}, - EraIndex, OnStakingUpdate, SessionIndex, -}; -use sp_std::{collections::btree_map::BTreeMap, prelude::*}; -pub use weights::WeightInfo; - -pub(crate) const LOG_TARGET: &str = "runtime::staking"; - -// syntactic sugar for logging. -#[macro_export] -macro_rules! log { - ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { - log::$level!( - target: crate::LOG_TARGET, - concat!("[{:?}] 💸 ", $patter), >::block_number() $(, $values)* - ) - }; -} - -/// Maximum number of winners (aka. active validators), as defined in the election provider of this -/// pallet. -pub type MaxWinnersOf = <::ElectionProvider as frame_election_provider_support::ElectionProviderBase>::MaxWinners; - -/// Counter for the number of "reward" points earned by a given validator. -pub type RewardPoint = u32; - -/// The balance type of this pallet. -pub type BalanceOf = ::CurrencyBalance; - -type PositiveImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::PositiveImbalance; -type NegativeImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::NegativeImbalance; - -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - -/// Information regarding the active era (era in used in session). -#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct ActiveEraInfo { - /// Index of era. - pub index: EraIndex, - /// Moment of start expressed as millisecond from `$UNIX_EPOCH`. - /// - /// Start can be none if start hasn't been set for the era yet, - /// Start is set on the first on_finalize of the era to guarantee usage of `Time`. - start: Option, -} - -/// Reward points of an era. Used to split era total payout between validators. -/// -/// This points will be used to reward validators and their respective nominators. -#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct EraRewardPoints { - /// Total number of points. Equals the sum of reward points for each validator. - pub total: RewardPoint, - /// The reward points earned by a given validator. - pub individual: BTreeMap, -} - -impl Default for EraRewardPoints { - fn default() -> Self { - EraRewardPoints { total: Default::default(), individual: BTreeMap::new() } - } -} - -/// A destination account for payment. -#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub enum RewardDestination { - /// Pay into the stash account, increasing the amount at stake accordingly. - Staked, - /// Pay into the stash account, not increasing the amount at stake. - Stash, - /// Pay into the controller account. - Controller, - /// Pay into a specified account. - Account(AccountId), - /// Receive no reward. - None, -} - -impl Default for RewardDestination { - fn default() -> Self { - RewardDestination::Staked - } -} - -/// Preference of what happens regarding validation. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default, MaxEncodedLen)] -pub struct ValidatorPrefs { - /// Reward that validator takes up-front; only the rest is split between themselves and - /// nominators. - #[codec(compact)] - pub commission: Perbill, - /// Whether or not this validator is accepting more nominations. If `true`, then no nominator - /// who is not already nominating this validator may nominate them. By default, validators - /// are accepting nominations. - pub blocked: bool, -} - -/// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct UnlockChunk { - /// Amount of funds to be unlocked. - #[codec(compact)] - value: Balance, - /// Era number at which point it'll be unlocked. - #[codec(compact)] - era: EraIndex, -} - -/// The ledger of a (bonded) stash. -#[derive( - PartialEqNoBound, - EqNoBound, - CloneNoBound, - Encode, - Decode, - RuntimeDebugNoBound, - TypeInfo, - MaxEncodedLen, -)] -#[scale_info(skip_type_params(T))] -pub struct StakingLedger { - /// The stash account whose balance is actually locked and at stake. - pub stash: T::AccountId, - /// The total amount of the stash's balance that we are currently accounting for. - /// It's just `active` plus all the `unlocking` balances. - #[codec(compact)] - pub total: BalanceOf, - /// The total amount of the stash's balance that will be at stake in any forthcoming - /// rounds. - #[codec(compact)] - pub active: BalanceOf, - /// Any balance that is becoming free, which may eventually be transferred out of the stash - /// (assuming it doesn't get slashed first). It is assumed that this will be treated as a first - /// in, first out queue where the new (higher value) eras get pushed on the back. - pub unlocking: BoundedVec>, T::MaxUnlockingChunks>, - /// List of eras for which the stakers behind a validator have claimed rewards. Only updated - /// for validators. - pub claimed_rewards: BoundedVec, -} - -impl StakingLedger { - /// Initializes the default object using the given `validator`. - pub fn default_from(stash: T::AccountId) -> Self { - Self { - stash, - total: Zero::zero(), - active: Zero::zero(), - unlocking: Default::default(), - claimed_rewards: Default::default(), - } - } - - /// Remove entries from `unlocking` that are sufficiently old and reduce the - /// total by the sum of their balances. - fn consolidate_unlocked(self, current_era: EraIndex) -> Self { - let mut total = self.total; - let unlocking: BoundedVec<_, _> = self - .unlocking - .into_iter() - .filter(|chunk| { - if chunk.era > current_era { - true - } else { - total = total.saturating_sub(chunk.value); - false - } - }) - .collect::>() - .try_into() - .expect( - "filtering items from a bounded vec always leaves length less than bounds. qed", - ); - - Self { - stash: self.stash, - total, - active: self.active, - unlocking, - claimed_rewards: self.claimed_rewards, - } - } - - /// Re-bond funds that were scheduled for unlocking. - /// - /// Returns the updated ledger, and the amount actually rebonded. - fn rebond(mut self, value: BalanceOf) -> (Self, BalanceOf) { - let mut unlocking_balance = BalanceOf::::zero(); - - while let Some(last) = self.unlocking.last_mut() { - if unlocking_balance + last.value <= value { - unlocking_balance += last.value; - self.active += last.value; - self.unlocking.pop(); - } else { - let diff = value - unlocking_balance; - - unlocking_balance += diff; - self.active += diff; - last.value -= diff; - } - - if unlocking_balance >= value { - break - } - } - - (self, unlocking_balance) - } - - /// Slash the staker for a given amount of balance. - /// - /// This implements a proportional slashing system, whereby we set our preference to slash as - /// such: - /// - /// - If any unlocking chunks exist that are scheduled to be unlocked at `slash_era + - /// bonding_duration` and onwards, the slash is divided equally between the active ledger and - /// the unlocking chunks. - /// - If no such chunks exist, then only the active balance is slashed. - /// - /// Note that the above is only a *preference*. If for any reason the active ledger, with or - /// without some portion of the unlocking chunks that are more justified to be slashed are not - /// enough, then the slashing will continue and will consume as much of the active and unlocking - /// chunks as needed. - /// - /// This will never slash more than the given amount. If any of the chunks become dusted, the - /// last chunk is slashed slightly less to compensate. Returns the amount of funds actually - /// slashed. - /// - /// `slash_era` is the era in which the slash (which is being enacted now) actually happened. - /// - /// This calls `Config::OnStakingUpdate::on_slash` with information as to how the slash was - /// applied. - pub fn slash( - &mut self, - slash_amount: BalanceOf, - minimum_balance: BalanceOf, - slash_era: EraIndex, - ) -> BalanceOf { - if slash_amount.is_zero() { - return Zero::zero() - } - - use sp_runtime::PerThing as _; - let mut remaining_slash = slash_amount; - let pre_slash_total = self.total; - - // for a `slash_era = x`, any chunk that is scheduled to be unlocked at era `x + 28` - // (assuming 28 is the bonding duration) onwards should be slashed. - let slashable_chunks_start = slash_era + T::BondingDuration::get(); - - // `Some(ratio)` if this is proportional, with `ratio`, `None` otherwise. In both cases, we - // slash first the active chunk, and then `slash_chunks_priority`. - let (maybe_proportional, slash_chunks_priority) = { - if let Some(first_slashable_index) = - self.unlocking.iter().position(|c| c.era >= slashable_chunks_start) - { - // If there exists a chunk who's after the first_slashable_start, then this is a - // proportional slash, because we want to slash active and these chunks - // proportionally. - - // The indices of the first chunk after the slash up through the most recent chunk. - // (The most recent chunk is at greatest from this era) - let affected_indices = first_slashable_index..self.unlocking.len(); - let unbonding_affected_balance = - affected_indices.clone().fold(BalanceOf::::zero(), |sum, i| { - if let Some(chunk) = self.unlocking.get(i).defensive() { - sum.saturating_add(chunk.value) - } else { - sum - } - }); - let affected_balance = self.active.saturating_add(unbonding_affected_balance); - let ratio = Perquintill::from_rational_with_rounding( - slash_amount, - affected_balance, - Rounding::Up, - ) - .unwrap_or_else(|_| Perquintill::one()); - ( - Some(ratio), - affected_indices.chain((0..first_slashable_index).rev()).collect::>(), - ) - } else { - // We just slash from the last chunk to the most recent one, if need be. - (None, (0..self.unlocking.len()).rev().collect::>()) - } - }; - - // Helper to update `target` and the ledgers total after accounting for slashing `target`. - log!( - debug, - "slashing {:?} for era {:?} out of {:?}, priority: {:?}, proportional = {:?}", - slash_amount, - slash_era, - self, - slash_chunks_priority, - maybe_proportional, - ); - - let mut slash_out_of = |target: &mut BalanceOf, slash_remaining: &mut BalanceOf| { - let mut slash_from_target = if let Some(ratio) = maybe_proportional { - ratio.mul_ceil(*target) - } else { - *slash_remaining - } - // this is the total that that the slash target has. We can't slash more than - // this anyhow! - .min(*target) - // this is the total amount that we would have wanted to slash - // non-proportionally, a proportional slash should never exceed this either! - .min(*slash_remaining); - - // slash out from *target exactly `slash_from_target`. - *target = *target - slash_from_target; - if *target < minimum_balance { - // Slash the rest of the target if it's dust. This might cause the last chunk to be - // slightly under-slashed, by at most `MaxUnlockingChunks * ED`, which is not a big - // deal. - slash_from_target = - sp_std::mem::replace(target, Zero::zero()).saturating_add(slash_from_target) - } - - self.total = self.total.saturating_sub(slash_from_target); - *slash_remaining = slash_remaining.saturating_sub(slash_from_target); - }; - - // If this is *not* a proportional slash, the active will always wiped to 0. - slash_out_of(&mut self.active, &mut remaining_slash); - - let mut slashed_unlocking = BTreeMap::<_, _>::new(); - for i in slash_chunks_priority { - if remaining_slash.is_zero() { - break - } - - if let Some(chunk) = self.unlocking.get_mut(i).defensive() { - slash_out_of(&mut chunk.value, &mut remaining_slash); - // write the new slashed value of this chunk to the map. - slashed_unlocking.insert(chunk.era, chunk.value); - } else { - break - } - } - - // clean unlocking chunks that are set to zero. - self.unlocking.retain(|c| !c.value.is_zero()); - - T::EventListeners::on_slash(&self.stash, self.active, &slashed_unlocking); - pre_slash_total.saturating_sub(self.total) - } -} - -/// A record of the nominations made by a specific account. -#[derive( - PartialEqNoBound, EqNoBound, Clone, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, -)] -#[codec(mel_bound())] -#[scale_info(skip_type_params(T))] -pub struct Nominations { - /// The targets of nomination. - pub targets: BoundedVec, - /// The era the nominations were submitted. - /// - /// Except for initial nominations which are considered submitted at era 0. - pub submitted_in: EraIndex, - /// Whether the nominations have been suppressed. This can happen due to slashing of the - /// validators, or other events that might invalidate the nomination. - /// - /// NOTE: this for future proofing and is thus far not used. - pub suppressed: bool, -} - -/// The amount of exposure (to slashing) than an individual nominator has. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct IndividualExposure { - /// The stash account of the nominator in question. - pub who: AccountId, - /// Amount of funds exposed. - #[codec(compact)] - pub value: Balance, -} - -/// A snapshot of the stake backing a single validator in the system. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct Exposure { - /// The total balance backing this validator. - #[codec(compact)] - pub total: Balance, - /// The validator's own stash that is exposed. - #[codec(compact)] - pub own: Balance, - /// The portions of nominators stashes that are exposed. - pub others: Vec>, -} - -impl Default for Exposure { - fn default() -> Self { - Self { total: Default::default(), own: Default::default(), others: vec![] } - } -} - -/// A pending slash record. The value of the slash has been computed but not applied yet, -/// rather deferred for several eras. -#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct UnappliedSlash { - /// The stash ID of the offending validator. - validator: AccountId, - /// The validator's own slash. - own: Balance, - /// All other slashed stakers and amounts. - others: Vec<(AccountId, Balance)>, - /// Reporters of the offence; bounty payout recipients. - reporters: Vec, - /// The amount of payout. - payout: Balance, -} - -impl UnappliedSlash { - /// Initializes the default object using the given `validator`. - pub fn default_from(validator: AccountId) -> Self { - Self { - validator, - own: Zero::zero(), - others: vec![], - reporters: vec![], - payout: Zero::zero(), - } - } -} - -/// Means for interacting with a specialized version of the `session` trait. -/// -/// This is needed because `Staking` sets the `ValidatorIdOf` of the `pallet_session::Config` -pub trait SessionInterface { - /// Disable the validator at the given index, returns `false` if the validator was already - /// disabled or the index is out of bounds. - fn disable_validator(validator_index: u32) -> bool; - /// Get the validators from session. - fn validators() -> Vec; - /// Prune historical session tries up to but not including the given index. - fn prune_historical_up_to(up_to: SessionIndex); -} - -impl SessionInterface<::AccountId> for T -where - T: pallet_session::Config::AccountId>, - T: pallet_session::historical::Config< - FullIdentification = Exposure<::AccountId, BalanceOf>, - FullIdentificationOf = ExposureOf, - >, - T::SessionHandler: pallet_session::SessionHandler<::AccountId>, - T::SessionManager: pallet_session::SessionManager<::AccountId>, - T::ValidatorIdOf: Convert< - ::AccountId, - Option<::AccountId>, - >, -{ - fn disable_validator(validator_index: u32) -> bool { - >::disable_index(validator_index) - } - - fn validators() -> Vec<::AccountId> { - >::validators() - } - - fn prune_historical_up_to(up_to: SessionIndex) { - >::prune_up_to(up_to); - } -} - -impl SessionInterface for () { - fn disable_validator(_: u32) -> bool { - true - } - fn validators() -> Vec { - Vec::new() - } - fn prune_historical_up_to(_: SessionIndex) { - () - } -} - -/// Handler for determining how much of a balance should be paid out on the current era. -pub trait EraPayout { - /// Determine the payout for this era. - /// - /// Returns the amount to be paid to stakers in this era, as well as whatever else should be - /// paid out ("the rest"). - fn era_payout( - total_staked: Balance, - total_issuance: Balance, - era_duration_millis: u64, - ) -> (Balance, Balance); -} - -impl EraPayout for () { - fn era_payout( - _total_staked: Balance, - _total_issuance: Balance, - _era_duration_millis: u64, - ) -> (Balance, Balance) { - (Default::default(), Default::default()) - } -} - -/// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for -/// backwards compatibility. -pub struct ConvertCurve(sp_std::marker::PhantomData); -impl>> - EraPayout for ConvertCurve -{ - fn era_payout( - total_staked: Balance, - total_issuance: Balance, - era_duration_millis: u64, - ) -> (Balance, Balance) { - let (validator_payout, max_payout) = inflation::compute_total_payout( - T::get(), - total_staked, - total_issuance, - // Duration of era; more than u64::MAX is rewarded as u64::MAX. - era_duration_millis, - ); - let rest = max_payout.saturating_sub(validator_payout.clone()); - (validator_payout, rest) - } -} - -/// Mode of era-forcing. -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Encode, - Decode, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - serde::Serialize, - serde::Deserialize, -)] -pub enum Forcing { - /// Not forcing anything - just let whatever happen. - NotForcing, - /// Force a new era, then reset to `NotForcing` as soon as it is done. - /// Note that this will force to trigger an election until a new era is triggered, if the - /// election failed, the next session end will trigger a new election again, until success. - ForceNew, - /// Avoid a new era indefinitely. - ForceNone, - /// Force a new era at the end of all sessions indefinitely. - ForceAlways, -} - -impl Default for Forcing { - fn default() -> Self { - Forcing::NotForcing - } -} - -/// A `Convert` implementation that finds the stash of the given controller account, -/// if any. -pub struct StashOf(sp_std::marker::PhantomData); - -impl Convert> for StashOf { - fn convert(controller: T::AccountId) -> Option { - >::ledger(&controller).map(|l| l.stash) - } -} - -/// A typed conversion from stash account ID to the active exposure of nominators -/// on that account. -/// -/// Active exposure is the exposure of the validator set currently validating, i.e. in -/// `active_era`. It can differ from the latest planned exposure in `current_era`. -pub struct ExposureOf(sp_std::marker::PhantomData); - -impl Convert>>> - for ExposureOf -{ - fn convert(validator: T::AccountId) -> Option>> { - >::active_era() - .map(|active_era| >::eras_stakers(active_era.index, &validator)) - } -} - -/// Filter historical offences out and only allow those from the bonding period. -pub struct FilterHistoricalOffences { - _inner: sp_std::marker::PhantomData<(T, R)>, -} - -impl ReportOffence - for FilterHistoricalOffences, R> -where - T: Config, - R: ReportOffence, - O: Offence, -{ - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { - // Disallow any slashing from before the current bonding period. - let offence_session = offence.session_index(); - let bonded_eras = BondedEras::::get(); - - if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { - R::report_offence(reporters, offence) - } else { - >::deposit_event(Event::::OldSlashingReportDiscarded { - session_index: offence_session, - }); - Ok(()) - } - } - - fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool { - R::is_known_offence(offenders, time_slot) - } -} - -/// Configurations of the benchmarking of the pallet. -pub trait BenchmarkingConfig { - /// The maximum number of validators to use. - type MaxValidators: Get; - /// The maximum number of nominators to use. - type MaxNominators: Get; -} - -/// A mock benchmarking config for pallet-staking. -/// -/// Should only be used for testing. -#[cfg(feature = "std")] -pub struct TestBenchmarkingConfig; - -#[cfg(feature = "std")] -impl BenchmarkingConfig for TestBenchmarkingConfig { - type MaxValidators = frame_support::traits::ConstU32<100>; - type MaxNominators = frame_support::traits::ConstU32<100>; -} diff --git a/pallets/staking/src/migrations.rs b/pallets/staking/src/migrations.rs deleted file mode 100644 index 83a9be204..000000000 --- a/pallets/staking/src/migrations.rs +++ /dev/null @@ -1,503 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and - -//! Storage migrations for the Staking pallet. - -use frame_election_provider_support::SortedListProvider; -#[cfg(feature = "try-runtime")] -use frame_support::ensure; -use frame_support::{ - dispatch::GetStorageVersion, pallet_prelude::ValueQuery, storage_alias, - traits::OnRuntimeUpgrade, -}; -#[cfg(feature = "try-runtime")] -use sp_runtime::TryRuntimeError; - -use super::*; - -/// Used for release versioning upto v12. -/// -/// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -enum ObsoleteReleases { - V1_0_0Ancient, - V2_0_0, - V3_0_0, - V4_0_0, - V5_0_0, // blockable validators. - V6_0_0, // removal of all storage associated with offchain phragmen. - V7_0_0, // keep track of number of nominators / validators in map - V8_0_0, // populate `VoterList`. - V9_0_0, // inject validators into `VoterList` as well. - V10_0_0, // remove `EarliestUnappliedSlash`. - V11_0_0, // Move pallet storage prefix, e.g. BagsList -> VoterBagsList - V12_0_0, // remove `HistoryDepth`. -} - -impl Default for ObsoleteReleases { - fn default() -> Self { - ObsoleteReleases::V12_0_0 - } -} - -/// Alias to the old storage item used for release versioning. Obsolete since v13. -#[storage_alias] -type StorageVersion = StorageValue, ObsoleteReleases, ValueQuery>; - -pub mod v13 { - use super::*; - - pub struct MigrateToV13(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV13 { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V12_0_0, - "Required v12 before upgrading to v13" - ); - - Ok(Default::default()) - } - - fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); - let onchain = StorageVersion::::get(); - - if current == 13 && onchain == ObsoleteReleases::V12_0_0 { - StorageVersion::::kill(); - current.put::>(); - - log!(info, "v13 applied successfully"); - T::DbWeight::get().reads_writes(1, 2) - } else { - log!(warn, "Skipping v13, should be removed"); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { - frame_support::ensure!( - Pallet::::on_chain_storage_version() == 13, - "v13 not applied" - ); - - frame_support::ensure!( - !StorageVersion::::exists(), - "Storage version not migrated correctly" - ); - - Ok(()) - } - } -} - -pub mod v12 { - use frame_support::{pallet_prelude::ValueQuery, storage_alias}; - - use super::*; - - #[storage_alias] - type HistoryDepth = StorageValue, u32, ValueQuery>; - - /// Clean up `HistoryDepth` from storage. - /// - /// We will be depending on the configurable value of `HistoryDepth` post - /// this release. - pub struct MigrateToV12(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV12 { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V11_0_0, - "Expected v11 before upgrading to v12" - ); - - if HistoryDepth::::exists() { - frame_support::ensure!( - T::HistoryDepth::get() == HistoryDepth::::get(), - "Provided value of HistoryDepth should be same as the existing storage value" - ); - } else { - log::info!("No HistoryDepth in storage; nothing to remove"); - } - - Ok(Default::default()) - } - - fn on_runtime_upgrade() -> frame_support::weights::Weight { - if StorageVersion::::get() == ObsoleteReleases::V11_0_0 { - HistoryDepth::::kill(); - StorageVersion::::put(ObsoleteReleases::V12_0_0); - - log!(info, "v12 applied successfully"); - T::DbWeight::get().reads_writes(1, 2) - } else { - log!(warn, "Skipping v12, should be removed"); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V12_0_0, - "v12 not applied" - ); - Ok(()) - } - } -} - -pub mod v11 { - use frame_support::{ - storage::migration::move_pallet, - traits::{GetStorageVersion, PalletInfoAccess}, - }; - #[cfg(feature = "try-runtime")] - use sp_io::hashing::twox_128; - - use super::*; - - pub struct MigrateToV11(sp_std::marker::PhantomData<(T, P, N)>); - impl> OnRuntimeUpgrade - for MigrateToV11 - { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V10_0_0, - "must upgrade linearly" - ); - let old_pallet_prefix = twox_128(N::get().as_bytes()); - - frame_support::ensure!( - sp_io::storage::next_key(&old_pallet_prefix).is_some(), - "no data for the old pallet name has been detected" - ); - - Ok(Default::default()) - } - - /// Migrate the entire storage of this pallet to a new prefix. - /// - /// Since Cere is already using VoterList, we just need to remove the old prefix - /// - /// This new prefix must be the same as the one set in construct_runtime. For safety, use - /// `PalletInfo` to get it, as: - /// `::PalletInfo::name::`. - /// - /// The migration will look into the storage version in order to avoid triggering a - /// migration on an up to date storage. - fn on_runtime_upgrade() -> Weight { - if StorageVersion::::get() == ObsoleteReleases::V10_0_0 { - log!(info, "removing {}", N::get()); - - let _ = frame_support::storage::migration::clear_storage_prefix( - N::get().as_bytes(), - &[], - &[], - None, - None, - ); - - StorageVersion::::put(ObsoleteReleases::V11_0_0); - - ::BlockWeights::get().max_block - } else { - log!(warn, "v11::migrate should be removed."); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V11_0_0, - "wrong version after the upgrade" - ); - - let old_pallet_name = N::get(); - let new_pallet_name =

::name(); - - // skip storage prefix checks for the same pallet names - if new_pallet_name == old_pallet_name { - return Ok(()) - } - - let old_pallet_prefix = twox_128(N::get().as_bytes()); - frame_support::ensure!( - sp_io::storage::next_key(&old_pallet_prefix).is_none(), - "old pallet data hasn't been removed" - ); - - let new_pallet_name =

::name(); - let new_pallet_prefix = twox_128(new_pallet_name.as_bytes()); - frame_support::ensure!( - sp_io::storage::next_key(&new_pallet_prefix).is_some(), - "new pallet data hasn't been created" - ); - - Ok(()) - } - } -} - -pub mod v10 { - use frame_support::storage_alias; - - use super::*; - - #[storage_alias] - type EarliestUnappliedSlash = StorageValue, EraIndex>; - - /// Apply any pending slashes that where queued. - /// - /// That means we might slash someone a bit too early, but we will definitely - /// won't forget to slash them. The cap of 512 is somewhat randomly taken to - /// prevent us from iterating over an arbitrary large number of keys `on_runtime_upgrade`. - pub struct MigrateToV10(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV10 { - fn on_runtime_upgrade() -> frame_support::weights::Weight { - if StorageVersion::::get() == ObsoleteReleases::V9_0_0 { - let pending_slashes = UnappliedSlashes::::iter().take(512); - for (era, slashes) in pending_slashes { - for slash in slashes { - // in the old slashing scheme, the slash era was the key at which we read - // from `UnappliedSlashes`. - log!(warn, "prematurely applying a slash ({:?}) for era {:?}", slash, era); - slashing::apply_slash::(slash, era); - } - } - - EarliestUnappliedSlash::::kill(); - StorageVersion::::put(ObsoleteReleases::V10_0_0); - - log!(info, "MigrateToV10 executed successfully"); - T::DbWeight::get().reads_writes(1, 1) - } else { - log!(warn, "MigrateToV10 should be removed."); - T::DbWeight::get().reads(1) - } - } - } -} - -pub mod v9 { - #[cfg(feature = "try-runtime")] - use frame_support::codec::{Decode, Encode}; - #[cfg(feature = "try-runtime")] - use sp_std::vec::Vec; - - use super::*; - - /// Migration implementation that injects all validators into sorted list. - /// - /// Skip the migration and update only StorageVersion since Cere already has all - /// validators in the list - /// - /// This is only useful for chains that started their `VoterList` just based on nominators. - pub struct InjectValidatorsIntoVoterList(sp_std::marker::PhantomData); - impl OnRuntimeUpgrade for InjectValidatorsIntoVoterList { - fn on_runtime_upgrade() -> Weight { - if StorageVersion::::get() == ObsoleteReleases::V8_0_0 { - log!(info, "migrating staking to ObsoleteReleases::V9_0_0"); - - StorageVersion::::put(ObsoleteReleases::V9_0_0); - T::DbWeight::get().reads_writes(1, 1) - } else { - log!( - warn, - "InjectValidatorsIntoVoterList being executed on the wrong storage \ - version, expected ObsoleteReleases::V8_0_0" - ); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V8_0_0, - "must upgrade linearly" - ); - - let prev_count = T::VoterList::count(); - Ok(prev_count.encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { - let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( - "the state parameter should be something that was generated by pre_upgrade", - ); - let post_count = T::VoterList::count(); - ensure!( - post_count == prev_count, - "`VoterList` count after the migration must equal to the sum of \ - previous count since we just skipping the migration" - ); - - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V9_0_0, - "must upgrade" - ); - Ok(()) - } - } -} - -pub mod v8 { - use frame_election_provider_support::SortedListProvider; - use frame_support::traits::Get; - - use super::*; - use crate::{Config, Nominators, Pallet, Weight}; - - #[cfg(feature = "try-runtime")] - pub fn pre_migrate() -> Result<(), &'static str> { - frame_support::ensure!( - StorageVersion::::get() == ObsoleteReleases::V7_0_0, - "must upgrade linearly" - ); - - crate::log!(info, "👜 staking bags-list migration passes PRE migrate checks ✅",); - Ok(()) - } - - /// Migration to sorted `VoterList`. - pub fn migrate() -> Weight { - if StorageVersion::::get() == ObsoleteReleases::V7_0_0 { - crate::log!(info, "migrating staking to ObsoleteReleases::V8_0_0"); - - let migrated = T::VoterList::unsafe_regenerate( - Nominators::::iter().map(|(id, _)| id), - Pallet::::weight_of_fn(), - ); - - StorageVersion::::put(ObsoleteReleases::V8_0_0); - crate::log!( - info, - "👜 completed staking migration to ObsoleteReleases::V8_0_0 with {} voters migrated", - migrated, - ); - - T::BlockWeights::get().max_block - } else { - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - pub fn post_migrate() -> Result<(), &'static str> { - T::VoterList::try_state().map_err(|_| "VoterList is not in a sane state.")?; - crate::log!(info, "👜 staking bags-list migration passes POST migrate checks ✅",); - Ok(()) - } -} - -pub mod v7 { - use frame_support::storage_alias; - - use super::*; - - #[storage_alias] - type CounterForValidators = StorageValue, u32>; - #[storage_alias] - type CounterForNominators = StorageValue, u32>; - - pub fn pre_migrate() -> Result<(), &'static str> { - assert!( - CounterForValidators::::get().unwrap().is_zero(), - "CounterForValidators already set." - ); - assert!( - CounterForNominators::::get().unwrap().is_zero(), - "CounterForNominators already set." - ); - assert!(Validators::::count().is_zero(), "Validators already set."); - assert!(Nominators::::count().is_zero(), "Nominators already set."); - assert!(StorageVersion::::get() == ObsoleteReleases::V6_0_0); - Ok(()) - } - - pub fn migrate() -> Weight { - log!(info, "Migrating staking to ObsoleteReleases::V7_0_0"); - let validator_count = Validators::::iter().count() as u32; - let nominator_count = Nominators::::iter().count() as u32; - - CounterForValidators::::put(validator_count); - CounterForNominators::::put(nominator_count); - - StorageVersion::::put(ObsoleteReleases::V7_0_0); - log!(info, "Completed staking migration to ObsoleteReleases::V7_0_0"); - - T::DbWeight::get().reads_writes(validator_count.saturating_add(nominator_count).into(), 2) - } -} - -pub mod v6 { - use frame_support::{storage_alias, traits::Get, weights::Weight}; - - use super::*; - - // NOTE: value type doesn't matter, we just set it to () here. - #[storage_alias] - type SnapshotValidators = StorageValue, ()>; - #[storage_alias] - type SnapshotNominators = StorageValue, ()>; - #[storage_alias] - type QueuedElected = StorageValue, ()>; - #[storage_alias] - type QueuedScore = StorageValue, ()>; - #[storage_alias] - type EraElectionStatus = StorageValue, ()>; - #[storage_alias] - type IsCurrentSessionFinal = StorageValue, ()>; - - /// check to execute prior to migration. - pub fn pre_migrate() -> Result<(), &'static str> { - // these may or may not exist. - log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::::exists()); - log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::::exists()); - log!(info, "QueuedElected.exits()? {:?}", QueuedElected::::exists()); - log!(info, "QueuedScore.exits()? {:?}", QueuedScore::::exists()); - // these must exist. - assert!( - IsCurrentSessionFinal::::exists(), - "IsCurrentSessionFinal storage item not found!" - ); - assert!(EraElectionStatus::::exists(), "EraElectionStatus storage item not found!"); - Ok(()) - } - - /// Migrate storage to v6. - pub fn migrate() -> Weight { - log!(info, "Migrating staking to ObsoleteReleases::V6_0_0"); - - SnapshotValidators::::kill(); - SnapshotNominators::::kill(); - QueuedElected::::kill(); - QueuedScore::::kill(); - EraElectionStatus::::kill(); - IsCurrentSessionFinal::::kill(); - - StorageVersion::::put(ObsoleteReleases::V6_0_0); - - log!(info, "Done."); - T::DbWeight::get().writes(6 + 1) - } -} diff --git a/pallets/staking/src/mock.rs b/pallets/staking/src/mock.rs deleted file mode 100644 index d9023c9a2..000000000 --- a/pallets/staking/src/mock.rs +++ /dev/null @@ -1,808 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Test utilities - -use frame_election_provider_support::{onchain, SequentialPhragmen, VoteWeight}; -use frame_support::{ - assert_ok, ord_parameter_types, parameter_types, - traits::{ - ConstU32, ConstU64, Currency, EitherOfDiverse, FindAuthor, Get, Hooks, Imbalance, - OnUnbalanced, OneSessionHandler, - }, - weights::constants::RocksDbWeight, -}; -use frame_system::{EnsureRoot, EnsureSignedBy}; -use sp_core::H256; -use sp_io; -use sp_runtime::{ - curve::PiecewiseLinear, - testing::UintAuthorityId, - traits::{IdentityLookup, Zero}, - BuildStorage, -}; -use sp_staking::offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}; - -use crate::{self as pallet_staking, *}; - -pub const INIT_TIMESTAMP: u64 = 30_000; -pub const BLOCK_TIME: u64 = 1000; - -/// The AccountId alias in this test module. -pub(crate) type AccountId = u64; -pub(crate) type Nonce = u64; -pub(crate) type BlockNumber = u64; -pub(crate) type Balance = u128; - -/// Another session handler struct to test on_disabled. -pub struct OtherSessionHandler; -impl OneSessionHandler for OtherSessionHandler { - type Key = UintAuthorityId; - - fn on_genesis_session<'a, I: 'a>(_: I) - where - I: Iterator, - AccountId: 'a, - { - } - - fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I) - where - I: Iterator, - AccountId: 'a, - { - } - - fn on_disabled(_validator_index: u32) {} -} - -impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { - type Public = UintAuthorityId; -} - -pub fn is_disabled(controller: AccountId) -> bool { - let stash = Staking::ledger(&controller).unwrap().stash; - let validator_index = match Session::validators().iter().position(|v| *v == stash) { - Some(index) => index as u32, - None => return false, - }; - - Session::disabled_validators().contains(&validator_index) -} - -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system, - Authorship: pallet_authorship, - Timestamp: pallet_timestamp, - Balances: pallet_balances, - Staking: pallet_staking, - Session: pallet_session, - Historical: pallet_session::historical, - VoterBagsList: pallet_bags_list::, - } -); - -/// Author of block is always 11 -pub struct Author11; -impl FindAuthor for Author11 { - fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, - { - Some(11) - } -} - -parameter_types! { - pub static SessionsPerEra: SessionIndex = 3; - pub static ExistentialDeposit: Balance = 1; - pub static SlashDeferDuration: EraIndex = 0; - pub static Period: BlockNumber = 5; - pub static Offset: BlockNumber = 0; -} - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type Nonce = Nonce; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = frame_support::traits::ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} -impl pallet_balances::Config for Test { - type MaxLocks = frame_support::traits::ConstU32<1024>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = (); - type MaxHolds = (); -} - -sp_runtime::impl_opaque_keys! { - pub struct SessionKeys { - pub other: OtherSessionHandler, - } -} -impl pallet_session::Config for Test { - type SessionManager = pallet_session::historical::NoteHistoricalRoot; - type Keys = SessionKeys; - type ShouldEndSession = pallet_session::PeriodicSessions; - type SessionHandler = (OtherSessionHandler,); - type RuntimeEvent = RuntimeEvent; - type ValidatorId = AccountId; - type ValidatorIdOf = crate::StashOf; - type NextSessionRotation = pallet_session::PeriodicSessions; - type WeightInfo = (); -} - -impl pallet_session::historical::Config for Test { - type FullIdentification = crate::Exposure; - type FullIdentificationOf = crate::ExposureOf; -} -impl pallet_authorship::Config for Test { - type FindAuthor = Author11; - type EventHandler = Pallet; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); -} - -pallet_staking_reward_curve::build! { - const I_NPOS: PiecewiseLinear<'static> = curve!( - min_inflation: 0_025_000, - max_inflation: 0_100_000, - ideal_stake: 0_500_000, - falloff: 0_050_000, - max_piece_count: 40, - test_precision: 0_005_000, - ); -} -parameter_types! { - pub const BondingDuration: EraIndex = 3; - pub const RewardCurve: &'static PiecewiseLinear<'static> = &I_NPOS; - pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(75); -} - -parameter_types! { - pub static RewardRemainderUnbalanced: u128 = 0; -} - -pub struct RewardRemainderMock; - -impl OnUnbalanced> for RewardRemainderMock { - fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { - RewardRemainderUnbalanced::mutate(|v| { - *v += amount.peek(); - }); - drop(amount); - } -} - -const THRESHOLDS: [sp_npos_elections::VoteWeight; 9] = - [10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; - -parameter_types! { - pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; - pub static MaxNominations: u32 = 16; - pub static HistoryDepth: u32 = 80; - pub static MaxUnlockingChunks: u32 = 32; - pub static RewardOnUnbalanceWasCalled: bool = false; - pub static MaxWinners: u32 = 100; -} - -type VoterBagsListInstance = pallet_bags_list::Instance1; -impl pallet_bags_list::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - // Staking is the source of truth for voter bags list, since they are not kept up to date. - type ScoreProvider = Staking; - type BagThresholds = BagThresholds; - type Score = VoteWeight; -} - -pub struct OnChainSeqPhragmen; -impl onchain::Config for OnChainSeqPhragmen { - type System = Test; - type Solver = SequentialPhragmen; - type DataProvider = Staking; - type WeightInfo = (); - type MaxWinners = MaxWinners; - type VotersBound = ConstU32<{ u32::MAX }>; - type TargetsBound = ConstU32<{ u32::MAX }>; -} - -pub struct MockReward {} -impl OnUnbalanced> for MockReward { - fn on_unbalanced(_: PositiveImbalanceOf) { - RewardOnUnbalanceWasCalled::set(true); - } -} - -parameter_types! { - pub static LedgerSlashPerEra: - (BalanceOf, BTreeMap>) = - (Zero::zero(), BTreeMap::new()); -} - -pub struct EventListenerMock; -impl OnStakingUpdate for EventListenerMock { - fn on_slash( - _pool_account: &AccountId, - slashed_bonded: Balance, - slashed_chunks: &BTreeMap, - ) { - LedgerSlashPerEra::set((slashed_bonded, slashed_chunks.clone())); - } -} - -impl crate::pallet::pallet::Config for Test { - type MaxNominations = MaxNominations; - type Currency = Balances; - type CurrencyBalance = ::Balance; - type UnixTime = Timestamp; - type CurrencyToVote = (); - type RewardRemainder = RewardRemainderMock; - type RuntimeEvent = RuntimeEvent; - type Slash = (); - type Reward = MockReward; - type SessionsPerEra = SessionsPerEra; - type SlashDeferDuration = SlashDeferDuration; - type AdminOrigin = EnsureOneOrRoot; - type BondingDuration = BondingDuration; - type SessionInterface = Self; - type EraPayout = ConvertCurve; - type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; - type OffendingValidatorsThreshold = OffendingValidatorsThreshold; - type ElectionProvider = onchain::OnChainExecution; - type GenesisElectionProvider = Self::ElectionProvider; - // NOTE: consider a macro and use `UseNominatorsAndValidatorsMap` as well. - type VoterList = VoterBagsList; - type TargetList = UseValidatorsMap; - type MaxUnlockingChunks = MaxUnlockingChunks; - type HistoryDepth = HistoryDepth; - type EventListeners = EventListenerMock; - type BenchmarkingConfig = TestBenchmarkingConfig; - type WeightInfo = (); -} - -pub(crate) type StakingCall = crate::Call; -pub(crate) type TestCall = ::RuntimeCall; - -pub struct ExtBuilder { - nominate: bool, - validator_count: u32, - minimum_validator_count: u32, - invulnerables: Vec, - has_stakers: bool, - initialize_first_session: bool, - pub min_nominator_bond: Balance, - min_validator_bond: Balance, - balance_factor: Balance, - status: BTreeMap>, - stakes: BTreeMap, - stakers: Vec<(AccountId, AccountId, Balance, StakerStatus)>, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - nominate: true, - validator_count: 2, - minimum_validator_count: 0, - balance_factor: 1, - invulnerables: vec![], - has_stakers: true, - initialize_first_session: true, - min_nominator_bond: ExistentialDeposit::get(), - min_validator_bond: ExistentialDeposit::get(), - status: Default::default(), - stakes: Default::default(), - stakers: Default::default(), - } - } -} - -impl ExtBuilder { - pub fn existential_deposit(self, existential_deposit: Balance) -> Self { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = existential_deposit); - self - } - pub fn nominate(mut self, nominate: bool) -> Self { - self.nominate = nominate; - self - } - pub fn validator_count(mut self, count: u32) -> Self { - self.validator_count = count; - self - } - pub fn minimum_validator_count(mut self, count: u32) -> Self { - self.minimum_validator_count = count; - self - } - pub fn slash_defer_duration(self, eras: EraIndex) -> Self { - SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = eras); - self - } - pub fn invulnerables(mut self, invulnerables: Vec) -> Self { - self.invulnerables = invulnerables; - self - } - pub fn session_per_era(self, length: SessionIndex) -> Self { - SESSIONS_PER_ERA.with(|v| *v.borrow_mut() = length); - self - } - pub fn period(self, length: BlockNumber) -> Self { - PERIOD.with(|v| *v.borrow_mut() = length); - self - } - pub fn has_stakers(mut self, has: bool) -> Self { - self.has_stakers = has; - self - } - pub fn initialize_first_session(mut self, init: bool) -> Self { - self.initialize_first_session = init; - self - } - pub fn offset(self, offset: BlockNumber) -> Self { - OFFSET.with(|v| *v.borrow_mut() = offset); - self - } - pub fn min_nominator_bond(mut self, amount: Balance) -> Self { - self.min_nominator_bond = amount; - self - } - pub fn min_validator_bond(mut self, amount: Balance) -> Self { - self.min_validator_bond = amount; - self - } - pub fn set_status(mut self, who: AccountId, status: StakerStatus) -> Self { - self.status.insert(who, status); - self - } - pub fn set_stake(mut self, who: AccountId, stake: Balance) -> Self { - self.stakes.insert(who, stake); - self - } - pub fn add_staker( - mut self, - stash: AccountId, - ctrl: AccountId, - stake: Balance, - status: StakerStatus, - ) -> Self { - self.stakers.push((stash, ctrl, stake, status)); - self - } - pub fn balance_factor(mut self, factor: Balance) -> Self { - self.balance_factor = factor; - self - } - fn build(self) -> sp_io::TestExternalities { - sp_tracing::try_init_simple(); - let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - let _ = pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 300 * self.balance_factor), - (4, 400 * self.balance_factor), - // controllers (still used in some tests. Soon to be deprecated). - (10, self.balance_factor), - (20, self.balance_factor), - (30, self.balance_factor), - (40, self.balance_factor), - (50, self.balance_factor), - // stashes - (11, self.balance_factor * 1000), - (21, self.balance_factor * 2000), - (31, self.balance_factor * 2000), - (41, self.balance_factor * 2000), - (51, self.balance_factor * 2000), - // optional nominator - (100, self.balance_factor * 2000), - (101, self.balance_factor * 2000), - // aux accounts - (60, self.balance_factor), - (61, self.balance_factor * 2000), - (70, self.balance_factor), - (71, self.balance_factor * 2000), - (80, self.balance_factor), - (81, self.balance_factor * 2000), - // This allows us to have a total_payout different from 0. - (999, 1_000_000_000_000), - ], - } - .assimilate_storage(&mut storage); - - let mut stakers = vec![]; - if self.has_stakers { - stakers = vec![ - // (stash, ctrl, stake, status) - // these two will be elected in the default test where we elect 2. - (11, 11, self.balance_factor * 1000, StakerStatus::::Validator), - (21, 21, self.balance_factor * 1000, StakerStatus::::Validator), - // a loser validator - (31, 31, self.balance_factor * 500, StakerStatus::::Validator), - // an idle validator - (41, 41, self.balance_factor * 1000, StakerStatus::::Idle), - ]; - // optionally add a nominator - if self.nominate { - stakers.push(( - 101, - 101, - self.balance_factor * 500, - StakerStatus::::Nominator(vec![11, 21]), - )) - } - // replace any of the status if needed. - self.status.into_iter().for_each(|(stash, status)| { - let (_, _, _, ref mut prev_status) = stakers - .iter_mut() - .find(|s| s.0 == stash) - .expect("set_status staker should exist; qed"); - *prev_status = status; - }); - // replaced any of the stakes if needed. - self.stakes.into_iter().for_each(|(stash, stake)| { - let (_, _, ref mut prev_stake, _) = stakers - .iter_mut() - .find(|s| s.0 == stash) - .expect("set_stake staker should exits; qed."); - *prev_stake = stake; - }); - // extend stakers if needed. - stakers.extend(self.stakers) - } - - let _ = pallet_staking::GenesisConfig:: { - stakers: stakers.clone(), - validator_count: self.validator_count, - minimum_validator_count: self.minimum_validator_count, - invulnerables: self.invulnerables, - slash_reward_fraction: Perbill::from_percent(10), - min_nominator_bond: self.min_nominator_bond, - min_validator_bond: self.min_validator_bond, - ..Default::default() - } - .assimilate_storage(&mut storage); - - let _ = pallet_session::GenesisConfig:: { - keys: if self.has_stakers { - // set the keys for the first session. - stakers - .into_iter() - .map(|(id, ..)| (id, id, SessionKeys { other: id.into() })) - .collect() - } else { - // set some dummy validators in genesis. - (0..self.validator_count as u64) - .map(|id| (id, id, SessionKeys { other: id.into() })) - .collect() - }, - } - .assimilate_storage(&mut storage); - - let mut ext = sp_io::TestExternalities::from(storage); - - if self.initialize_first_session { - // We consider all test to start after timestamp is initialized This must be ensured by - // having `timestamp::on_initialize` called before `staking::on_initialize`. Also, if - // session length is 1, then it is already triggered. - ext.execute_with(|| { - System::set_block_number(1); - Session::on_initialize(1); - >::on_initialize(1); - Timestamp::set_timestamp(INIT_TIMESTAMP); - }); - } - - ext - } - pub fn build_and_execute(self, test: impl FnOnce() -> ()) { - sp_tracing::try_init_simple(); - let mut ext = self.build(); - ext.execute_with(test); - ext.execute_with(|| { - Staking::do_try_state(System::block_number()).unwrap(); - }); - } -} - -pub(crate) fn active_era() -> EraIndex { - Staking::active_era().unwrap().index -} - -pub(crate) fn current_era() -> EraIndex { - Staking::current_era().unwrap() -} - -pub(crate) fn bond(who: AccountId, val: Balance) { - let _ = Balances::make_free_balance_be(&who, val); - assert_ok!(Staking::bond(RuntimeOrigin::signed(who), val, RewardDestination::Controller)); -} - -pub(crate) fn bond_validator(who: AccountId, val: Balance) { - bond(who, val); - assert_ok!(Staking::validate(RuntimeOrigin::signed(who), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(who), - SessionKeys { other: who.into() }, - vec![] - )); -} - -pub(crate) fn bond_nominator(who: AccountId, val: Balance, target: Vec) { - bond(who, val); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(who), target)); -} - -/// Progress to the given block, triggering session and era changes as we progress. -/// -/// This will finalize the previous block, initialize up to the given block, essentially simulating -/// a block import/propose process where we first initialize the block, then execute some stuff (not -/// in the function), and then finalize the block. -pub(crate) fn run_to_block(n: BlockNumber) { - Staking::on_finalize(System::block_number()); - for b in (System::block_number() + 1)..=n { - System::set_block_number(b); - Session::on_initialize(b); - >::on_initialize(b); - Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); - if b != n { - Staking::on_finalize(System::block_number()); - } - } -} - -/// Progresses from the current block number (whatever that may be) to the `P * session_index + 1`. -pub(crate) fn start_session(session_index: SessionIndex) { - let end: u64 = if Offset::get().is_zero() { - (session_index as u64) * Period::get() - } else { - Offset::get() + (session_index.saturating_sub(1) as u64) * Period::get() - }; - run_to_block(end); - // session must have progressed properly. - assert_eq!( - Session::current_index(), - session_index, - "current session index = {}, expected = {}", - Session::current_index(), - session_index, - ); -} - -/// Go one session forward. -pub(crate) fn advance_session() { - let current_index = Session::current_index(); - start_session(current_index + 1); -} - -/// Progress until the given era. -pub(crate) fn start_active_era(era_index: EraIndex) { - start_session((era_index * >::get()).into()); - assert_eq!(active_era(), era_index); - // One way or another, current_era must have changed before the active era, so they must match - // at this point. - assert_eq!(current_era(), active_era()); -} - -pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { - let (payout, _rest) = ::EraPayout::era_payout( - Staking::eras_total_stake(active_era()), - Balances::total_issuance(), - duration, - ); - assert!(payout > 0); - payout -} - -pub(crate) fn maximum_payout_for_duration(duration: u64) -> Balance { - let (payout, rest) = ::EraPayout::era_payout( - Staking::eras_total_stake(active_era()), - Balances::total_issuance(), - duration, - ); - payout + rest -} - -/// Time it takes to finish a session. -/// -/// Note, if you see `time_per_session() - BLOCK_TIME`, it is fine. This is because we set the -/// timestamp after on_initialize, so the timestamp is always one block old. -pub(crate) fn time_per_session() -> u64 { - Period::get() * BLOCK_TIME -} - -/// Time it takes to finish an era. -/// -/// Note, if you see `time_per_era() - BLOCK_TIME`, it is fine. This is because we set the -/// timestamp after on_initialize, so the timestamp is always one block old. -pub(crate) fn time_per_era() -> u64 { - time_per_session() * SessionsPerEra::get() as u64 -} - -/// Time that will be calculated for the reward per era. -pub(crate) fn reward_time_per_era() -> u64 { - time_per_era() - BLOCK_TIME -} - -pub(crate) fn reward_all_elected() { - let rewards = ::SessionInterface::validators().into_iter().map(|v| (v, 1)); - - >::reward_by_ids(rewards) -} - -pub(crate) fn validator_controllers() -> Vec { - Session::validators() - .into_iter() - .map(|s| Staking::bonded(&s).expect("no controller for validator")) - .collect() -} - -pub(crate) fn on_offence_in_era( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], - slash_fraction: &[Perbill], - era: EraIndex, - disable_strategy: DisableStrategy, -) { - let bonded_eras = crate::BondedEras::::get(); - for &(bonded_era, start_session) in bonded_eras.iter() { - if bonded_era == era { - let _ = Staking::on_offence(offenders, slash_fraction, start_session, disable_strategy); - return - } else if bonded_era > era { - break - } - } - - if Staking::active_era().unwrap().index == era { - let _ = Staking::on_offence( - offenders, - slash_fraction, - Staking::eras_start_session_index(era).unwrap(), - disable_strategy, - ); - } else { - panic!("cannot slash in era {}", era); - } -} - -pub(crate) fn on_offence_now( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], - slash_fraction: &[Perbill], -) { - let now = Staking::active_era().unwrap().index; - on_offence_in_era(offenders, slash_fraction, now, DisableStrategy::WhenSlashed) -} - -pub(crate) fn add_slash(who: &AccountId) { - on_offence_now( - &[OffenceDetails { - offender: (*who, Staking::eras_stakers(active_era(), *who)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); -} - -/// Make all validator and nominator request their payment -pub(crate) fn make_all_reward_payment(era: EraIndex) { - let validators_with_reward = ErasRewardPoints::::get(era) - .individual - .keys() - .cloned() - .collect::>(); - - // reward validators - for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) { - let ledger = >::get(&validator_controller).unwrap(); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), ledger.stash, era)); - } -} - -#[macro_export] -macro_rules! assert_session_era { - ($session:expr, $era:expr) => { - assert_eq!( - Session::current_index(), - $session, - "wrong session {} != {}", - Session::current_index(), - $session, - ); - assert_eq!( - Staking::current_era().unwrap(), - $era, - "wrong current era {} != {}", - Staking::current_era().unwrap(), - $era, - ); - }; -} - -pub(crate) fn staking_events() -> Vec> { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None }) - .collect() -} - -parameter_types! { - static StakingEventsIndex: usize = 0; -} -ord_parameter_types! { - pub const One: u64 = 1; -} - -type EnsureOneOrRoot = EitherOfDiverse, EnsureSignedBy>; - -pub(crate) fn staking_events_since_last_call() -> Vec> { - let all: Vec<_> = System::events() - .into_iter() - .filter_map(|r| if let RuntimeEvent::Staking(inner) = r.event { Some(inner) } else { None }) - .collect(); - let seen = StakingEventsIndex::get(); - StakingEventsIndex::set(all.len()); - all.into_iter().skip(seen).collect() -} - -pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { - (Balances::free_balance(who), Balances::reserved_balance(who)) -} diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs deleted file mode 100644 index fb5c27b15..000000000 --- a/pallets/staking/src/pallet/impls.rs +++ /dev/null @@ -1,1838 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementations for the Staking FRAME Pallet. - -use frame_election_provider_support::{ - data_provider, BoundedSupportsOf, ElectionDataProvider, ElectionProvider, ScoreProvider, - SortedListProvider, VoteWeight, VoterOf, -}; -#[cfg(feature = "try-runtime")] -use frame_support::ensure; -use frame_support::{ - defensive, - dispatch::WithPostDispatchInfo, - pallet_prelude::*, - traits::{ - Currency, Defensive, DefensiveResult, EstimateNextNewSession, Get, Imbalance, - LockableCurrency, OnUnbalanced, TryCollect, UnixTime, WithdrawReasons, - }, - weights::Weight, -}; -use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; -use pallet_session::historical; -#[cfg(any(test, feature = "try-runtime"))] -use sp_runtime::TryRuntimeError; -use sp_runtime::{ - traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, - Perbill, -}; -use sp_staking::{ - currency_to_vote::CurrencyToVote, - offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, SessionIndex, Stake, StakingInterface, -}; -use sp_std::prelude::*; - -use super::{pallet::*, STAKING_ID}; -use crate::{ - log, slashing, weights::WeightInfo, ActiveEraInfo, BalanceOf, EraPayout, Exposure, ExposureOf, - Forcing, IndividualExposure, MaxWinnersOf, Nominations, PositiveImbalanceOf, RewardDestination, - SessionInterface, StakingLedger, ValidatorPrefs, -}; - -/// The maximum number of iterations that we do whilst iterating over `T::VoterList` in -/// `get_npos_voters`. -/// -/// In most cases, if we want n items, we iterate exactly n times. In rare cases, if a voter is -/// invalid (for any reason) the iteration continues. With this constant, we iterate at most 2 * n -/// times and then give up. -const NPOS_MAX_ITERATIONS_COEFFICIENT: u32 = 2; - -impl Pallet { - /// The total balance that can be slashed from a stash account as of right now. - pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf { - // Weight note: consider making the stake accessible through stash. - Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() - } - - /// Internal impl of [`Self::slashable_balance_of`] that returns [`VoteWeight`]. - pub fn slashable_balance_of_vote_weight( - stash: &T::AccountId, - issuance: BalanceOf, - ) -> VoteWeight { - T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance) - } - - /// Returns a closure around `slashable_balance_of_vote_weight` that can be passed around. - /// - /// This prevents call sites from repeatedly requesting `total_issuance` from backend. But it is - /// important to be only used while the total issuance is not changing. - pub fn weight_of_fn() -> Box VoteWeight> { - // NOTE: changing this to unboxed `impl Fn(..)` return type and the pallet will still - // compile, while some types in mock fail to resolve. - let issuance = T::Currency::total_issuance(); - Box::new(move |who: &T::AccountId| -> VoteWeight { - Self::slashable_balance_of_vote_weight(who, issuance) - }) - } - - /// Same as `weight_of_fn`, but made for one time use. - pub fn weight_of(who: &T::AccountId) -> VoteWeight { - let issuance = T::Currency::total_issuance(); - Self::slashable_balance_of_vote_weight(who, issuance) - } - - pub(super) fn do_withdraw_unbonded( - controller: &T::AccountId, - num_slashing_spans: u32, - ) -> Result { - let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let (stash, old_total) = (ledger.stash.clone(), ledger.total); - if let Some(current_era) = Self::current_era() { - ledger = ledger.consolidate_unlocked(current_era) - } - - let used_weight = - if ledger.unlocking.is_empty() && ledger.active < T::Currency::minimum_balance() { - // This account must have called `unbond()` with some value that caused the active - // portion to fall below existential deposit + will have no more unlocking chunks - // left. We can now safely remove all staking-related information. - Self::kill_stash(&stash, num_slashing_spans)?; - // Remove the lock. - T::Currency::remove_lock(STAKING_ID, &stash); - - T::WeightInfo::withdraw_unbonded_kill(num_slashing_spans) - } else { - // This was the consequence of a partial unbond. just update the ledger and move on. - Self::update_ledger(&controller, &ledger); - - // This is only an update, so we use less overall weight. - T::WeightInfo::withdraw_unbonded_update(num_slashing_spans) - }; - - // `old_total` should never be less than the new total because - // `consolidate_unlocked` strictly subtracts balance. - if ledger.total < old_total { - // Already checked that this won't overflow by entry condition. - let value = old_total - ledger.total; - Self::deposit_event(Event::::Withdrawn { stash, amount: value }); - } - - Ok(used_weight) - } - - pub(super) fn do_payout_stakers( - validator_stash: T::AccountId, - era: EraIndex, - ) -> DispatchResultWithPostInfo { - // Validate input data - let current_era = CurrentEra::::get().ok_or_else(|| { - Error::::InvalidEraToReward - .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) - })?; - let history_depth = T::HistoryDepth::get(); - ensure!( - era <= current_era && era >= current_era.saturating_sub(history_depth), - Error::::InvalidEraToReward - .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) - ); - - // Note: if era has no reward to be claimed, era may be future. better not to update - // `ledger.claimed_rewards` in this case. - let era_payout = >::get(&era).ok_or_else(|| { - Error::::InvalidEraToReward - .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) - })?; - - let controller = Self::bonded(&validator_stash).ok_or_else(|| { - Error::::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) - })?; - let mut ledger = >::get(&controller).ok_or(Error::::NotController)?; - - ledger - .claimed_rewards - .retain(|&x| x >= current_era.saturating_sub(history_depth)); - - match ledger.claimed_rewards.binary_search(&era) { - Ok(_) => - return Err(Error::::AlreadyClaimed - .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))), - Err(pos) => ledger - .claimed_rewards - .try_insert(pos, era) - // Since we retain era entries in `claimed_rewards` only upto - // `HistoryDepth`, following bound is always expected to be - // satisfied. - .defensive_map_err(|_| Error::::BoundNotMet)?, - } - - let exposure = >::get(&era, &ledger.stash); - - // Input data seems good, no errors allowed after this point - - >::insert(&controller, &ledger); - - // Get Era reward points. It has TOTAL and INDIVIDUAL - // Find the fraction of the era reward that belongs to the validator - // Take that fraction of the eras rewards to split to nominator and validator - // - // Then look at the validator, figure out the proportion of their reward - // which goes to them and each of their nominators. - - let era_reward_points = >::get(&era); - let total_reward_points = era_reward_points.total; - let validator_reward_points = era_reward_points - .individual - .get(&ledger.stash) - .copied() - .unwrap_or_else(Zero::zero); - - // Nothing to do if they have no reward points. - if validator_reward_points.is_zero() { - return Ok(Some(T::WeightInfo::payout_stakers_alive_staked(0)).into()) - } - - // This is the fraction of the total reward that the validator and the - // nominators will get. - let validator_total_reward_part = - Perbill::from_rational(validator_reward_points, total_reward_points); - - // This is how much validator + nominators are entitled to. - let validator_total_payout = validator_total_reward_part * era_payout; - - let validator_prefs = Self::eras_validator_prefs(&era, &validator_stash); - // Validator first gets a cut off the top. - let validator_commission = validator_prefs.commission; - let validator_commission_payout = validator_commission * validator_total_payout; - - let validator_leftover_payout = validator_total_payout - validator_commission_payout; - // Now let's calculate how this is split to the validator. - let validator_exposure_part = Perbill::from_rational(exposure.own, exposure.total); - let validator_staking_payout = validator_exposure_part * validator_leftover_payout; - - Self::deposit_event(Event::::PayoutStarted { - era_index: era, - validator_stash: ledger.stash.clone(), - }); - - let mut total_imbalance = PositiveImbalanceOf::::zero(); - // We can now make total validator payout: - if let Some(imbalance) = - Self::make_payout(&ledger.stash, validator_staking_payout + validator_commission_payout) - { - Self::deposit_event(Event::::Rewarded { - stash: ledger.stash, - amount: imbalance.peek(), - }); - total_imbalance.subsume(imbalance); - } - - // Track the number of payout ops to nominators. Note: - // `WeightInfo::payout_stakers_alive_staked` always assumes at least a validator is paid - // out, so we do not need to count their payout op. - let mut nominator_payout_count: u32 = 0; - - // Lets now calculate how this is split to the nominators. - // Reward only the clipped exposures. Note this is not necessarily sorted. - for nominator in exposure.others.iter() { - let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total); - - let nominator_reward: BalanceOf = - nominator_exposure_part * validator_leftover_payout; - // We can now make nominator payout: - if let Some(imbalance) = Self::make_payout(&nominator.who, nominator_reward) { - // Note: this logic does not count payouts for `RewardDestination::None`. - nominator_payout_count += 1; - let e = - Event::::Rewarded { stash: nominator.who.clone(), amount: imbalance.peek() }; - Self::deposit_event(e); - total_imbalance.subsume(imbalance); - } - } - - T::Reward::on_unbalanced(total_imbalance); - debug_assert!(nominator_payout_count <= T::MaxNominatorRewardedPerValidator::get()); - Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into()) - } - - /// Update the ledger for a controller. - /// - /// This will also update the stash lock. - pub(crate) fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger) { - T::Currency::set_lock(STAKING_ID, &ledger.stash, ledger.total, WithdrawReasons::all()); - >::insert(controller, ledger); - } - - /// Chill a stash account. - pub(crate) fn chill_stash(stash: &T::AccountId) { - let chilled_as_validator = Self::do_remove_validator(stash); - let chilled_as_nominator = Self::do_remove_nominator(stash); - if chilled_as_validator || chilled_as_nominator { - Self::deposit_event(Event::::Chilled { stash: stash.clone() }); - } - } - - /// Actually make a payment to a staker. This uses the currency's reward function - /// to pay the right payee for the given staker account. - fn make_payout(stash: &T::AccountId, amount: BalanceOf) -> Option> { - let dest = Self::payee(stash); - match dest { - RewardDestination::Controller => Self::bonded(stash) - .map(|controller| T::Currency::deposit_creating(&controller, amount)), - RewardDestination::Stash => T::Currency::deposit_into_existing(stash, amount).ok(), - RewardDestination::Staked => Self::bonded(stash) - .and_then(|c| Self::ledger(&c).map(|l| (c, l))) - .and_then(|(controller, mut l)| { - l.active += amount; - l.total += amount; - let r = T::Currency::deposit_into_existing(stash, amount).ok(); - Self::update_ledger(&controller, &l); - r - }), - RewardDestination::Account(dest_account) => - Some(T::Currency::deposit_creating(&dest_account, amount)), - RewardDestination::None => None, - } - } - - /// Plan a new session potentially trigger a new era. - fn new_session( - session_index: SessionIndex, - is_genesis: bool, - ) -> Option>> { - if let Some(current_era) = Self::current_era() { - // Initial era has been set. - let current_era_start_session_index = Self::eras_start_session_index(current_era) - .unwrap_or_else(|| { - frame_support::print("Error: start_session_index must be set for current_era"); - 0 - }); - - let era_length = session_index.saturating_sub(current_era_start_session_index); // Must never happen. - - match ForceEra::::get() { - // Will be set to `NotForcing` again if a new era has been triggered. - Forcing::ForceNew => (), - // Short circuit to `try_trigger_new_era`. - Forcing::ForceAlways => (), - // Only go to `try_trigger_new_era` if deadline reached. - Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), - _ => { - // Either `Forcing::ForceNone`, - // or `Forcing::NotForcing if era_length >= T::SessionsPerEra::get()`. - return None - }, - } - - // New era. - let maybe_new_era_validators = Self::try_trigger_new_era(session_index, is_genesis); - if maybe_new_era_validators.is_some() && - matches!(ForceEra::::get(), Forcing::ForceNew) - { - Self::set_force_era(Forcing::NotForcing); - } - - maybe_new_era_validators - } else { - // Set initial era. - log!(debug, "Starting the first era."); - Self::try_trigger_new_era(session_index, is_genesis) - } - } - - /// Start a session potentially starting an era. - fn start_session(start_session: SessionIndex) { - let next_active_era = Self::active_era().map(|e| e.index + 1).unwrap_or(0); - // This is only `Some` when current era has already progressed to the next era, while the - // active era is one behind (i.e. in the *last session of the active era*, or *first session - // of the new current era*, depending on how you look at it). - if let Some(next_active_era_start_session_index) = - Self::eras_start_session_index(next_active_era) - { - if next_active_era_start_session_index == start_session { - Self::start_era(start_session); - } else if next_active_era_start_session_index < start_session { - // This arm should never happen, but better handle it than to stall the staking - // pallet. - frame_support::print("Warning: A session appears to have been skipped."); - Self::start_era(start_session); - } - } - - // disable all offending validators that have been disabled for the whole era - for (index, disabled) in >::get() { - if disabled { - T::SessionInterface::disable_validator(index); - } - } - } - - /// End a session potentially ending an era. - fn end_session(session_index: SessionIndex) { - if let Some(active_era) = Self::active_era() { - if let Some(next_active_era_start_session_index) = - Self::eras_start_session_index(active_era.index + 1) - { - if next_active_era_start_session_index == session_index + 1 { - Self::end_era(active_era, session_index); - } - } - } - } - - /// Start a new era. It does: - /// - /// * Increment `active_era.index`, - /// * reset `active_era.start`, - /// * update `BondedEras` and apply slashes. - fn start_era(start_session: SessionIndex) { - let active_era = ActiveEra::::mutate(|active_era| { - let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0); - *active_era = Some(ActiveEraInfo { - index: new_index, - // Set new active era start in next `on_finalize`. To guarantee usage of `Time` - start: None, - }); - new_index - }); - - let bonding_duration = T::BondingDuration::get(); - - BondedEras::::mutate(|bonded| { - bonded.push((active_era, start_session)); - - if active_era > bonding_duration { - let first_kept = active_era - bonding_duration; - - // Prune out everything that's from before the first-kept index. - let n_to_prune = - bonded.iter().take_while(|&&(era_idx, _)| era_idx < first_kept).count(); - - // Kill slashing metadata. - for (pruned_era, _) in bonded.drain(..n_to_prune) { - slashing::clear_era_metadata::(pruned_era); - } - - if let Some(&(_, first_session)) = bonded.first() { - T::SessionInterface::prune_historical_up_to(first_session); - } - } - }); - - Self::apply_unapplied_slashes(active_era); - } - - /// Compute payout for era. - fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) { - // Note: active_era_start can be None if end era is called during genesis config. - if let Some(active_era_start) = active_era.start { - let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::(); - - let era_duration = (now_as_millis_u64 - active_era_start).saturated_into::(); - let staked = Self::eras_total_stake(&active_era.index); - let issuance = T::Currency::total_issuance(); - let (validator_payout, remainder) = - T::EraPayout::era_payout(staked, issuance, era_duration); - - Self::deposit_event(Event::::EraPaid { - era_index: active_era.index, - validator_payout, - remainder, - }); - - // Set ending era reward. - >::insert(&active_era.index, validator_payout); - T::RewardRemainder::on_unbalanced(T::Currency::issue(remainder)); - - // Clear offending validators. - >::kill(); - } - } - - /// Plan a new era. - /// - /// * Bump the current era storage (which holds the latest planned era). - /// * Store start session index for the new planned era. - /// * Clean old era information. - /// * Store staking information for the new planned era - /// - /// Returns the new validator set. - pub fn trigger_new_era( - start_session_index: SessionIndex, - exposures: BoundedVec< - (T::AccountId, Exposure>), - MaxWinnersOf, - >, - ) -> BoundedVec> { - // Increment or set current era. - let new_planned_era = CurrentEra::::mutate(|s| { - *s = Some(s.map(|s| s + 1).unwrap_or(0)); - s.unwrap() - }); - ErasStartSessionIndex::::insert(&new_planned_era, &start_session_index); - - // Clean old era information. - if let Some(old_era) = new_planned_era.checked_sub(T::HistoryDepth::get() + 1) { - Self::clear_era_information(old_era); - } - - // Set staking information for the new era. - Self::store_stakers_info(exposures, new_planned_era) - } - - /// Potentially plan a new era. - /// - /// Get election result from `T::ElectionProvider`. - /// In case election result has more than [`MinimumValidatorCount`] validator trigger a new era. - /// - /// In case a new era is planned, the new validator set is returned. - pub(crate) fn try_trigger_new_era( - start_session_index: SessionIndex, - is_genesis: bool, - ) -> Option>> { - let election_result: BoundedVec<_, MaxWinnersOf> = if is_genesis { - let result = ::elect().map_err(|e| { - log!(warn, "genesis election provider failed due to {:?}", e); - Self::deposit_event(Event::StakingElectionFailed); - }); - - result - .ok()? - .into_inner() - .try_into() - // both bounds checked in integrity test to be equal - .defensive_unwrap_or_default() - } else { - let result = ::elect().map_err(|e| { - log!(warn, "election provider failed due to {:?}", e); - Self::deposit_event(Event::StakingElectionFailed); - }); - result.ok()? - }; - - let exposures = Self::collect_exposures(election_result); - if (exposures.len() as u32) < Self::minimum_validator_count().max(1) { - // Session will panic if we ever return an empty validator set, thus max(1) ^^. - match CurrentEra::::get() { - Some(current_era) if current_era > 0 => log!( - warn, - "chain does not have enough staking candidates to operate for era {:?} ({} \ - elected, minimum is {})", - CurrentEra::::get().unwrap_or(0), - exposures.len(), - Self::minimum_validator_count(), - ), - None => { - // The initial era is allowed to have no exposures. - // In this case the SessionManager is expected to choose a sensible validator - // set. - // TODO: this should be simplified #8911 - CurrentEra::::put(0); - ErasStartSessionIndex::::insert(&0, &start_session_index); - }, - _ => (), - } - - Self::deposit_event(Event::StakingElectionFailed); - return None - } - - Self::deposit_event(Event::StakersElected); - Some(Self::trigger_new_era(start_session_index, exposures)) - } - - /// Process the output of the election. - /// - /// Store staking information for the new planned era - pub fn store_stakers_info( - exposures: BoundedVec< - (T::AccountId, Exposure>), - MaxWinnersOf, - >, - new_planned_era: EraIndex, - ) -> BoundedVec> { - let elected_stashes: BoundedVec<_, MaxWinnersOf> = exposures - .iter() - .cloned() - .map(|(x, _)| x) - .collect::>() - .try_into() - .expect("since we only map through exposures, size of elected_stashes is always same as exposures; qed"); - - // Populate stakers, exposures, and the snapshot of validator prefs. - let mut total_stake: BalanceOf = Zero::zero(); - exposures.into_iter().for_each(|(stash, exposure)| { - total_stake = total_stake.saturating_add(exposure.total); - >::insert(new_planned_era, &stash, &exposure); - - let mut exposure_clipped = exposure; - let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize; - if exposure_clipped.others.len() > clipped_max_len { - exposure_clipped.others.sort_by(|a, b| a.value.cmp(&b.value).reverse()); - exposure_clipped.others.truncate(clipped_max_len); - } - >::insert(&new_planned_era, &stash, exposure_clipped); - }); - - // Insert current era staking information - >::insert(&new_planned_era, total_stake); - - // Collect the pref of all winners. - for stash in &elected_stashes { - let pref = Self::validators(stash); - >::insert(&new_planned_era, stash, pref); - } - - if new_planned_era > 0 { - log!( - info, - "new validator set of size {:?} has been processed for era {:?}", - elected_stashes.len(), - new_planned_era, - ); - } - - elected_stashes - } - - /// Consume a set of [`BoundedSupports`] from [`sp_npos_elections`] and collect them into a - /// [`Exposure`]. - fn collect_exposures( - supports: BoundedSupportsOf, - ) -> BoundedVec<(T::AccountId, Exposure>), MaxWinnersOf> { - let total_issuance = T::Currency::total_issuance(); - let to_currency = |e: frame_election_provider_support::ExtendedBalance| { - T::CurrencyToVote::to_currency(e, total_issuance) - }; - - supports - .into_iter() - .map(|(validator, support)| { - // Build `struct exposure` from `support`. - let mut others = Vec::with_capacity(support.voters.len()); - let mut own: BalanceOf = Zero::zero(); - let mut total: BalanceOf = Zero::zero(); - support - .voters - .into_iter() - .map(|(nominator, weight)| (nominator, to_currency(weight))) - .for_each(|(nominator, stake)| { - if nominator == validator { - own = own.saturating_add(stake); - } else { - others.push(IndividualExposure { who: nominator, value: stake }); - } - total = total.saturating_add(stake); - }); - - let exposure = Exposure { own, others, total }; - (validator, exposure) - }) - .try_collect() - .expect("we only map through support vector which cannot change the size; qed") - } - - /// Remove all associated data of a stash account from the staking system. - /// - /// Assumes storage is upgraded before calling. - /// - /// This is called: - /// - after a `withdraw_unbonded()` call that frees all of a stash's bonded balance. - /// - through `reap_stash()` if the balance has fallen to zero (through slashing). - pub(crate) fn kill_stash(stash: &T::AccountId, num_slashing_spans: u32) -> DispatchResult { - let controller = >::get(stash).ok_or(Error::::NotStash)?; - - slashing::clear_stash_metadata::(stash, num_slashing_spans)?; - - >::remove(stash); - >::remove(&controller); - - >::remove(stash); - Self::do_remove_validator(stash); - Self::do_remove_nominator(stash); - - frame_system::Pallet::::dec_consumers(stash); - - Ok(()) - } - - /// Clear all era information for given era. - pub(crate) fn clear_era_information(era_index: EraIndex) { - let mut cursor = >::clear_prefix(era_index, u32::MAX, None); - debug_assert!(cursor.maybe_cursor.is_none()); - cursor = >::clear_prefix(era_index, u32::MAX, None); - debug_assert!(cursor.maybe_cursor.is_none()); - cursor = >::clear_prefix(era_index, u32::MAX, None); - debug_assert!(cursor.maybe_cursor.is_none()); - >::remove(era_index); - >::remove(era_index); - >::remove(era_index); - ErasStartSessionIndex::::remove(era_index); - } - - /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. - fn apply_unapplied_slashes(active_era: EraIndex) { - let era_slashes = UnappliedSlashes::::take(&active_era); - log!( - debug, - "found {} slashes scheduled to be executed in era {:?}", - era_slashes.len(), - active_era, - ); - for slash in era_slashes { - let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get()); - slashing::apply_slash::(slash, slash_era); - } - } - - /// Add reward points to validators using their stash account ID. - /// - /// Validators are keyed by stash account ID and must be in the current elected set. - /// - /// For each element in the iterator the given number of points in u32 is added to the - /// validator, thus duplicates are handled. - /// - /// At the end of the era each the total payout will be distributed among validator - /// relatively to their points. - /// - /// COMPLEXITY: Complexity is `number_of_validator_to_reward x current_elected_len`. - pub fn reward_by_ids(validators_points: impl IntoIterator) { - if let Some(active_era) = Self::active_era() { - >::mutate(active_era.index, |era_rewards| { - for (validator, points) in validators_points.into_iter() { - *era_rewards.individual.entry(validator).or_default() += points; - era_rewards.total += points; - } - }); - } - } - - /// Helper to set a new `ForceEra` mode. - pub(crate) fn set_force_era(mode: Forcing) { - log!(info, "Setting force era mode {:?}.", mode); - ForceEra::::put(mode); - Self::deposit_event(Event::::ForceEra { mode }); - } - - /// Ensures that at the end of the current session there will be a new era. - pub(crate) fn ensure_new_era() { - match ForceEra::::get() { - Forcing::ForceAlways | Forcing::ForceNew => (), - _ => Self::set_force_era(Forcing::ForceNew), - } - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn add_era_stakers( - current_era: EraIndex, - stash: T::AccountId, - exposure: Exposure>, - ) { - >::insert(¤t_era, &stash, &exposure); - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn set_slash_reward_fraction(fraction: Perbill) { - SlashRewardFraction::::put(fraction); - } - - /// Get all of the voters that are eligible for the npos election. - /// - /// `maybe_max_len` can imposes a cap on the number of voters returned; - /// - /// Sets `MinimumActiveStake` to the minimum active nominator stake in the returned set of - /// nominators. - /// - /// This function is self-weighing as [`DispatchClass::Mandatory`]. - pub fn get_npos_voters(maybe_max_len: Option) -> Vec> { - let max_allowed_len = { - let all_voter_count = T::VoterList::count() as usize; - maybe_max_len.unwrap_or(all_voter_count).min(all_voter_count) - }; - - let mut all_voters = Vec::<_>::with_capacity(max_allowed_len); - - // cache a few things. - let weight_of = Self::weight_of_fn(); - - let mut voters_seen = 0u32; - let mut validators_taken = 0u32; - let mut nominators_taken = 0u32; - let mut min_active_stake = u64::MAX; - - let mut sorted_voters = T::VoterList::iter(); - while all_voters.len() < max_allowed_len && - voters_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * max_allowed_len as u32) - { - let voter = match sorted_voters.next() { - Some(voter) => { - voters_seen.saturating_inc(); - voter - }, - None => break, - }; - - if let Some(Nominations { targets, .. }) = >::get(&voter) { - let voter_weight = weight_of(&voter); - if !targets.is_empty() { - all_voters.push((voter.clone(), voter_weight, targets)); - nominators_taken.saturating_inc(); - } else { - // Technically should never happen, but not much we can do about it. - } - min_active_stake = - if voter_weight < min_active_stake { voter_weight } else { min_active_stake }; - } else if Validators::::contains_key(&voter) { - // if this voter is a validator: - let self_vote = ( - voter.clone(), - weight_of(&voter), - vec![voter.clone()] - .try_into() - .expect("`MaxVotesPerVoter` must be greater than or equal to 1"), - ); - all_voters.push(self_vote); - validators_taken.saturating_inc(); - } else { - // this can only happen if: 1. there a bug in the bags-list (or whatever is the - // sorted list) logic and the state of the two pallets is no longer compatible, or - // because the nominators is not decodable since they have more nomination than - // `T::MaxNominations`. The latter can rarely happen, and is not really an emergency - // or bug if it does. - log!( - warn, - "DEFENSIVE: invalid item in `VoterList`: {:?}, this nominator probably has too many nominations now", - voter - ); - } - } - - // all_voters should have not re-allocated. - debug_assert!(all_voters.capacity() == max_allowed_len); - - Self::register_weight(T::WeightInfo::get_npos_voters(validators_taken, nominators_taken)); - - let min_active_stake: T::CurrencyBalance = - if all_voters.len() == 0 { 0u64.into() } else { min_active_stake.into() }; - - MinimumActiveStake::::put(min_active_stake); - - log!( - info, - "generated {} npos voters, {} from validators and {} nominators", - all_voters.len(), - validators_taken, - nominators_taken - ); - - all_voters - } - - /// Get the targets for an upcoming npos election. - /// - /// This function is self-weighing as [`DispatchClass::Mandatory`]. - pub fn get_npos_targets(maybe_max_len: Option) -> Vec { - let max_allowed_len = maybe_max_len.unwrap_or_else(|| T::TargetList::count() as usize); - let mut all_targets = Vec::::with_capacity(max_allowed_len); - let mut targets_seen = 0; - - let mut targets_iter = T::TargetList::iter(); - while all_targets.len() < max_allowed_len && - targets_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * max_allowed_len as u32) - { - let target = match targets_iter.next() { - Some(target) => { - targets_seen.saturating_inc(); - target - }, - None => break, - }; - - if Validators::::contains_key(&target) { - all_targets.push(target); - } - } - - Self::register_weight(T::WeightInfo::get_npos_targets(all_targets.len() as u32)); - log!(info, "generated {} npos targets", all_targets.len()); - - all_targets - } - - /// This function will add a nominator to the `Nominators` storage map, - /// and `VoterList`. - /// - /// If the nominator already exists, their nominations will be updated. - /// - /// NOTE: you must ALWAYS use this function to add nominator or update their targets. Any access - /// to `Nominators` or `VoterList` outside of this function is almost certainly - /// wrong. - pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations) { - if !Nominators::::contains_key(who) { - // maybe update sorted list. - let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who)) - .defensive_unwrap_or_default(); - } - Nominators::::insert(who, nominations); - - debug_assert_eq!( - Nominators::::count() + Validators::::count(), - T::VoterList::count() - ); - } - - /// This function will remove a nominator from the `Nominators` storage map, - /// and `VoterList`. - /// - /// Returns true if `who` was removed from `Nominators`, otherwise false. - /// - /// NOTE: you must ALWAYS use this function to remove a nominator from the system. Any access to - /// `Nominators` or `VoterList` outside of this function is almost certainly - /// wrong. - pub fn do_remove_nominator(who: &T::AccountId) -> bool { - let outcome = if Nominators::::contains_key(who) { - Nominators::::remove(who); - let _ = T::VoterList::on_remove(who).defensive(); - true - } else { - false - }; - - debug_assert_eq!( - Nominators::::count() + Validators::::count(), - T::VoterList::count() - ); - - outcome - } - - /// This function will add a validator to the `Validators` storage map. - /// - /// If the validator already exists, their preferences will be updated. - /// - /// NOTE: you must ALWAYS use this function to add a validator to the system. Any access to - /// `Validators` or `VoterList` outside of this function is almost certainly - /// wrong. - pub fn do_add_validator(who: &T::AccountId, prefs: ValidatorPrefs) { - if !Validators::::contains_key(who) { - // maybe update sorted list. - let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who)) - .defensive_unwrap_or_default(); - } - Validators::::insert(who, prefs); - - debug_assert_eq!( - Nominators::::count() + Validators::::count(), - T::VoterList::count() - ); - } - - /// This function will remove a validator from the `Validators` storage map. - /// - /// Returns true if `who` was removed from `Validators`, otherwise false. - /// - /// NOTE: you must ALWAYS use this function to remove a validator from the system. Any access to - /// `Validators` or `VoterList` outside of this function is almost certainly - /// wrong. - pub fn do_remove_validator(who: &T::AccountId) -> bool { - let outcome = if Validators::::contains_key(who) { - Validators::::remove(who); - let _ = T::VoterList::on_remove(who).defensive(); - true - } else { - false - }; - - debug_assert_eq!( - Nominators::::count() + Validators::::count(), - T::VoterList::count() - ); - - outcome - } - - /// Register some amount of weight directly with the system pallet. - /// - /// This is always mandatory weight. - fn register_weight(weight: Weight) { - >::register_extra_weight_unchecked( - weight, - DispatchClass::Mandatory, - ); - } -} - -impl Pallet { - /// Returns the current nominations quota for nominators. - /// - /// Used by the runtime API. - /// Note: for now, this api runtime will always return value of `T::MaxNominations` and thus it - /// is redundant. However, with the upcoming changes in - /// , the nominations quota will change - /// depending on the nominators balance. We're introducing this runtime API now to prepare the - /// community to use it before rolling out PR#12970. - pub fn api_nominations_quota(_balance: BalanceOf) -> u32 { - T::MaxNominations::get() - } -} - -impl ElectionDataProvider for Pallet { - type AccountId = T::AccountId; - type BlockNumber = BlockNumberFor; - type MaxVotesPerVoter = T::MaxNominations; - - fn desired_targets() -> data_provider::Result { - Self::register_weight(T::DbWeight::get().reads(1)); - Ok(Self::validator_count()) - } - - fn electing_voters(maybe_max_len: Option) -> data_provider::Result>> { - // This can never fail -- if `maybe_max_len` is `Some(_)` we handle it. - let voters = Self::get_npos_voters(maybe_max_len); - debug_assert!(maybe_max_len.map_or(true, |max| voters.len() <= max)); - - Ok(voters) - } - - fn electable_targets(maybe_max_len: Option) -> data_provider::Result> { - let target_count = T::TargetList::count(); - - // We can't handle this case yet -- return an error. - if maybe_max_len.map_or(false, |max_len| target_count > max_len as u32) { - return Err("Target snapshot too big") - } - - Ok(Self::get_npos_targets(None)) - } - - fn next_election_prediction(now: BlockNumberFor) -> BlockNumberFor { - let current_era = Self::current_era().unwrap_or(0); - let current_session = Self::current_planned_session(); - let current_era_start_session_index = - Self::eras_start_session_index(current_era).unwrap_or(0); - // Number of session in the current era or the maximum session per era if reached. - let era_progress = current_session - .saturating_sub(current_era_start_session_index) - .min(T::SessionsPerEra::get()); - - let until_this_session_end = T::NextNewSession::estimate_next_new_session(now) - .0 - .unwrap_or_default() - .saturating_sub(now); - - let session_length = T::NextNewSession::average_session_length(); - - let sessions_left: BlockNumberFor = match ForceEra::::get() { - Forcing::ForceNone => Bounded::max_value(), - Forcing::ForceNew | Forcing::ForceAlways => Zero::zero(), - Forcing::NotForcing if era_progress >= T::SessionsPerEra::get() => Zero::zero(), - Forcing::NotForcing => T::SessionsPerEra::get() - .saturating_sub(era_progress) - // One session is computed in this_session_end. - .saturating_sub(1) - .into(), - }; - - now.saturating_add( - until_this_session_end.saturating_add(sessions_left.saturating_mul(session_length)), - ) - } - - #[cfg(feature = "runtime-benchmarks")] - fn add_voter( - voter: T::AccountId, - weight: VoteWeight, - targets: BoundedVec, - ) { - let stake = >::try_from(weight).unwrap_or_else(|_| { - panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.") - }); - >::insert(voter.clone(), voter.clone()); - >::insert( - voter.clone(), - StakingLedger { - stash: voter.clone(), - active: stake, - total: stake, - unlocking: Default::default(), - claimed_rewards: Default::default(), - }, - ); - - Self::do_add_nominator(&voter, Nominations { targets, submitted_in: 0, suppressed: false }); - } - - #[cfg(feature = "runtime-benchmarks")] - fn add_target(target: T::AccountId) { - let stake = MinValidatorBond::::get() * 100u32.into(); - >::insert(target.clone(), target.clone()); - >::insert( - target.clone(), - StakingLedger { - stash: target.clone(), - active: stake, - total: stake, - unlocking: Default::default(), - claimed_rewards: Default::default(), - }, - ); - Self::do_add_validator( - &target, - ValidatorPrefs { commission: Perbill::zero(), blocked: false }, - ); - } - - #[cfg(feature = "runtime-benchmarks")] - fn clear() { - #[allow(deprecated)] - >::remove_all(None); - #[allow(deprecated)] - >::remove_all(None); - #[allow(deprecated)] - >::remove_all(); - #[allow(deprecated)] - >::remove_all(); - - T::VoterList::unsafe_clear(); - } - - #[cfg(feature = "runtime-benchmarks")] - fn put_snapshot( - voters: Vec>, - targets: Vec, - target_stake: Option, - ) { - targets.into_iter().for_each(|v| { - let stake: BalanceOf = target_stake - .and_then(|w| >::try_from(w).ok()) - .unwrap_or_else(|| MinNominatorBond::::get() * 100u32.into()); - >::insert(v.clone(), v.clone()); - >::insert( - v.clone(), - StakingLedger { - stash: v.clone(), - active: stake, - total: stake, - unlocking: Default::default(), - claimed_rewards: Default::default(), - }, - ); - Self::do_add_validator( - &v, - ValidatorPrefs { commission: Perbill::zero(), blocked: false }, - ); - }); - - voters.into_iter().for_each(|(v, s, t)| { - let stake = >::try_from(s).unwrap_or_else(|_| { - panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.") - }); - >::insert(v.clone(), v.clone()); - >::insert( - v.clone(), - StakingLedger { - stash: v.clone(), - active: stake, - total: stake, - unlocking: Default::default(), - claimed_rewards: Default::default(), - }, - ); - Self::do_add_nominator( - &v, - Nominations { targets: t, submitted_in: 0, suppressed: false }, - ); - }); - } -} - -/// In this implementation `new_session(session)` must be called before `end_session(session-1)` -/// i.e. the new session must be planned before the ending of the previous session. -/// -/// Once the first new_session is planned, all session must start and then end in order, though -/// some session can lag in between the newest session planned and the latest session started. -impl pallet_session::SessionManager for Pallet { - fn new_session(new_index: SessionIndex) -> Option> { - log!(trace, "planning new session {}", new_index); - CurrentPlannedSession::::put(new_index); - Self::new_session(new_index, false).map(|v| v.into_inner()) - } - fn new_session_genesis(new_index: SessionIndex) -> Option> { - log!(trace, "planning new session {} at genesis", new_index); - CurrentPlannedSession::::put(new_index); - Self::new_session(new_index, true).map(|v| v.into_inner()) - } - fn start_session(start_index: SessionIndex) { - log!(trace, "starting session {}", start_index); - Self::start_session(start_index) - } - fn end_session(end_index: SessionIndex) { - log!(trace, "ending session {}", end_index); - Self::end_session(end_index) - } -} - -impl historical::SessionManager>> - for Pallet -{ - fn new_session( - new_index: SessionIndex, - ) -> Option>)>> { - >::new_session(new_index).map(|validators| { - let current_era = Self::current_era() - // Must be some as a new era has been created. - .unwrap_or(0); - - validators - .into_iter() - .map(|v| { - let exposure = Self::eras_stakers(current_era, &v); - (v, exposure) - }) - .collect() - }) - } - fn new_session_genesis( - new_index: SessionIndex, - ) -> Option>)>> { - >::new_session_genesis(new_index).map( - |validators| { - let current_era = Self::current_era() - // Must be some as a new era has been created. - .unwrap_or(0); - - validators - .into_iter() - .map(|v| { - let exposure = Self::eras_stakers(current_era, &v); - (v, exposure) - }) - .collect() - }, - ) - } - fn start_session(start_index: SessionIndex) { - >::start_session(start_index) - } - fn end_session(end_index: SessionIndex) { - >::end_session(end_index) - } -} - -/// Add reward points to block authors: -/// * 20 points to the block producer for producing a (non-uncle) block, -impl pallet_authorship::EventHandler> for Pallet -where - T: Config + pallet_authorship::Config + pallet_session::Config, -{ - fn note_author(author: T::AccountId) { - Self::reward_by_ids(vec![(author, 20)]) - } -} - -/// This is intended to be used with `FilterHistoricalOffences`. -impl - OnOffenceHandler, Weight> - for Pallet -where - T: pallet_session::Config::AccountId>, - T: pallet_session::historical::Config< - FullIdentification = Exposure<::AccountId, BalanceOf>, - FullIdentificationOf = ExposureOf, - >, - T::SessionHandler: pallet_session::SessionHandler<::AccountId>, - T::SessionManager: pallet_session::SessionManager<::AccountId>, - T::ValidatorIdOf: Convert< - ::AccountId, - Option<::AccountId>, - >, -{ - fn on_offence( - offenders: &[OffenceDetails< - T::AccountId, - pallet_session::historical::IdentificationTuple, - >], - slash_fraction: &[Perbill], - slash_session: SessionIndex, - disable_strategy: DisableStrategy, - ) -> Weight { - let reward_proportion = SlashRewardFraction::::get(); - let mut consumed_weight = Weight::from_parts(0, 0); - let mut add_db_reads_writes = |reads, writes| { - consumed_weight += T::DbWeight::get().reads_writes(reads, writes); - }; - - let active_era = { - let active_era = Self::active_era(); - add_db_reads_writes(1, 0); - if active_era.is_none() { - // This offence need not be re-submitted. - return consumed_weight - } - active_era.expect("value checked not to be `None`; qed").index - }; - let active_era_start_session_index = Self::eras_start_session_index(active_era) - .unwrap_or_else(|| { - frame_support::print("Error: start_session_index must be set for current_era"); - 0 - }); - add_db_reads_writes(1, 0); - - let window_start = active_era.saturating_sub(T::BondingDuration::get()); - - // Fast path for active-era report - most likely. - // `slash_session` cannot be in a future active era. It must be in `active_era` or before. - let slash_era = if slash_session >= active_era_start_session_index { - active_era - } else { - let eras = BondedEras::::get(); - add_db_reads_writes(1, 0); - - // Reverse because it's more likely to find reports from recent eras. - match eras.iter().rev().find(|&(_, sesh)| sesh <= &slash_session) { - Some((slash_era, _)) => *slash_era, - // Before bonding period. defensive - should be filtered out. - None => return consumed_weight, - } - }; - - add_db_reads_writes(1, 1); - - let slash_defer_duration = T::SlashDeferDuration::get(); - - let invulnerables = Self::invulnerables(); - add_db_reads_writes(1, 0); - - for (details, slash_fraction) in offenders.iter().zip(slash_fraction) { - let (stash, exposure) = &details.offender; - - // Skip if the validator is invulnerable. - if invulnerables.contains(stash) { - continue - } - - let unapplied = slashing::compute_slash::(slashing::SlashParams { - stash, - slash: *slash_fraction, - exposure, - slash_era, - window_start, - now: active_era, - reward_proportion, - disable_strategy, - }); - - Self::deposit_event(Event::::SlashReported { - validator: stash.clone(), - fraction: *slash_fraction, - slash_era, - }); - - if let Some(mut unapplied) = unapplied { - let nominators_len = unapplied.others.len() as u64; - let reporters_len = details.reporters.len() as u64; - - { - let upper_bound = 1 /* Validator/NominatorSlashInEra */ + 2 /* fetch_spans */; - let rw = upper_bound + nominators_len * upper_bound; - add_db_reads_writes(rw, rw); - } - unapplied.reporters = details.reporters.clone(); - if slash_defer_duration == 0 { - // Apply right away. - slashing::apply_slash::(unapplied, slash_era); - { - let slash_cost = (6, 5); - let reward_cost = (2, 2); - add_db_reads_writes( - (1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len, - (1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len, - ); - } - } else { - // Defer to end of some `slash_defer_duration` from now. - log!( - debug, - "deferring slash of {:?}% happened in {:?} (reported in {:?}) to {:?}", - slash_fraction, - slash_era, - active_era, - slash_era + slash_defer_duration + 1, - ); - UnappliedSlashes::::mutate( - slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()), - move |for_later| for_later.push(unapplied), - ); - add_db_reads_writes(1, 1); - } - } else { - add_db_reads_writes(4 /* fetch_spans */, 5 /* kick_out_if_recent */) - } - } - - consumed_weight - } -} - -impl ScoreProvider for Pallet { - type Score = VoteWeight; - - fn score(who: &T::AccountId) -> Self::Score { - Self::weight_of(who) - } - - #[cfg(feature = "runtime-benchmarks")] - fn set_score_of(who: &T::AccountId, weight: Self::Score) { - // this will clearly results in an inconsistent state, but it should not matter for a - // benchmark. - let active: BalanceOf = weight.try_into().map_err(|_| ()).unwrap(); - let mut ledger = match Self::ledger(who) { - None => StakingLedger::default_from(who.clone()), - Some(l) => l, - }; - ledger.active = active; - - >::insert(who, ledger); - >::insert(who, who); - - // also, we play a trick to make sure that a issuance based-`CurrencyToVote` behaves well: - // This will make sure that total issuance is zero, thus the currency to vote will be a 1-1 - // conversion. - let imbalance = T::Currency::burn(T::Currency::total_issuance()); - // kinda ugly, but gets the job done. The fact that this works here is a HUGE exception. - // Don't try this pattern in other places. - sp_std::mem::forget(imbalance); - } -} - -/// A simple sorted list implementation that does not require any additional pallets. Note, this -/// does not provide validators in sorted order. If you desire nominators in a sorted order take -/// a look at [`pallet-bags-list`]. -pub struct UseValidatorsMap(sp_std::marker::PhantomData); -impl SortedListProvider for UseValidatorsMap { - type Score = BalanceOf; - type Error = (); - - /// Returns iterator over voter list, which can have `take` called on it. - fn iter() -> Box> { - Box::new(Validators::::iter().map(|(v, _)| v)) - } - fn iter_from( - start: &T::AccountId, - ) -> Result>, Self::Error> { - if Validators::::contains_key(start) { - let start_key = Validators::::hashed_key_for(start); - Ok(Box::new(Validators::::iter_from(start_key).map(|(n, _)| n))) - } else { - Err(()) - } - } - fn count() -> u32 { - Validators::::count() - } - fn contains(id: &T::AccountId) -> bool { - Validators::::contains_key(id) - } - fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> { - // nothing to do on insert. - Ok(()) - } - fn get_score(id: &T::AccountId) -> Result { - Ok(Pallet::::weight_of(id).into()) - } - fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> { - // nothing to do on update. - Ok(()) - } - fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> { - // nothing to do on remove. - Ok(()) - } - fn unsafe_regenerate( - _: impl IntoIterator, - _: Box Self::Score>, - ) -> u32 { - // nothing to do upon regenerate. - 0 - } - #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), TryRuntimeError> { - Ok(()) - } - - fn unsafe_clear() { - #[allow(deprecated)] - Validators::::remove_all(); - } - - #[cfg(feature = "runtime-benchmarks")] - fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score { - unimplemented!() - } -} - -/// A simple voter list implementation that does not require any additional pallets. Note, this -/// does not provided nominators in sorted ordered. If you desire nominators in a sorted order take -/// a look at [`pallet-bags-list]. -pub struct UseNominatorsAndValidatorsMap(sp_std::marker::PhantomData); -impl SortedListProvider for UseNominatorsAndValidatorsMap { - type Error = (); - type Score = VoteWeight; - - fn iter() -> Box> { - Box::new( - Validators::::iter() - .map(|(v, _)| v) - .chain(Nominators::::iter().map(|(n, _)| n)), - ) - } - fn iter_from( - start: &T::AccountId, - ) -> Result>, Self::Error> { - if Validators::::contains_key(start) { - let start_key = Validators::::hashed_key_for(start); - Ok(Box::new( - Validators::::iter_from(start_key) - .map(|(n, _)| n) - .chain(Nominators::::iter().map(|(x, _)| x)), - )) - } else if Nominators::::contains_key(start) { - let start_key = Nominators::::hashed_key_for(start); - Ok(Box::new(Nominators::::iter_from(start_key).map(|(n, _)| n))) - } else { - Err(()) - } - } - fn count() -> u32 { - Nominators::::count().saturating_add(Validators::::count()) - } - fn contains(id: &T::AccountId) -> bool { - Nominators::::contains_key(id) || Validators::::contains_key(id) - } - fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> { - // nothing to do on insert. - Ok(()) - } - fn get_score(id: &T::AccountId) -> Result { - Ok(Pallet::::weight_of(id)) - } - fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> { - // nothing to do on update. - Ok(()) - } - fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> { - // nothing to do on remove. - Ok(()) - } - fn unsafe_regenerate( - _: impl IntoIterator, - _: Box Self::Score>, - ) -> u32 { - // nothing to do upon regenerate. - 0 - } - - #[cfg(feature = "try-runtime")] - fn try_state() -> Result<(), TryRuntimeError> { - Ok(()) - } - - fn unsafe_clear() { - // NOTE: Caller must ensure this doesn't lead to too many storage accesses. This is a - // condition of SortedListProvider::unsafe_clear. - #[allow(deprecated)] - Nominators::::remove_all(); - #[allow(deprecated)] - Validators::::remove_all(); - } - - #[cfg(feature = "runtime-benchmarks")] - fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score { - unimplemented!() - } -} - -impl StakingInterface for Pallet { - type AccountId = T::AccountId; - type Balance = BalanceOf; - type CurrencyToVote = T::CurrencyToVote; - - fn minimum_nominator_bond() -> Self::Balance { - MinNominatorBond::::get() - } - - fn minimum_validator_bond() -> Self::Balance { - MinValidatorBond::::get() - } - - fn desired_validator_count() -> u32 { - ValidatorCount::::get() - } - - fn election_ongoing() -> bool { - T::ElectionProvider::ongoing() - } - - fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult { - let num_slashing_spans = Self::slashing_spans(&who).map_or(0, |s| s.iter().count() as u32); - Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans) - } - - fn stash_by_ctrl(controller: &Self::AccountId) -> Result { - Self::ledger(controller) - .map(|l| l.stash) - .ok_or(Error::::NotController.into()) - } - - fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { - ErasStakers::::iter_prefix(era).any(|(validator, exposures)| { - validator == *who || exposures.others.iter().any(|i| i.who == *who) - }) - } - - fn bonding_duration() -> EraIndex { - T::BondingDuration::get() - } - - fn current_era() -> EraIndex { - Self::current_era().unwrap_or(Zero::zero()) - } - - fn stake(who: &Self::AccountId) -> Result>, DispatchError> { - Self::bonded(who) - .and_then(|c| Self::ledger(c)) - .map(|l| Stake { total: l.total, active: l.active }) - .ok_or(Error::::NotStash.into()) - } - - fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra) - } - - fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult { - let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; - Self::unbond(RawOrigin::Signed(ctrl).into(), value) - .map_err(|with_post| with_post.error) - .map(|_| ()) - } - - fn chill(who: &Self::AccountId) -> DispatchResult { - // defensive-only: any account bonded via this interface has the stash set as the - // controller, but we have to be sure. Same comment anywhere else that we read this. - let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; - Self::chill(RawOrigin::Signed(ctrl).into()) - } - - fn withdraw_unbonded( - who: Self::AccountId, - num_slashing_spans: u32, - ) -> Result { - let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; - Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans) - .map(|_| !Ledger::::contains_key(&ctrl)) - .map_err(|with_post| with_post.error) - } - - fn bond( - who: &Self::AccountId, - value: Self::Balance, - payee: &Self::AccountId, - ) -> DispatchResult { - Self::bond( - RawOrigin::Signed(who.clone()).into(), - value, - RewardDestination::Account(payee.clone()), - ) - } - - fn nominate(who: &Self::AccountId, targets: Vec) -> DispatchResult { - let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; - let targets = targets.into_iter().map(T::Lookup::unlookup).collect::>(); - Self::nominate(RawOrigin::Signed(ctrl).into(), targets) - } - - fn status( - who: &Self::AccountId, - ) -> Result, DispatchError> { - let is_bonded = Self::bonded(who).is_some(); - if !is_bonded { - return Err(Error::::NotStash.into()) - } - - let is_validator = Validators::::contains_key(&who); - let is_nominator = Nominators::::get(&who); - - use sp_staking::StakerStatus; - match (is_validator, is_nominator.is_some()) { - (false, false) => Ok(StakerStatus::Idle), - (true, false) => Ok(StakerStatus::Validator), - (false, true) => Ok(StakerStatus::Nominator( - is_nominator.expect("is checked above; qed").targets.into_inner(), - )), - (true, true) => { - defensive!("cannot be both validators and nominator"); - Err(Error::::BadState.into()) - }, - } - } - - sp_staking::runtime_benchmarks_enabled! { - fn nominations(who: &Self::AccountId) -> Option> { - Nominators::::get(who).map(|n| n.targets.into_inner()) - } - - fn add_era_stakers( - current_era: &EraIndex, - stash: &T::AccountId, - exposures: Vec<(Self::AccountId, Self::Balance)>, - ) { - let others = exposures - .iter() - .map(|(who, value)| IndividualExposure { who: who.clone(), value: value.clone() }) - .collect::>(); - let exposure = Exposure { total: Default::default(), own: Default::default(), others }; - >::insert(¤t_era, &stash, &exposure); - } - - fn set_current_era(era: EraIndex) { - CurrentEra::::put(era); - } - } -} - -#[cfg(any(test, feature = "try-runtime"))] -impl Pallet { - pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { - ensure!( - T::VoterList::iter() - .all(|x| >::contains_key(&x) || >::contains_key(&x)), - "VoterList contains non-staker" - ); - - Self::check_nominators()?; - Self::check_exposures()?; - Self::check_ledgers()?; - Self::check_count() - } - - fn check_count() -> Result<(), TryRuntimeError> { - ensure!( - ::VoterList::count() == - Nominators::::count() + Validators::::count(), - "wrong external count" - ); - ensure!( - ::TargetList::count() == Validators::::count(), - "wrong external count" - ); - ensure!( - ValidatorCount::::get() <= - ::MaxWinners::get(), - Error::::TooManyValidators - ); - Ok(()) - } - - fn check_ledgers() -> Result<(), TryRuntimeError> { - Bonded::::iter() - .map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl)) - .collect::, _>>()?; - Ok(()) - } - - fn check_exposures() -> Result<(), TryRuntimeError> { - // a check per validator to ensure the exposure struct is always sane. - let era = Self::active_era().unwrap().index; - ErasStakers::::iter_prefix_values(era) - .map(|expo| { - ensure!( - expo.total == - expo.own + - expo.others - .iter() - .map(|e| e.value) - .fold(Zero::zero(), |acc, x| acc + x), - "wrong total exposure.", - ); - Ok(()) - }) - .collect::>() - } - - fn check_nominators() -> Result<(), TryRuntimeError> { - // a check per nominator to ensure their entire stake is correctly distributed. Will only - // kick-in if the nomination was submitted before the current era. - let era = Self::active_era().unwrap().index; - >::iter() - .filter_map( - |(nominator, nomination)| { - if nomination.submitted_in < era { - Some(nominator) - } else { - None - } - }, - ) - .map(|nominator| -> Result<(), TryRuntimeError> { - // must be bonded. - Self::ensure_is_stash(&nominator)?; - let mut sum = BalanceOf::::zero(); - T::SessionInterface::validators() - .iter() - .map(|v| Self::eras_stakers(era, v)) - .map(|e| -> Result<(), TryRuntimeError> { - let individual = - e.others.iter().filter(|e| e.who == nominator).collect::>(); - let len = individual.len(); - match len { - 0 => { /* not supporting this validator at all. */ }, - 1 => sum += individual[0].value, - _ => - return Err( - "nominator cannot back a validator more than once.".into() - ), - }; - Ok(()) - }) - .collect::, _>>()?; - Ok(()) - }) - .collect::, _>>()?; - - Ok(()) - } - - fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> { - ensure!(Self::bonded(who).is_some(), "Not a stash."); - Ok(()) - } - - fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), TryRuntimeError> { - // ensures ledger.total == ledger.active + sum(ledger.unlocking). - let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?; - let real_total: BalanceOf = - ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value); - ensure!(real_total == ledger.total, "ledger.total corrupt"); - - if !(ledger.active >= T::Currency::minimum_balance() || ledger.active.is_zero()) { - log!(warn, "ledger.active less than ED: {:?}, {:?}", ctrl, ledger) - } - - Ok(()) - } -} diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs deleted file mode 100644 index 46d5403ee..000000000 --- a/pallets/staking/src/pallet/mod.rs +++ /dev/null @@ -1,1781 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Staking FRAME Pallet. - -use frame_election_provider_support::{ - ElectionProvider, ElectionProviderBase, SortedListProvider, VoteWeight, -}; -use frame_support::{ - dispatch::Codec, - pallet_prelude::*, - traits::{ - Currency, Defensive, DefensiveResult, DefensiveSaturating, EnsureOrigin, - EstimateNextNewSession, Get, LockIdentifier, LockableCurrency, OnUnbalanced, TryCollect, - UnixTime, - }, - weights::Weight, - BoundedVec, -}; -use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; -use sp_runtime::{ - traits::{CheckedSub, SaturatedConversion, StaticLookup, Zero}, - ArithmeticError, Perbill, Percent, -}; -use sp_staking::{EraIndex, SessionIndex}; -use sp_std::prelude::*; - -mod impls; - -pub use impls::*; - -use crate::{ - slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout, - EraRewardPoints, Exposure, Forcing, NegativeImbalanceOf, Nominations, PositiveImbalanceOf, - RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, - ValidatorPrefs, -}; - -const STAKING_ID: LockIdentifier = *b"staking "; -// The speculative number of spans are used as an input of the weight annotation of -// [`Call::unbond`], as the post dipatch weight may depend on the number of slashing span on the -// account which is not provided as an input. The value set should be conservative but sensible. -pub(crate) const SPECULATIVE_NUM_SPANS: u32 = 32; - -#[frame_support::pallet] -pub mod pallet { - use frame_election_provider_support::ElectionDataProvider; - - use super::*; - use crate::BenchmarkingConfig; - - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(13); - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(_); - - /// Possible operations on the configuration values of this pallet. - #[derive(TypeInfo, Debug, Clone, Encode, Decode, PartialEq)] - pub enum ConfigOp { - /// Don't change. - Noop, - /// Set the given value. - Set(T), - /// Remove from storage. - Remove, - } - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The staking balance. - type Currency: LockableCurrency< - Self::AccountId, - Moment = BlockNumberFor, - Balance = Self::CurrencyBalance, - >; - /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to - /// `From`. - type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned - + codec::FullCodec - + Copy - + MaybeSerializeDeserialize - + sp_std::fmt::Debug - + Default - + From - + TypeInfo - + MaxEncodedLen; - /// Time used for computing era duration. - /// - /// It is guaranteed to start being called from the first `on_finalize`. Thus value at - /// genesis is not used. - type UnixTime: UnixTime; - - /// Convert a balance into a number used for election calculation. This must fit into a - /// `u64` but is allowed to be sensibly lossy. The `u64` is used to communicate with the - /// [`frame_election_provider_support`] crate which accepts u64 numbers and does operations - /// in 128. - /// Consequently, the backward convert is used convert the u128s from sp-elections back to a - /// [`BalanceOf`]. - type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote>; - - /// Something that provides the election functionality. - type ElectionProvider: ElectionProvider< - AccountId = Self::AccountId, - BlockNumber = BlockNumberFor, - // we only accept an election provider that has staking as data provider. - DataProvider = Pallet, - >; - /// Something that provides the election functionality at genesis. - type GenesisElectionProvider: ElectionProvider< - AccountId = Self::AccountId, - BlockNumber = BlockNumberFor, - DataProvider = Pallet, - >; - - /// Maximum number of nominations per nominator. - #[pallet::constant] - type MaxNominations: Get; - - /// Number of eras to keep in history. - /// - /// Following information is kept for eras in `[current_era - - /// HistoryDepth, current_era]`: `ErasStakers`, `ErasStakersClipped`, - /// `ErasValidatorPrefs`, `ErasValidatorReward`, `ErasRewardPoints`, - /// `ErasTotalStake`, `ErasStartSessionIndex`, - /// `StakingLedger.claimed_rewards`. - /// - /// Must be more than the number of eras delayed by session. - /// I.e. active era must always be in history. I.e. `active_era > - /// current_era - history_depth` must be guaranteed. - /// - /// If migrating an existing pallet from storage value to config value, - /// this should be set to same value or greater as in storage. - /// - /// Note: `HistoryDepth` is used as the upper bound for the `BoundedVec` - /// item `StakingLedger.claimed_rewards`. Setting this value lower than - /// the existing value can lead to inconsistencies in the - /// `StakingLedger` and will need to be handled properly in a migration. - /// The test `reducing_history_depth_abrupt` shows this effect. - #[pallet::constant] - type HistoryDepth: Get; - - /// Tokens have been minted and are unused for validator-reward. - /// See [Era payout](./index.html#era-payout). - type RewardRemainder: OnUnbalanced>; - - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// Handler for the unbalanced reduction when slashing a staker. - type Slash: OnUnbalanced>; - - /// Handler for the unbalanced increment when rewarding a staker. - /// NOTE: in most cases, the implementation of `OnUnbalanced` should modify the total - /// issuance. - type Reward: OnUnbalanced>; - - /// Number of sessions per era. - #[pallet::constant] - type SessionsPerEra: Get; - - /// Number of eras that staked funds must remain bonded for. - #[pallet::constant] - type BondingDuration: Get; - - /// Number of eras that slashes are deferred by, after computation. - /// - /// This should be less than the bonding duration. Set to 0 if slashes - /// should be applied immediately, without opportunity for intervention. - #[pallet::constant] - type SlashDeferDuration: Get; - - /// The origin which can manage less critical staking parameters that does not require root. - /// - /// Supported actions: (1) cancel deferred slash, (2) set minimum commission. - type AdminOrigin: EnsureOrigin; - - /// Interface for interacting with a session pallet. - type SessionInterface: SessionInterface; - - /// The payout for validators and the system for the current era. - /// See [Era payout](./index.html#era-payout). - type EraPayout: EraPayout>; - - /// Something that can estimate the next session change, accurately or as a best effort - /// guess. - type NextNewSession: EstimateNextNewSession>; - - /// The maximum number of nominators rewarded for each validator. - /// - /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can - /// claim their reward. This used to limit the i/o cost for the nominator payout. - #[pallet::constant] - type MaxNominatorRewardedPerValidator: Get; - - /// The fraction of the validator set that is safe to be offending. - /// After the threshold is reached a new era will be forced. - type OffendingValidatorsThreshold: Get; - - /// Something that provides a best-effort sorted list of voters aka electing nominators, - /// used for NPoS election. - /// - /// The changes to nominators are reported to this. Moreover, each validator's self-vote is - /// also reported as one independent vote. - /// - /// To keep the load off the chain as much as possible, changes made to the staked amount - /// via rewards and slashes are not reported and thus need to be manually fixed by the - /// staker. In case of `bags-list`, this always means using `rebag` and `putInFrontOf`. - /// - /// Invariant: what comes out of this list will always be a nominator. - type VoterList: SortedListProvider; - - /// WIP: This is a noop as of now, the actual business logic that's described below is going - /// to be introduced in a follow-up PR. - /// - /// Something that provides a best-effort sorted list of targets aka electable validators, - /// used for NPoS election. - /// - /// The changes to the approval stake of each validator are reported to this. This means any - /// change to: - /// 1. The stake of any validator or nominator. - /// 2. The targets of any nominator - /// 3. The role of any staker (e.g. validator -> chilled, nominator -> validator, etc) - /// - /// Unlike `VoterList`, the values in this list are always kept up to date with reward and - /// slash as well, and thus represent the accurate approval stake of all account being - /// nominated by nominators. - /// - /// Note that while at the time of nomination, all targets are checked to be real - /// validators, they can chill at any point, and their approval stakes will still be - /// recorded. This implies that what comes out of iterating this list MIGHT NOT BE AN ACTIVE - /// VALIDATOR. - type TargetList: SortedListProvider>; - - /// The maximum number of `unlocking` chunks a [`StakingLedger`] can - /// have. Effectively determines how many unique eras a staker may be - /// unbonding in. - /// - /// Note: `MaxUnlockingChunks` is used as the upper bound for the - /// `BoundedVec` item `StakingLedger.unlocking`. Setting this value - /// lower than the existing value can lead to inconsistencies in the - /// `StakingLedger` and will need to be handled properly in a runtime - /// migration. The test `reducing_max_unlocking_chunks_abrupt` shows - /// this effect. - #[pallet::constant] - type MaxUnlockingChunks: Get; - - /// Something that listens to staking updates and performs actions based on the data it - /// receives. - /// - /// WARNING: this only reports slashing events for the time being. - type EventListeners: sp_staking::OnStakingUpdate>; - - /// Some parameters of the benchmarking. - type BenchmarkingConfig: BenchmarkingConfig; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - } - - /// The ideal number of active validators. - #[pallet::storage] - #[pallet::getter(fn validator_count)] - pub type ValidatorCount = StorageValue<_, u32, ValueQuery>; - - /// Minimum number of staking participants before emergency conditions are imposed. - #[pallet::storage] - #[pallet::getter(fn minimum_validator_count)] - pub type MinimumValidatorCount = StorageValue<_, u32, ValueQuery>; - - /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're - /// easy to initialize and the performance hit is minimal (we expect no more than four - /// invulnerables) and restricted to testnets. - #[pallet::storage] - #[pallet::getter(fn invulnerables)] - #[pallet::unbounded] - pub type Invulnerables = StorageValue<_, Vec, ValueQuery>; - - /// Map from all locked "stash" accounts to the controller account. - /// - /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. - #[pallet::storage] - #[pallet::getter(fn bonded)] - pub type Bonded = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; - - /// The minimum active bond to become and maintain the role of a nominator. - #[pallet::storage] - pub type MinNominatorBond = StorageValue<_, BalanceOf, ValueQuery>; - - /// The minimum active bond to become and maintain the role of a validator. - #[pallet::storage] - pub type MinValidatorBond = StorageValue<_, BalanceOf, ValueQuery>; - - /// The minimum active nominator stake of the last successful election. - #[pallet::storage] - pub type MinimumActiveStake = StorageValue<_, BalanceOf, ValueQuery>; - - /// The minimum amount of commission that validators can set. - /// - /// If set to `0`, no limit exists. - #[pallet::storage] - pub type MinCommission = StorageValue<_, Perbill, ValueQuery>; - - /// Map from all (unlocked) "controller" accounts to the info regarding the staking. - #[pallet::storage] - #[pallet::getter(fn ledger)] - pub type Ledger = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger>; - - /// Where the reward payment should be made. Keyed by stash. - /// - /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. - #[pallet::storage] - #[pallet::getter(fn payee)] - pub type Payee = - StorageMap<_, Twox64Concat, T::AccountId, RewardDestination, ValueQuery>; - - /// The map from (wannabe) validator stash key to the preferences of that validator. - /// - /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. - #[pallet::storage] - #[pallet::getter(fn validators)] - pub type Validators = - CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>; - - /// The maximum validator count before we stop allowing new validators to join. - /// - /// When this value is not set, no limits are enforced. - #[pallet::storage] - pub type MaxValidatorsCount = StorageValue<_, u32, OptionQuery>; - - /// The map from nominator stash key to their nomination preferences, namely the validators that - /// they wish to support. - /// - /// Note that the keys of this storage map might become non-decodable in case the - /// [`Config::MaxNominations`] configuration is decreased. In this rare case, these nominators - /// are still existent in storage, their key is correct and retrievable (i.e. `contains_key` - /// indicates that they exist), but their value cannot be decoded. Therefore, the non-decodable - /// nominators will effectively not-exist, until they re-submit their preferences such that it - /// is within the bounds of the newly set `Config::MaxNominations`. - /// - /// This implies that `::iter_keys().count()` and `::iter().count()` might return different - /// values for this map. Moreover, the main `::count()` is aligned with the former, namely the - /// number of keys that exist. - /// - /// Lastly, if any of the nominators become non-decodable, they can be chilled immediately via - /// [`Call::chill_other`] dispatchable by anyone. - /// - /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. - #[pallet::storage] - #[pallet::getter(fn nominators)] - pub type Nominators = - CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations>; - - /// The maximum nominator count before we stop allowing new validators to join. - /// - /// When this value is not set, no limits are enforced. - #[pallet::storage] - pub type MaxNominatorsCount = StorageValue<_, u32, OptionQuery>; - - /// The current era index. - /// - /// This is the latest planned era, depending on how the Session pallet queues the validator - /// set, it might be active or not. - #[pallet::storage] - #[pallet::getter(fn current_era)] - pub type CurrentEra = StorageValue<_, EraIndex>; - - /// The active era information, it holds index and start. - /// - /// The active era is the era being currently rewarded. Validator set of this era must be - /// equal to [`SessionInterface::validators`]. - #[pallet::storage] - #[pallet::getter(fn active_era)] - pub type ActiveEra = StorageValue<_, ActiveEraInfo>; - - /// The session index at which the era start for the last `HISTORY_DEPTH` eras. - /// - /// Note: This tracks the starting session (i.e. session index when era start being active) - /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`. - #[pallet::storage] - #[pallet::getter(fn eras_start_session_index)] - pub type ErasStartSessionIndex = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>; - - /// Exposure of validator at era. - /// - /// This is keyed first by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - /// If stakers hasn't been set or has been removed then empty exposure is returned. - #[pallet::storage] - #[pallet::getter(fn eras_stakers)] - #[pallet::unbounded] - pub type ErasStakers = StorageDoubleMap< - _, - Twox64Concat, - EraIndex, - Twox64Concat, - T::AccountId, - Exposure>, - ValueQuery, - >; - - /// Clipped Exposure of validator at era. - /// - /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the - /// `T::MaxNominatorRewardedPerValidator` biggest stakers. - /// (Note: the field `total` and `own` of the exposure remains unchanged). - /// This is used to limit the i/o cost for the nominator payout. - /// - /// This is keyed fist by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - /// If stakers hasn't been set or has been removed then empty exposure is returned. - #[pallet::storage] - #[pallet::unbounded] - #[pallet::getter(fn eras_stakers_clipped)] - pub type ErasStakersClipped = StorageDoubleMap< - _, - Twox64Concat, - EraIndex, - Twox64Concat, - T::AccountId, - Exposure>, - ValueQuery, - >; - - /// Similar to `ErasStakers`, this holds the preferences of validators. - /// - /// This is keyed first by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - // If prefs hasn't been set or has been removed then 0 commission is returned. - #[pallet::storage] - #[pallet::getter(fn eras_validator_prefs)] - pub type ErasValidatorPrefs = StorageDoubleMap< - _, - Twox64Concat, - EraIndex, - Twox64Concat, - T::AccountId, - ValidatorPrefs, - ValueQuery, - >; - - /// The total validator era payout for the last `HISTORY_DEPTH` eras. - /// - /// Eras that haven't finished yet or has been removed doesn't have reward. - #[pallet::storage] - #[pallet::getter(fn eras_validator_reward)] - pub type ErasValidatorReward = StorageMap<_, Twox64Concat, EraIndex, BalanceOf>; - - /// Rewards for the last `HISTORY_DEPTH` eras. - /// If reward hasn't been set or has been removed then 0 reward is returned. - #[pallet::storage] - #[pallet::unbounded] - #[pallet::getter(fn eras_reward_points)] - pub type ErasRewardPoints = - StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints, ValueQuery>; - - /// The total amount staked for the last `HISTORY_DEPTH` eras. - /// If total hasn't been set or has been removed then 0 stake is returned. - #[pallet::storage] - #[pallet::getter(fn eras_total_stake)] - pub type ErasTotalStake = - StorageMap<_, Twox64Concat, EraIndex, BalanceOf, ValueQuery>; - - /// Mode of era forcing. - #[pallet::storage] - #[pallet::getter(fn force_era)] - pub type ForceEra = StorageValue<_, Forcing, ValueQuery>; - - /// The percentage of the slash that is distributed to reporters. - /// - /// The rest of the slashed value is handled by the `Slash`. - #[pallet::storage] - #[pallet::getter(fn slash_reward_fraction)] - pub type SlashRewardFraction = StorageValue<_, Perbill, ValueQuery>; - - /// The amount of currency given to reporters of a slash event which was - /// canceled by extraordinary circumstances (e.g. governance). - #[pallet::storage] - #[pallet::getter(fn canceled_payout)] - pub type CanceledSlashPayout = StorageValue<_, BalanceOf, ValueQuery>; - - /// All unapplied slashes that are queued for later. - #[pallet::storage] - #[pallet::unbounded] - pub type UnappliedSlashes = StorageMap< - _, - Twox64Concat, - EraIndex, - Vec>>, - ValueQuery, - >; - - /// A mapping from still-bonded eras to the first session index of that era. - /// - /// Must contains information for eras for the range: - /// `[active_era - bounding_duration; active_era]` - #[pallet::storage] - #[pallet::unbounded] - pub(crate) type BondedEras = - StorageValue<_, Vec<(EraIndex, SessionIndex)>, ValueQuery>; - - /// All slashing events on validators, mapped by era to the highest slash proportion - /// and slash value of the era. - #[pallet::storage] - pub(crate) type ValidatorSlashInEra = StorageDoubleMap< - _, - Twox64Concat, - EraIndex, - Twox64Concat, - T::AccountId, - (Perbill, BalanceOf), - >; - - /// All slashing events on nominators, mapped by era to the highest slash value of the era. - #[pallet::storage] - pub(crate) type NominatorSlashInEra = - StorageDoubleMap<_, Twox64Concat, EraIndex, Twox64Concat, T::AccountId, BalanceOf>; - - /// Slashing spans for stash accounts. - #[pallet::storage] - #[pallet::getter(fn slashing_spans)] - #[pallet::unbounded] - pub type SlashingSpans = - StorageMap<_, Twox64Concat, T::AccountId, slashing::SlashingSpans>; - - /// Records information about the maximum slash of a stash within a slashing span, - /// as well as how much reward has been paid out. - #[pallet::storage] - pub(crate) type SpanSlash = StorageMap< - _, - Twox64Concat, - (T::AccountId, slashing::SpanIndex), - slashing::SpanRecord>, - ValueQuery, - >; - - /// The last planned session scheduled by the session pallet. - /// - /// This is basically in sync with the call to [`pallet_session::SessionManager::new_session`]. - #[pallet::storage] - #[pallet::getter(fn current_planned_session)] - pub type CurrentPlannedSession = StorageValue<_, SessionIndex, ValueQuery>; - - /// Indices of validators that have offended in the active era and whether they are currently - /// disabled. - /// - /// This value should be a superset of disabled validators since not all offences lead to the - /// validator being disabled (if there was no slash). This is needed to track the percentage of - /// validators that have offended in the current era, ensuring a new era is forced if - /// `OffendingValidatorsThreshold` is reached. The vec is always kept sorted so that we can find - /// whether a given validator has previously offended using binary search. It gets cleared when - /// the era ends. - #[pallet::storage] - #[pallet::unbounded] - #[pallet::getter(fn offending_validators)] - pub type OffendingValidators = StorageValue<_, Vec<(u32, bool)>, ValueQuery>; - - /// The threshold for when users can start calling `chill_other` for other validators / - /// nominators. The threshold is compared to the actual number of validators / nominators - /// (`CountFor*`) in the system compared to the configured max (`Max*Count`). - #[pallet::storage] - pub(crate) type ChillThreshold = StorageValue<_, Percent, OptionQuery>; - - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig { - pub validator_count: u32, - pub minimum_validator_count: u32, - pub invulnerables: Vec, - pub force_era: Forcing, - pub slash_reward_fraction: Perbill, - pub canceled_payout: BalanceOf, - pub stakers: - Vec<(T::AccountId, T::AccountId, BalanceOf, crate::StakerStatus)>, - pub min_nominator_bond: BalanceOf, - pub min_validator_bond: BalanceOf, - pub max_validator_count: Option, - pub max_nominator_count: Option, - } - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - ValidatorCount::::put(self.validator_count); - MinimumValidatorCount::::put(self.minimum_validator_count); - Invulnerables::::put(&self.invulnerables); - ForceEra::::put(self.force_era); - CanceledSlashPayout::::put(self.canceled_payout); - SlashRewardFraction::::put(self.slash_reward_fraction); - MinNominatorBond::::put(self.min_nominator_bond); - MinValidatorBond::::put(self.min_validator_bond); - if let Some(x) = self.max_validator_count { - MaxValidatorsCount::::put(x); - } - if let Some(x) = self.max_nominator_count { - MaxNominatorsCount::::put(x); - } - - for &(ref stash, _, balance, ref status) in &self.stakers { - crate::log!( - trace, - "inserting genesis staker: {:?} => {:?} => {:?}", - stash, - balance, - status - ); - assert!( - T::Currency::free_balance(stash) >= balance, - "Stash does not have enough balance to bond." - ); - frame_support::assert_ok!(>::bond( - T::RuntimeOrigin::from(Some(stash.clone()).into()), - balance, - RewardDestination::Staked, - )); - frame_support::assert_ok!(match status { - crate::StakerStatus::Validator => >::validate( - T::RuntimeOrigin::from(Some(stash.clone()).into()), - Default::default(), - ), - crate::StakerStatus::Nominator(votes) => >::nominate( - T::RuntimeOrigin::from(Some(stash.clone()).into()), - votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(), - ), - _ => Ok(()), - }); - assert!( - ValidatorCount::::get() <= - ::MaxWinners::get() - ); - } - - // all voters are reported to the `VoterList`. - assert_eq!( - T::VoterList::count(), - Nominators::::count() + Validators::::count(), - "not all genesis stakers were inserted into sorted list provider, something is wrong." - ); - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event { - /// The era payout has been set; the first balance is the validator-payout; the second is - /// the remainder from the maximum amount of reward. - EraPaid { era_index: EraIndex, validator_payout: BalanceOf, remainder: BalanceOf }, - /// The nominator has been rewarded by this amount. - Rewarded { stash: T::AccountId, amount: BalanceOf }, - /// A staker (validator or nominator) has been slashed by the given amount. - Slashed { staker: T::AccountId, amount: BalanceOf }, - /// A slash for the given validator, for the given percentage of their stake, at the given - /// era as been reported. - SlashReported { validator: T::AccountId, fraction: Perbill, slash_era: EraIndex }, - /// An old slashing report from a prior era was discarded because it could - /// not be processed. - OldSlashingReportDiscarded { session_index: SessionIndex }, - /// A new set of stakers was elected. - StakersElected, - /// An account has bonded this amount. \[stash, amount\] - /// - /// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably, - /// it will not be emitted for staking rewards when they are added to stake. - Bonded { stash: T::AccountId, amount: BalanceOf }, - /// An account has unbonded this amount. - Unbonded { stash: T::AccountId, amount: BalanceOf }, - /// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance` - /// from the unlocking queue. - Withdrawn { stash: T::AccountId, amount: BalanceOf }, - /// A nominator has been kicked from a validator. - Kicked { nominator: T::AccountId, stash: T::AccountId }, - /// The election failed. No new era is planned. - StakingElectionFailed, - /// An account has stopped participating as either a validator or nominator. - Chilled { stash: T::AccountId }, - /// The stakers' rewards are getting paid. - PayoutStarted { era_index: EraIndex, validator_stash: T::AccountId }, - /// A validator has set their preferences. - ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs }, - /// A new force era mode was set. - ForceEra { mode: Forcing }, - } - - #[pallet::error] - pub enum Error { - /// Not a controller account. - NotController, - /// Not a stash account. - NotStash, - /// Stash is already bonded. - AlreadyBonded, - /// Controller is already paired. - AlreadyPaired, - /// Targets cannot be empty. - EmptyTargets, - /// Duplicate index. - DuplicateIndex, - /// Slash record index out of bounds. - InvalidSlashIndex, - /// Cannot have a validator or nominator role, with value less than the minimum defined by - /// governance (see `MinValidatorBond` and `MinNominatorBond`). If unbonding is the - /// intention, `chill` first to remove one's role as validator/nominator. - InsufficientBond, - /// Can not schedule more unlock chunks. - NoMoreChunks, - /// Can not rebond without unlocking chunks. - NoUnlockChunk, - /// Attempting to target a stash that still has funds. - FundedTarget, - /// Invalid era to reward. - InvalidEraToReward, - /// Invalid number of nominations. - InvalidNumberOfNominations, - /// Items are not sorted and unique. - NotSortedAndUnique, - /// Rewards for this era have already been claimed for this validator. - AlreadyClaimed, - /// Incorrect previous history depth input provided. - IncorrectHistoryDepth, - /// Incorrect number of slashing spans provided. - IncorrectSlashingSpans, - /// Internal state has become somehow corrupted and the operation cannot continue. - BadState, - /// Too many nomination targets supplied. - TooManyTargets, - /// A nomination target was supplied that was blocked or otherwise not a validator. - BadTarget, - /// The user has enough bond and thus cannot be chilled forcefully by an external person. - CannotChillOther, - /// There are too many nominators in the system. Governance needs to adjust the staking - /// settings to keep things safe for the runtime. - TooManyNominators, - /// There are too many validator candidates in the system. Governance needs to adjust the - /// staking settings to keep things safe for the runtime. - TooManyValidators, - /// Commission is too low. Must be at least `MinCommission`. - CommissionTooLow, - /// Some bound is not met. - BoundNotMet, - } - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_initialize(_now: BlockNumberFor) -> Weight { - // just return the weight of the on_finalize. - T::DbWeight::get().reads(1) - } - - fn on_finalize(_n: BlockNumberFor) { - // Set the start of the first era. - if let Some(mut active_era) = Self::active_era() { - if active_era.start.is_none() { - let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::(); - active_era.start = Some(now_as_millis_u64); - // This write only ever happens once, we don't include it in the weight in - // general - ActiveEra::::put(active_era); - } - } - // `on_finalize` weight is tracked in `on_initialize` - } - - fn integrity_test() { - // ensure that we funnel the correct value to the `DataProvider::MaxVotesPerVoter`; - assert_eq!( - T::MaxNominations::get(), - ::MaxVotesPerVoter::get() - ); - // and that MaxNominations is always greater than 1, since we count on this. - assert!(!T::MaxNominations::get().is_zero()); - - // ensure election results are always bounded with the same value - assert!( - ::MaxWinners::get() == - ::MaxWinners::get() - ); - - sp_std::if_std! { - sp_io::TestExternalities::new_empty().execute_with(|| - assert!( - T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0, - "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).", - T::SlashDeferDuration::get(), - T::BondingDuration::get(), - ) - ); - } - } - - #[cfg(feature = "try-runtime")] - fn try_state(n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { - Self::do_try_state(n) - } - } - - #[pallet::call] - impl Pallet { - /// Take the origin account as a stash and lock up `value` of its balance. `controller` will - /// be the account that controls it. - /// - /// `value` must be more than the `minimum_balance` specified by `T::Currency`. - /// - /// The dispatch origin for this call must be _Signed_ by the stash account. - /// - /// Emits `Bonded`. - /// ## Complexity - /// - Independent of the arguments. Moderate complexity. - /// - O(1). - /// - Three extra DB entries. - /// - /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned - /// unless the `origin` falls below _existential deposit_ and gets removed as dust. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::bond())] - pub fn bond( - origin: OriginFor, - #[pallet::compact] value: BalanceOf, - payee: RewardDestination, - ) -> DispatchResult { - let stash = ensure_signed(origin)?; - let controller_to_be_deprecated = stash.clone(); - - if >::contains_key(&stash) { - return Err(Error::::AlreadyBonded.into()) - } - - if >::contains_key(&controller_to_be_deprecated) { - return Err(Error::::AlreadyPaired.into()) - } - - // Reject a bond which is considered to be _dust_. - if value < T::Currency::minimum_balance() { - return Err(Error::::InsufficientBond.into()) - } - - frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - - // You're auto-bonded forever, here. We might improve this by only bonding when - // you actually validate/nominate and remove once you unbond __everything__. - >::insert(&stash, &stash); - >::insert(&stash, payee); - - let current_era = CurrentEra::::get().unwrap_or(0); - let history_depth = T::HistoryDepth::get(); - let last_reward_era = current_era.saturating_sub(history_depth); - - let stash_balance = T::Currency::free_balance(&stash); - let value = value.min(stash_balance); - Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); - let item = StakingLedger { - stash: stash.clone(), - total: value, - active: value, - unlocking: Default::default(), - claimed_rewards: (last_reward_era..current_era) - .try_collect() - // Since last_reward_era is calculated as `current_era - - // HistoryDepth`, following bound is always expected to be - // satisfied. - .defensive_map_err(|_| Error::::BoundNotMet)?, - }; - Self::update_ledger(&controller_to_be_deprecated, &item); - Ok(()) - } - - /// Add some extra amount that have appeared in the stash `free_balance` into the balance up - /// for staking. - /// - /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. - /// - /// Use this if there are additional funds in your stash account that you wish to bond. - /// Unlike [`bond`](Self::bond) or [`unbond`](Self::unbond) this function does not impose - /// any limitation on the amount that can be added. - /// - /// Emits `Bonded`. - /// - /// ## Complexity - /// - Independent of the arguments. Insignificant complexity. - /// - O(1). - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::bond_extra())] - pub fn bond_extra( - origin: OriginFor, - #[pallet::compact] max_additional: BalanceOf, - ) -> DispatchResult { - let stash = ensure_signed(origin)?; - - let controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; - let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - - let stash_balance = T::Currency::free_balance(&stash); - if let Some(extra) = stash_balance.checked_sub(&ledger.total) { - let extra = extra.min(max_additional); - ledger.total += extra; - ledger.active += extra; - // Last check: the new active amount of ledger must be more than ED. - ensure!( - ledger.active >= T::Currency::minimum_balance(), - Error::::InsufficientBond - ); - - // NOTE: ledger must be updated prior to calling `Self::weight_of`. - Self::update_ledger(&controller, &ledger); - // update this staker in the sorted list, if they exist in it. - if T::VoterList::contains(&stash) { - let _ = - T::VoterList::on_update(&stash, Self::weight_of(&ledger.stash)).defensive(); - } - - Self::deposit_event(Event::::Bonded { stash, amount: extra }); - } - Ok(()) - } - - /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond - /// period ends. If this leaves an amount actively bonded less than - /// T::Currency::minimum_balance(), then it is increased to the full amount. - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - /// - /// Once the unlock period is done, you can call `withdraw_unbonded` to actually move - /// the funds out of management ready for transfer. - /// - /// No more than a limited number of unlocking chunks (see `MaxUnlockingChunks`) - /// can co-exists at the same time. If there are no unlocking chunks slots available - /// [`Call::withdraw_unbonded`] is called to remove some of the chunks (if possible). - /// - /// If a user encounters the `InsufficientBond` error when calling this extrinsic, - /// they should call `chill` first in order to free up their bonded funds. - /// - /// Emits `Unbonded`. - /// - /// See also [`Call::withdraw_unbonded`]. - #[pallet::call_index(2)] - #[pallet::weight( - T::WeightInfo::withdraw_unbonded_kill(SPECULATIVE_NUM_SPANS).saturating_add(T::WeightInfo::unbond())) - ] - pub fn unbond( - origin: OriginFor, - #[pallet::compact] value: BalanceOf, - ) -> DispatchResultWithPostInfo { - let controller = ensure_signed(origin)?; - let unlocking = Self::ledger(&controller) - .map(|l| l.unlocking.len()) - .ok_or(Error::::NotController)?; - - // if there are no unlocking chunks available, try to withdraw chunks older than - // `BondingDuration` to proceed with the unbonding. - let maybe_withdraw_weight = { - if unlocking == T::MaxUnlockingChunks::get() as usize { - let real_num_slashing_spans = - Self::slashing_spans(&controller).map_or(0, |s| s.iter().count()); - Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32)?) - } else { - None - } - }; - - // we need to fetch the ledger again because it may have been mutated in the call - // to `Self::do_withdraw_unbonded` above. - let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let mut value = value.min(ledger.active); - - ensure!( - ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize, - Error::::NoMoreChunks, - ); - - if !value.is_zero() { - ledger.active -= value; - - // Avoid there being a dust balance left in the staking system. - if ledger.active < T::Currency::minimum_balance() { - value += ledger.active; - ledger.active = Zero::zero(); - } - - let min_active_bond = if Nominators::::contains_key(&ledger.stash) { - MinNominatorBond::::get() - } else if Validators::::contains_key(&ledger.stash) { - MinValidatorBond::::get() - } else { - Zero::zero() - }; - - // Make sure that the user maintains enough active bond for their role. - // If a user runs into this error, they should chill first. - ensure!(ledger.active >= min_active_bond, Error::::InsufficientBond); - - // Note: in case there is no current era it is fine to bond one era more. - let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get(); - if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) { - // To keep the chunk count down, we only keep one chunk per era. Since - // `unlocking` is a FiFo queue, if a chunk exists for `era` we know that it will - // be the last one. - chunk.value = chunk.value.defensive_saturating_add(value) - } else { - ledger - .unlocking - .try_push(UnlockChunk { value, era }) - .map_err(|_| Error::::NoMoreChunks)?; - }; - // NOTE: ledger must be updated prior to calling `Self::weight_of`. - Self::update_ledger(&controller, &ledger); - - // update this staker in the sorted list, if they exist in it. - if T::VoterList::contains(&ledger.stash) { - let _ = T::VoterList::on_update(&ledger.stash, Self::weight_of(&ledger.stash)) - .defensive(); - } - - Self::deposit_event(Event::::Unbonded { stash: ledger.stash, amount: value }); - } - - let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight { - Some(T::WeightInfo::unbond().saturating_add(withdraw_weight)) - } else { - Some(T::WeightInfo::unbond()) - }; - - Ok(actual_weight.into()) - } - - /// Remove any unlocked chunks from the `unlocking` queue from our management. - /// - /// This essentially frees up that balance to be used by the stash account to do whatever - /// it wants. - /// - /// The dispatch origin for this call must be _Signed_ by the controller. - /// - /// Emits `Withdrawn`. - /// - /// See also [`Call::unbond`]. - /// - /// ## Parameters - /// - /// - `num_slashing_spans` indicates the number of metadata slashing spans to clear when - /// this call results in a complete removal of all the data related to the stash account. - /// In this case, the `num_slashing_spans` must be larger or equal to the number of - /// slashing spans associated with the stash account in the [`SlashingSpans`] storage type, - /// otherwise the call will fail. The call weight is directly propotional to - /// `num_slashing_spans`. - /// - /// ## Complexity - /// O(S) where S is the number of slashing spans to remove - /// NOTE: Weight annotation is the kill scenario, we refund otherwise. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans))] - pub fn withdraw_unbonded( - origin: OriginFor, - num_slashing_spans: u32, - ) -> DispatchResultWithPostInfo { - let controller = ensure_signed(origin)?; - - let actual_weight = Self::do_withdraw_unbonded(&controller, num_slashing_spans)?; - Ok(Some(actual_weight).into()) - } - - /// Declare the desire to validate for the origin controller. - /// - /// Effects will be felt at the beginning of the next era. - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::validate())] - pub fn validate(origin: OriginFor, prefs: ValidatorPrefs) -> DispatchResult { - let controller = ensure_signed(origin)?; - - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - - ensure!(ledger.active >= MinValidatorBond::::get(), Error::::InsufficientBond); - let stash = &ledger.stash; - - // ensure their commission is correct. - ensure!(prefs.commission >= MinCommission::::get(), Error::::CommissionTooLow); - - // Only check limits if they are not already a validator. - if !Validators::::contains_key(stash) { - // If this error is reached, we need to adjust the `MinValidatorBond` and start - // calling `chill_other`. Until then, we explicitly block new validators to protect - // the runtime. - if let Some(max_validators) = MaxValidatorsCount::::get() { - ensure!( - Validators::::count() < max_validators, - Error::::TooManyValidators - ); - } - } - - Self::do_remove_nominator(stash); - Self::do_add_validator(stash, prefs.clone()); - Self::deposit_event(Event::::ValidatorPrefsSet { stash: ledger.stash, prefs }); - - Ok(()) - } - - /// Declare the desire to nominate `targets` for the origin controller. - /// - /// Effects will be felt at the beginning of the next era. - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - /// - /// ## Complexity - /// - The transaction's complexity is proportional to the size of `targets` (N) - /// which is capped at CompactAssignments::LIMIT (T::MaxNominations). - /// - Both the reads and writes follow a similar pattern. - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))] - pub fn nominate( - origin: OriginFor, - targets: Vec>, - ) -> DispatchResult { - let controller = ensure_signed(origin)?; - - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - ensure!(ledger.active >= MinNominatorBond::::get(), Error::::InsufficientBond); - let stash = &ledger.stash; - - // Only check limits if they are not already a nominator. - if !Nominators::::contains_key(stash) { - // If this error is reached, we need to adjust the `MinNominatorBond` and start - // calling `chill_other`. Until then, we explicitly block new nominators to protect - // the runtime. - if let Some(max_nominators) = MaxNominatorsCount::::get() { - ensure!( - Nominators::::count() < max_nominators, - Error::::TooManyNominators - ); - } - } - - ensure!(!targets.is_empty(), Error::::EmptyTargets); - ensure!(targets.len() <= T::MaxNominations::get() as usize, Error::::TooManyTargets); - - let old = Nominators::::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner()); - - let targets: BoundedVec<_, _> = targets - .into_iter() - .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from)) - .map(|n| { - n.and_then(|n| { - if old.contains(&n) || !Validators::::get(&n).blocked { - Ok(n) - } else { - Err(Error::::BadTarget.into()) - } - }) - }) - .collect::, _>>()? - .try_into() - .map_err(|_| Error::::TooManyNominators)?; - - let nominations = Nominations { - targets, - // Initial nominations are considered submitted at era 0. See `Nominations` doc. - submitted_in: Self::current_era().unwrap_or(0), - suppressed: false, - }; - - Self::do_remove_validator(stash); - Self::do_add_nominator(stash, nominations); - Ok(()) - } - - /// Declare no desire to either validate or nominate. - /// - /// Effects will be felt at the beginning of the next era. - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - /// - /// ## Complexity - /// - Independent of the arguments. Insignificant complexity. - /// - Contains one read. - /// - Writes are limited to the `origin` account key. - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::chill())] - pub fn chill(origin: OriginFor) -> DispatchResult { - let controller = ensure_signed(origin)?; - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - Self::chill_stash(&ledger.stash); - Ok(()) - } - - /// (Re-)set the payment target for a controller. - /// - /// Effects will be felt instantly (as soon as this function is completed successfully). - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - /// - /// ## Complexity - /// - O(1) - /// - Independent of the arguments. Insignificant complexity. - /// - Contains a limited number of reads. - /// - Writes are limited to the `origin` account key. - /// --------- - #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::set_payee())] - pub fn set_payee( - origin: OriginFor, - payee: RewardDestination, - ) -> DispatchResult { - let controller = ensure_signed(origin)?; - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let stash = &ledger.stash; - >::insert(stash, payee); - Ok(()) - } - - /// (Re-)sets the controller of a stash to the stash itself. This function previously - /// accepted a `controller` argument to set the controller to an account other than the - /// stash itself. This functionality has now been removed, now only setting the controller - /// to the stash, if it is not already. - /// - /// Effects will be felt instantly (as soon as this function is completed successfully). - /// - /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. - /// - /// ## Complexity - /// O(1) - /// - Independent of the arguments. Insignificant complexity. - /// - Contains a limited number of reads. - /// - Writes are limited to the `origin` account key. - #[pallet::call_index(8)] - #[pallet::weight(T::WeightInfo::set_controller())] - pub fn set_controller(origin: OriginFor) -> DispatchResult { - let stash = ensure_signed(origin)?; - let old_controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; - - if >::contains_key(&stash) { - return Err(Error::::AlreadyPaired.into()) - } - if old_controller != stash { - >::insert(&stash, &stash); - if let Some(l) = >::take(&old_controller) { - >::insert(&stash, l); - } - } - Ok(()) - } - - /// Sets the ideal number of validators. - /// - /// The dispatch origin must be Root. - /// - /// ## Complexity - /// O(1) - #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::set_validator_count())] - pub fn set_validator_count( - origin: OriginFor, - #[pallet::compact] new: u32, - ) -> DispatchResult { - ensure_root(origin)?; - // ensure new validator count does not exceed maximum winners - // support by election provider. - ensure!( - new <= ::MaxWinners::get(), - Error::::TooManyValidators - ); - ValidatorCount::::put(new); - Ok(()) - } - - /// Increments the ideal number of validators upto maximum of - /// `ElectionProviderBase::MaxWinners`. - /// - /// The dispatch origin must be Root. - /// - /// ## Complexity - /// Same as [`Self::set_validator_count`]. - #[pallet::call_index(10)] - #[pallet::weight(T::WeightInfo::set_validator_count())] - pub fn increase_validator_count( - origin: OriginFor, - #[pallet::compact] additional: u32, - ) -> DispatchResult { - ensure_root(origin)?; - let old = ValidatorCount::::get(); - let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?; - ensure!( - new <= ::MaxWinners::get(), - Error::::TooManyValidators - ); - - ValidatorCount::::put(new); - Ok(()) - } - - /// Scale up the ideal number of validators by a factor upto maximum of - /// `ElectionProviderBase::MaxWinners`. - /// - /// The dispatch origin must be Root. - /// - /// ## Complexity - /// Same as [`Self::set_validator_count`]. - #[pallet::call_index(11)] - #[pallet::weight(T::WeightInfo::set_validator_count())] - pub fn scale_validator_count(origin: OriginFor, factor: Percent) -> DispatchResult { - ensure_root(origin)?; - let old = ValidatorCount::::get(); - let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?; - - ensure!( - new <= ::MaxWinners::get(), - Error::::TooManyValidators - ); - - ValidatorCount::::put(new); - Ok(()) - } - - /// Force there to be no new eras indefinitely. - /// - /// The dispatch origin must be Root. - /// - /// # Warning - /// - /// The election process starts multiple blocks before the end of the era. - /// Thus the election process may be ongoing when this is called. In this case the - /// election will continue until the next era is triggered. - /// - /// ## Complexity - /// - No arguments. - /// - Weight: O(1) - #[pallet::call_index(12)] - #[pallet::weight(T::WeightInfo::force_no_eras())] - pub fn force_no_eras(origin: OriginFor) -> DispatchResult { - ensure_root(origin)?; - Self::set_force_era(Forcing::ForceNone); - Ok(()) - } - - /// Force there to be a new era at the end of the next session. After this, it will be - /// reset to normal (non-forced) behaviour. - /// - /// The dispatch origin must be Root. - /// - /// # Warning - /// - /// The election process starts multiple blocks before the end of the era. - /// If this is called just before a new era is triggered, the election process may not - /// have enough blocks to get a result. - /// - /// ## Complexity - /// - No arguments. - /// - Weight: O(1) - #[pallet::call_index(13)] - #[pallet::weight(T::WeightInfo::force_new_era())] - pub fn force_new_era(origin: OriginFor) -> DispatchResult { - ensure_root(origin)?; - Self::set_force_era(Forcing::ForceNew); - Ok(()) - } - - /// Set the validators who cannot be slashed (if any). - /// - /// The dispatch origin must be Root. - #[pallet::call_index(14)] - #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))] - pub fn set_invulnerables( - origin: OriginFor, - invulnerables: Vec, - ) -> DispatchResult { - ensure_root(origin)?; - >::put(invulnerables); - Ok(()) - } - - /// Force a current staker to become completely unstaked, immediately. - /// - /// The dispatch origin must be Root. - /// - /// ## Parameters - /// - /// - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more - /// details. - #[pallet::call_index(15)] - #[pallet::weight(T::WeightInfo::force_unstake(*num_slashing_spans))] - pub fn force_unstake( - origin: OriginFor, - stash: T::AccountId, - num_slashing_spans: u32, - ) -> DispatchResult { - ensure_root(origin)?; - - // Remove all staking-related information. - Self::kill_stash(&stash, num_slashing_spans)?; - - // Remove the lock. - T::Currency::remove_lock(STAKING_ID, &stash); - Ok(()) - } - - /// Force there to be a new era at the end of sessions indefinitely. - /// - /// The dispatch origin must be Root. - /// - /// # Warning - /// - /// The election process starts multiple blocks before the end of the era. - /// If this is called just before a new era is triggered, the election process may not - /// have enough blocks to get a result. - #[pallet::call_index(16)] - #[pallet::weight(T::WeightInfo::force_new_era_always())] - pub fn force_new_era_always(origin: OriginFor) -> DispatchResult { - ensure_root(origin)?; - Self::set_force_era(Forcing::ForceAlways); - Ok(()) - } - - /// Cancel enactment of a deferred slash. - /// - /// Can be called by the `T::AdminOrigin`. - /// - /// Parameters: era and indices of the slashes for that era to kill. - #[pallet::call_index(17)] - #[pallet::weight(T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32))] - pub fn cancel_deferred_slash( - origin: OriginFor, - era: EraIndex, - slash_indices: Vec, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); - ensure!(is_sorted_and_unique(&slash_indices), Error::::NotSortedAndUnique); - - let mut unapplied = UnappliedSlashes::::get(&era); - let last_item = slash_indices[slash_indices.len() - 1]; - ensure!((last_item as usize) < unapplied.len(), Error::::InvalidSlashIndex); - - for (removed, index) in slash_indices.into_iter().enumerate() { - let index = (index as usize) - removed; - unapplied.remove(index); - } - - UnappliedSlashes::::insert(&era, &unapplied); - Ok(()) - } - - /// Pay out all the stakers behind a single validator for a single era. - /// - /// - `validator_stash` is the stash account of the validator. Their nominators, up to - /// `T::MaxNominatorRewardedPerValidator`, will also receive their rewards. - /// - `era` may be any era between `[current_era - history_depth; current_era]`. - /// - /// The origin of this call must be _Signed_. Any account can call this function, even if - /// it is not one of the stakers. - /// - /// ## Complexity - /// - At most O(MaxNominatorRewardedPerValidator). - #[pallet::call_index(18)] - #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked( - T::MaxNominatorRewardedPerValidator::get() - ))] - pub fn payout_stakers( - origin: OriginFor, - validator_stash: T::AccountId, - era: EraIndex, - ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; - Self::do_payout_stakers(validator_stash, era) - } - - /// Rebond a portion of the stash scheduled to be unlocked. - /// - /// The dispatch origin must be signed by the controller. - /// - /// ## Complexity - /// - Time complexity: O(L), where L is unlocking chunks - /// - Bounded by `MaxUnlockingChunks`. - #[pallet::call_index(19)] - #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))] - pub fn rebond( - origin: OriginFor, - #[pallet::compact] value: BalanceOf, - ) -> DispatchResultWithPostInfo { - let controller = ensure_signed(origin)?; - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - ensure!(!ledger.unlocking.is_empty(), Error::::NoUnlockChunk); - - let initial_unlocking = ledger.unlocking.len() as u32; - let (ledger, rebonded_value) = ledger.rebond(value); - // Last check: the new active amount of ledger must be more than ED. - ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); - - Self::deposit_event(Event::::Bonded { - stash: ledger.stash.clone(), - amount: rebonded_value, - }); - - // NOTE: ledger must be updated prior to calling `Self::weight_of`. - Self::update_ledger(&controller, &ledger); - if T::VoterList::contains(&ledger.stash) { - let _ = T::VoterList::on_update(&ledger.stash, Self::weight_of(&ledger.stash)) - .defensive(); - } - - let removed_chunks = 1u32 // for the case where the last iterated chunk is not removed - .saturating_add(initial_unlocking) - .saturating_sub(ledger.unlocking.len() as u32); - Ok(Some(T::WeightInfo::rebond(removed_chunks)).into()) - } - - /// Remove all data structures concerning a staker/stash once it is at a state where it can - /// be considered `dust` in the staking system. The requirements are: - /// - /// 1. the `total_balance` of the stash is below existential deposit. - /// 2. or, the `ledger.total` of the stash is below existential deposit. - /// - /// The former can happen in cases like a slash; the latter when a fully unbonded account - /// is still receiving staking rewards in `RewardDestination::Staked`. - /// - /// It can be called by anyone, as long as `stash` meets the above requirements. - /// - /// Refunds the transaction fees upon successful execution. - /// - /// ## Parameters - /// - /// - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more - /// details. - #[pallet::call_index(20)] - #[pallet::weight(T::WeightInfo::reap_stash(*num_slashing_spans))] - pub fn reap_stash( - origin: OriginFor, - stash: T::AccountId, - num_slashing_spans: u32, - ) -> DispatchResultWithPostInfo { - let _ = ensure_signed(origin)?; - - let ed = T::Currency::minimum_balance(); - let reapable = T::Currency::total_balance(&stash) < ed || - Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) - .map(|l| l.total) - .unwrap_or_default() < ed; - ensure!(reapable, Error::::FundedTarget); - - Self::kill_stash(&stash, num_slashing_spans)?; - T::Currency::remove_lock(STAKING_ID, &stash); - - Ok(Pays::No.into()) - } - - /// Remove the given nominations from the calling validator. - /// - /// Effects will be felt at the beginning of the next era. - /// - /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. - /// - /// - `who`: A list of nominator stash accounts who are nominating this validator which - /// should no longer be nominating this validator. - /// - /// Note: Making this call only makes sense if you first set the validator preferences to - /// block any further nominations. - #[pallet::call_index(21)] - #[pallet::weight(T::WeightInfo::kick(who.len() as u32))] - pub fn kick(origin: OriginFor, who: Vec>) -> DispatchResult { - let controller = ensure_signed(origin)?; - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let stash = &ledger.stash; - - for nom_stash in who - .into_iter() - .map(T::Lookup::lookup) - .collect::, _>>()? - .into_iter() - { - Nominators::::mutate(&nom_stash, |maybe_nom| { - if let Some(ref mut nom) = maybe_nom { - if let Some(pos) = nom.targets.iter().position(|v| v == stash) { - nom.targets.swap_remove(pos); - Self::deposit_event(Event::::Kicked { - nominator: nom_stash.clone(), - stash: stash.clone(), - }); - } - } - }); - } - - Ok(()) - } - - /// Update the various staking configurations . - /// - /// * `min_nominator_bond`: The minimum active bond needed to be a nominator. - /// * `min_validator_bond`: The minimum active bond needed to be a validator. - /// * `max_nominator_count`: The max number of users who can be a nominator at once. When - /// set to `None`, no limit is enforced. - /// * `max_validator_count`: The max number of users who can be a validator at once. When - /// set to `None`, no limit is enforced. - /// * `chill_threshold`: The ratio of `max_nominator_count` or `max_validator_count` which - /// should be filled in order for the `chill_other` transaction to work. - /// * `min_commission`: The minimum amount of commission that each validators must maintain. - /// This is checked only upon calling `validate`. Existing validators are not affected. - /// - /// RuntimeOrigin must be Root to call this function. - /// - /// NOTE: Existing nominators and validators will not be affected by this update. - /// to kick people under the new limits, `chill_other` should be called. - // We assume the worst case for this call is either: all items are set or all items are - // removed. - #[pallet::call_index(22)] - #[pallet::weight( - T::WeightInfo::set_staking_configs_all_set() - .max(T::WeightInfo::set_staking_configs_all_remove()) - )] - pub fn set_staking_configs( - origin: OriginFor, - min_nominator_bond: ConfigOp>, - min_validator_bond: ConfigOp>, - max_nominator_count: ConfigOp, - max_validator_count: ConfigOp, - chill_threshold: ConfigOp, - min_commission: ConfigOp, - ) -> DispatchResult { - ensure_root(origin)?; - - macro_rules! config_op_exp { - ($storage:ty, $op:ident) => { - match $op { - ConfigOp::Noop => (), - ConfigOp::Set(v) => <$storage>::put(v), - ConfigOp::Remove => <$storage>::kill(), - } - }; - } - - config_op_exp!(MinNominatorBond, min_nominator_bond); - config_op_exp!(MinValidatorBond, min_validator_bond); - config_op_exp!(MaxNominatorsCount, max_nominator_count); - config_op_exp!(MaxValidatorsCount, max_validator_count); - config_op_exp!(ChillThreshold, chill_threshold); - config_op_exp!(MinCommission, min_commission); - Ok(()) - } - /// Declare a `controller` to stop participating as either a validator or nominator. - /// - /// Effects will be felt at the beginning of the next era. - /// - /// The dispatch origin for this call must be _Signed_, but can be called by anyone. - /// - /// If the caller is the same as the controller being targeted, then no further checks are - /// enforced, and this function behaves just like `chill`. - /// - /// If the caller is different than the controller being targeted, the following conditions - /// must be met: - /// - /// * `controller` must belong to a nominator who has become non-decodable, - /// - /// Or: - /// - /// * A `ChillThreshold` must be set and checked which defines how close to the max - /// nominators or validators we must reach before users can start chilling one-another. - /// * A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine - /// how close we are to the threshold. - /// * A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines - /// if this is a person that should be chilled because they have not met the threshold - /// bond required. - /// - /// This can be helpful if bond requirements are updated, and we need to remove old users - /// who do not satisfy these requirements. - #[pallet::call_index(23)] - #[pallet::weight(T::WeightInfo::chill_other())] - pub fn chill_other(origin: OriginFor, controller: T::AccountId) -> DispatchResult { - // Anyone can call this function. - let caller = ensure_signed(origin)?; - let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let stash = ledger.stash; - - // In order for one user to chill another user, the following conditions must be met: - // - // * `controller` belongs to a nominator who has become non-decodable, - // - // Or - // - // * A `ChillThreshold` is set which defines how close to the max nominators or - // validators we must reach before users can start chilling one-another. - // * A `MaxNominatorCount` and `MaxValidatorCount` which is used to determine how close - // we are to the threshold. - // * A `MinNominatorBond` and `MinValidatorBond` which is the final condition checked to - // determine this is a person that should be chilled because they have not met the - // threshold bond required. - // - // Otherwise, if caller is the same as the controller, this is just like `chill`. - - if Nominators::::contains_key(&stash) && Nominators::::get(&stash).is_none() { - Self::chill_stash(&stash); - return Ok(()) - } - - if caller != controller { - let threshold = ChillThreshold::::get().ok_or(Error::::CannotChillOther)?; - let min_active_bond = if Nominators::::contains_key(&stash) { - let max_nominator_count = - MaxNominatorsCount::::get().ok_or(Error::::CannotChillOther)?; - let current_nominator_count = Nominators::::count(); - ensure!( - threshold * max_nominator_count < current_nominator_count, - Error::::CannotChillOther - ); - MinNominatorBond::::get() - } else if Validators::::contains_key(&stash) { - let max_validator_count = - MaxValidatorsCount::::get().ok_or(Error::::CannotChillOther)?; - let current_validator_count = Validators::::count(); - ensure!( - threshold * max_validator_count < current_validator_count, - Error::::CannotChillOther - ); - MinValidatorBond::::get() - } else { - Zero::zero() - }; - - ensure!(ledger.active < min_active_bond, Error::::CannotChillOther); - } - - Self::chill_stash(&stash); - Ok(()) - } - - /// Force a validator to have at least the minimum commission. This will not affect a - /// validator who already has a commission greater than or equal to the minimum. Any account - /// can call this. - #[pallet::call_index(24)] - #[pallet::weight(T::WeightInfo::force_apply_min_commission())] - pub fn force_apply_min_commission( - origin: OriginFor, - validator_stash: T::AccountId, - ) -> DispatchResult { - ensure_signed(origin)?; - let min_commission = MinCommission::::get(); - Validators::::try_mutate_exists(validator_stash, |maybe_prefs| { - maybe_prefs - .as_mut() - .map(|prefs| { - (prefs.commission < min_commission) - .then(|| prefs.commission = min_commission) - }) - .ok_or(Error::::NotStash) - })?; - Ok(()) - } - - /// Sets the minimum amount of commission that each validators must maintain. - /// - /// This call has lower privilege requirements than `set_staking_config` and can be called - /// by the `T::AdminOrigin`. Root can always call this. - #[pallet::call_index(25)] - #[pallet::weight(T::WeightInfo::set_min_commission())] - pub fn set_min_commission(origin: OriginFor, new: Perbill) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - MinCommission::::put(new); - Ok(()) - } - } -} - -/// Check that list is sorted and has no duplicates. -fn is_sorted_and_unique(list: &[u32]) -> bool { - list.windows(2).all(|w| w[0] < w[1]) -} diff --git a/pallets/staking/src/slashing.rs b/pallets/staking/src/slashing.rs deleted file mode 100644 index 1315b27bc..000000000 --- a/pallets/staking/src/slashing.rs +++ /dev/null @@ -1,861 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! A slashing implementation for NPoS systems. -//! -//! For the purposes of the economic model, it is easiest to think of each validator as a nominator -//! which nominates only its own identity. -//! -//! The act of nomination signals intent to unify economic identity with the validator - to take -//! part in the rewards of a job well done, and to take part in the punishment of a job done badly. -//! -//! There are 3 main difficulties to account for with slashing in NPoS: -//! - A nominator can nominate multiple validators and be slashed via any of them. -//! - Until slashed, stake is reused from era to era. Nominating with N coins for E eras in a row -//! does not mean you have N*E coins to be slashed - you've only ever had N. -//! - Slashable offences can be found after the fact and out of order. -//! -//! The algorithm implemented in this module tries to balance these 3 difficulties. -//! -//! First, we only slash participants for the _maximum_ slash they receive in some time period, -//! rather than the sum. This ensures a protection from overslashing. -//! -//! Second, we do not want the time period (or "span") that the maximum is computed -//! over to last indefinitely. That would allow participants to begin acting with -//! impunity after some point, fearing no further repercussions. For that reason, we -//! automatically "chill" validators and withdraw a nominator's nomination after a slashing event, -//! requiring them to re-enlist voluntarily (acknowledging the slash) and begin a new -//! slashing span. -//! -//! Typically, you will have a single slashing event per slashing span. Only in the case -//! where a validator releases many misbehaviors at once, or goes "back in time" to misbehave in -//! eras that have already passed, would you encounter situations where a slashing span -//! has multiple misbehaviors. However, accounting for such cases is necessary -//! to deter a class of "rage-quit" attacks. -//! -//! Based on research at - -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{ - ensure, - traits::{Currency, Defensive, Get, Imbalance, OnUnbalanced}, -}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{Saturating, Zero}, - DispatchResult, RuntimeDebug, -}; -use sp_staking::{offence::DisableStrategy, EraIndex}; -use sp_std::vec::Vec; - -use crate::{ - BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, NominatorSlashInEra, - OffendingValidators, Pallet, Perbill, SessionInterface, SpanSlash, UnappliedSlash, - ValidatorSlashInEra, -}; - -/// The proportion of the slashing reward to be paid out on the first slashing detection. -/// This is f_1 in the paper. -const REWARD_F1: Perbill = Perbill::from_percent(50); - -/// The index of a slashing span - unique to each stash. -pub type SpanIndex = u32; - -// A range of start..end eras for a slashing span. -#[derive(Encode, Decode, TypeInfo)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub(crate) struct SlashingSpan { - pub(crate) index: SpanIndex, - pub(crate) start: EraIndex, - pub(crate) length: Option, // the ongoing slashing span has indeterminate length. -} - -impl SlashingSpan { - fn contains_era(&self, era: EraIndex) -> bool { - self.start <= era && self.length.map_or(true, |l| self.start + l > era) - } -} - -/// An encoding of all of a nominator's slashing spans. -#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct SlashingSpans { - // the index of the current slashing span of the nominator. different for - // every stash, resets when the account hits free balance 0. - span_index: SpanIndex, - // the start era of the most recent (ongoing) slashing span. - last_start: EraIndex, - // the last era at which a non-zero slash occurred. - last_nonzero_slash: EraIndex, - // all prior slashing spans' start indices, in reverse order (most recent first) - // encoded as offsets relative to the slashing span after it. - prior: Vec, -} - -impl SlashingSpans { - // creates a new record of slashing spans for a stash, starting at the beginning - // of the bonding period, relative to now. - pub(crate) fn new(window_start: EraIndex) -> Self { - SlashingSpans { - span_index: 0, - last_start: window_start, - // initialize to zero, as this structure is lazily created until - // the first slash is applied. setting equal to `window_start` would - // put a time limit on nominations. - last_nonzero_slash: 0, - prior: Vec::new(), - } - } - - // update the slashing spans to reflect the start of a new span at the era after `now` - // returns `true` if a new span was started, `false` otherwise. `false` indicates - // that internal state is unchanged. - pub(crate) fn end_span(&mut self, now: EraIndex) -> bool { - let next_start = now + 1; - if next_start <= self.last_start { - return false - } - - let last_length = next_start - self.last_start; - self.prior.insert(0, last_length); - self.last_start = next_start; - self.span_index += 1; - true - } - - // an iterator over all slashing spans in _reverse_ order - most recent first. - pub(crate) fn iter(&'_ self) -> impl Iterator + '_ { - let mut last_start = self.last_start; - let mut index = self.span_index; - let last = SlashingSpan { index, start: last_start, length: None }; - let prior = self.prior.iter().cloned().map(move |length| { - let start = last_start - length; - last_start = start; - index -= 1; - - SlashingSpan { index, start, length: Some(length) } - }); - - sp_std::iter::once(last).chain(prior) - } - - /// Yields the era index where the most recent non-zero slash occurred. - pub fn last_nonzero_slash(&self) -> EraIndex { - self.last_nonzero_slash - } - - // prune the slashing spans against a window, whose start era index is given. - // - // If this returns `Some`, then it includes a range start..end of all the span - // indices which were pruned. - fn prune(&mut self, window_start: EraIndex) -> Option<(SpanIndex, SpanIndex)> { - let old_idx = self - .iter() - .skip(1) // skip ongoing span. - .position(|span| span.length.map_or(false, |len| span.start + len <= window_start)); - - let earliest_span_index = self.span_index - self.prior.len() as SpanIndex; - let pruned = match old_idx { - Some(o) => { - self.prior.truncate(o); - let new_earliest = self.span_index - self.prior.len() as SpanIndex; - Some((earliest_span_index, new_earliest)) - }, - None => None, - }; - - // readjust the ongoing span, if it started before the beginning of the window. - self.last_start = sp_std::cmp::max(self.last_start, window_start); - pruned - } -} - -/// A slashing-span record for a particular stash. -#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)] -pub(crate) struct SpanRecord { - slashed: Balance, - paid_out: Balance, -} - -impl SpanRecord { - /// The value of stash balance slashed in this span. - #[cfg(test)] - pub(crate) fn amount(&self) -> &Balance { - &self.slashed - } -} - -/// Parameters for performing a slash. -#[derive(Clone)] -pub(crate) struct SlashParams<'a, T: 'a + Config> { - /// The stash account being slashed. - pub(crate) stash: &'a T::AccountId, - /// The proportion of the slash. - pub(crate) slash: Perbill, - /// The exposure of the stash and all nominators. - pub(crate) exposure: &'a Exposure>, - /// The era where the offence occurred. - pub(crate) slash_era: EraIndex, - /// The first era in the current bonding period. - pub(crate) window_start: EraIndex, - /// The current era. - pub(crate) now: EraIndex, - /// The maximum percentage of a slash that ever gets paid out. - /// This is f_inf in the paper. - pub(crate) reward_proportion: Perbill, - /// When to disable offenders. - pub(crate) disable_strategy: DisableStrategy, -} - -/// Computes a slash of a validator and nominators. It returns an unapplied -/// record to be applied at some later point. Slashing metadata is updated in storage, -/// since unapplied records are only rarely intended to be dropped. -/// -/// The pending slash record returned does not have initialized reporters. Those have -/// to be set at a higher level, if any. -pub(crate) fn compute_slash( - params: SlashParams, -) -> Option>> { - let mut reward_payout = Zero::zero(); - let mut val_slashed = Zero::zero(); - - // is the slash amount here a maximum for the era? - let own_slash = params.slash * params.exposure.own; - if params.slash * params.exposure.total == Zero::zero() { - // kick out the validator even if they won't be slashed, - // as long as the misbehavior is from their most recent slashing span. - kick_out_if_recent::(params); - return None - } - - let prior_slash_p = ValidatorSlashInEra::::get(¶ms.slash_era, params.stash) - .map_or(Zero::zero(), |(prior_slash_proportion, _)| prior_slash_proportion); - - // compare slash proportions rather than slash values to avoid issues due to rounding - // error. - if params.slash.deconstruct() > prior_slash_p.deconstruct() { - ValidatorSlashInEra::::insert( - ¶ms.slash_era, - params.stash, - &(params.slash, own_slash), - ); - } else { - // we slash based on the max in era - this new event is not the max, - // so neither the validator or any nominators will need an update. - // - // this does lead to a divergence of our system from the paper, which - // pays out some reward even if the latest report is not max-in-era. - // we opt to avoid the nominator lookups and edits and leave more rewards - // for more drastic misbehavior. - return None - } - - // apply slash to validator. - { - let mut spans = fetch_spans::( - params.stash, - params.window_start, - &mut reward_payout, - &mut val_slashed, - params.reward_proportion, - ); - - let target_span = spans.compare_and_update_span_slash(params.slash_era, own_slash); - - if target_span == Some(spans.span_index()) { - // misbehavior occurred within the current slashing span - take appropriate - // actions. - - // chill the validator - it misbehaved in the current span and should - // not continue in the next election. also end the slashing span. - spans.end_span(params.now); - >::chill_stash(params.stash); - } - } - - let disable_when_slashed = params.disable_strategy != DisableStrategy::Never; - add_offending_validator::(params.stash, disable_when_slashed); - - let mut nominators_slashed = Vec::new(); - reward_payout += slash_nominators::(params.clone(), prior_slash_p, &mut nominators_slashed); - - Some(UnappliedSlash { - validator: params.stash.clone(), - own: val_slashed, - others: nominators_slashed, - reporters: Vec::new(), - payout: reward_payout, - }) -} - -// doesn't apply any slash, but kicks out the validator if the misbehavior is from -// the most recent slashing span. -fn kick_out_if_recent(params: SlashParams) { - // these are not updated by era-span or end-span. - let mut reward_payout = Zero::zero(); - let mut val_slashed = Zero::zero(); - let mut spans = fetch_spans::( - params.stash, - params.window_start, - &mut reward_payout, - &mut val_slashed, - params.reward_proportion, - ); - - if spans.era_span(params.slash_era).map(|s| s.index) == Some(spans.span_index()) { - spans.end_span(params.now); - >::chill_stash(params.stash); - } - - let disable_without_slash = params.disable_strategy == DisableStrategy::Always; - add_offending_validator::(params.stash, disable_without_slash); -} - -/// Add the given validator to the offenders list and optionally disable it. -/// If after adding the validator `OffendingValidatorsThreshold` is reached -/// a new era will be forced. -fn add_offending_validator(stash: &T::AccountId, disable: bool) { - OffendingValidators::::mutate(|offending| { - let validators = T::SessionInterface::validators(); - let validator_index = match validators.iter().position(|i| i == stash) { - Some(index) => index, - None => return, - }; - - let validator_index_u32 = validator_index as u32; - - match offending.binary_search_by_key(&validator_index_u32, |(index, _)| *index) { - // this is a new offending validator - Err(index) => { - offending.insert(index, (validator_index_u32, disable)); - - let offending_threshold = - T::OffendingValidatorsThreshold::get() * validators.len() as u32; - - if offending.len() >= offending_threshold as usize { - // force a new era, to select a new validator set - >::ensure_new_era() - } - - if disable { - T::SessionInterface::disable_validator(validator_index_u32); - } - }, - Ok(index) => { - if disable && !offending[index].1 { - // the validator had previously offended without being disabled, - // let's make sure we disable it now - offending[index].1 = true; - T::SessionInterface::disable_validator(validator_index_u32); - } - }, - } - }); -} - -/// Slash nominators. Accepts general parameters and the prior slash percentage of the validator. -/// -/// Returns the amount of reward to pay out. -fn slash_nominators( - params: SlashParams, - prior_slash_p: Perbill, - nominators_slashed: &mut Vec<(T::AccountId, BalanceOf)>, -) -> BalanceOf { - let mut reward_payout = Zero::zero(); - - nominators_slashed.reserve(params.exposure.others.len()); - for nominator in ¶ms.exposure.others { - let stash = &nominator.who; - let mut nom_slashed = Zero::zero(); - - // the era slash of a nominator always grows, if the validator - // had a new max slash for the era. - let era_slash = { - let own_slash_prior = prior_slash_p * nominator.value; - let own_slash_by_validator = params.slash * nominator.value; - let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior); - - let mut era_slash = - NominatorSlashInEra::::get(¶ms.slash_era, stash).unwrap_or_else(Zero::zero); - era_slash += own_slash_difference; - NominatorSlashInEra::::insert(¶ms.slash_era, stash, &era_slash); - - era_slash - }; - - // compare the era slash against other eras in the same span. - { - let mut spans = fetch_spans::( - stash, - params.window_start, - &mut reward_payout, - &mut nom_slashed, - params.reward_proportion, - ); - - let target_span = spans.compare_and_update_span_slash(params.slash_era, era_slash); - - if target_span == Some(spans.span_index()) { - // end the span, but don't chill the nominator. - spans.end_span(params.now); - } - } - nominators_slashed.push((stash.clone(), nom_slashed)); - } - - reward_payout -} - -// helper struct for managing a set of spans we are currently inspecting. -// writes alterations to disk on drop, but only if a slash has been carried out. -// -// NOTE: alterations to slashing metadata should not be done after this is dropped. -// dropping this struct applies any necessary slashes, which can lead to free balance -// being 0, and the account being garbage-collected -- a dead account should get no new -// metadata. -struct InspectingSpans<'a, T: Config + 'a> { - dirty: bool, - window_start: EraIndex, - stash: &'a T::AccountId, - spans: SlashingSpans, - paid_out: &'a mut BalanceOf, - slash_of: &'a mut BalanceOf, - reward_proportion: Perbill, - _marker: sp_std::marker::PhantomData, -} - -// fetches the slashing spans record for a stash account, initializing it if necessary. -fn fetch_spans<'a, T: Config + 'a>( - stash: &'a T::AccountId, - window_start: EraIndex, - paid_out: &'a mut BalanceOf, - slash_of: &'a mut BalanceOf, - reward_proportion: Perbill, -) -> InspectingSpans<'a, T> { - let spans = crate::SlashingSpans::::get(stash).unwrap_or_else(|| { - let spans = SlashingSpans::new(window_start); - crate::SlashingSpans::::insert(stash, &spans); - spans - }); - - InspectingSpans { - dirty: false, - window_start, - stash, - spans, - slash_of, - paid_out, - reward_proportion, - _marker: sp_std::marker::PhantomData, - } -} - -impl<'a, T: 'a + Config> InspectingSpans<'a, T> { - fn span_index(&self) -> SpanIndex { - self.spans.span_index - } - - fn end_span(&mut self, now: EraIndex) { - self.dirty = self.spans.end_span(now) || self.dirty; - } - - // add some value to the slash of the staker. - // invariant: the staker is being slashed for non-zero value here - // although `amount` may be zero, as it is only a difference. - fn add_slash(&mut self, amount: BalanceOf, slash_era: EraIndex) { - *self.slash_of += amount; - self.spans.last_nonzero_slash = sp_std::cmp::max(self.spans.last_nonzero_slash, slash_era); - } - - // find the span index of the given era, if covered. - fn era_span(&self, era: EraIndex) -> Option { - self.spans.iter().find(|span| span.contains_era(era)) - } - - // compares the slash in an era to the overall current span slash. - // if it's higher, applies the difference of the slashes and then updates the span on disk. - // - // returns the span index of the era where the slash occurred, if any. - fn compare_and_update_span_slash( - &mut self, - slash_era: EraIndex, - slash: BalanceOf, - ) -> Option { - let target_span = self.era_span(slash_era)?; - let span_slash_key = (self.stash.clone(), target_span.index); - let mut span_record = SpanSlash::::get(&span_slash_key); - let mut changed = false; - - let reward = if span_record.slashed < slash { - // new maximum span slash. apply the difference. - let difference = slash - span_record.slashed; - span_record.slashed = slash; - - // compute reward. - let reward = - REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out); - - self.add_slash(difference, slash_era); - changed = true; - - reward - } else if span_record.slashed == slash { - // compute reward. no slash difference to apply. - REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out) - } else { - Zero::zero() - }; - - if !reward.is_zero() { - changed = true; - span_record.paid_out += reward; - *self.paid_out += reward; - } - - if changed { - self.dirty = true; - SpanSlash::::insert(&span_slash_key, &span_record); - } - - Some(target_span.index) - } -} - -impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> { - fn drop(&mut self) { - // only update on disk if we slashed this account. - if !self.dirty { - return - } - - if let Some((start, end)) = self.spans.prune(self.window_start) { - for span_index in start..end { - SpanSlash::::remove(&(self.stash.clone(), span_index)); - } - } - - crate::SlashingSpans::::insert(self.stash, &self.spans); - } -} - -/// Clear slashing metadata for an obsolete era. -pub(crate) fn clear_era_metadata(obsolete_era: EraIndex) { - #[allow(deprecated)] - ValidatorSlashInEra::::remove_prefix(&obsolete_era, None); - #[allow(deprecated)] - NominatorSlashInEra::::remove_prefix(&obsolete_era, None); -} - -/// Clear slashing metadata for a dead account. -pub(crate) fn clear_stash_metadata( - stash: &T::AccountId, - num_slashing_spans: u32, -) -> DispatchResult { - let spans = match crate::SlashingSpans::::get(stash) { - None => return Ok(()), - Some(s) => s, - }; - - ensure!( - num_slashing_spans as usize >= spans.iter().count(), - Error::::IncorrectSlashingSpans - ); - - crate::SlashingSpans::::remove(stash); - - // kill slashing-span metadata for account. - // - // this can only happen while the account is staked _if_ they are completely slashed. - // in that case, they may re-bond, but it would count again as span 0. Further ancient - // slashes would slash into this new bond, since metadata has now been cleared. - for span in spans.iter() { - SpanSlash::::remove(&(stash.clone(), span.index)); - } - - Ok(()) -} - -// apply the slash to a stash account, deducting any missing funds from the reward -// payout, saturating at 0. this is mildly unfair but also an edge-case that -// can only occur when overlapping locked funds have been slashed. -pub fn do_slash( - stash: &T::AccountId, - value: BalanceOf, - reward_payout: &mut BalanceOf, - slashed_imbalance: &mut NegativeImbalanceOf, - slash_era: EraIndex, -) { - let controller = match >::bonded(stash).defensive() { - None => return, - Some(c) => c, - }; - - let mut ledger = match >::ledger(&controller) { - Some(ledger) => ledger, - None => return, // nothing to do. - }; - - let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); - - if !value.is_zero() { - let (imbalance, missing) = T::Currency::slash(stash, value); - slashed_imbalance.subsume(imbalance); - - if !missing.is_zero() { - // deduct overslash from the reward payout - *reward_payout = reward_payout.saturating_sub(missing); - } - - >::update_ledger(&controller, &ledger); - - // trigger the event - >::deposit_event(super::Event::::Slashed { - staker: stash.clone(), - amount: value, - }); - } -} - -/// Apply a previously-unapplied slash. -pub(crate) fn apply_slash( - unapplied_slash: UnappliedSlash>, - slash_era: EraIndex, -) { - let mut slashed_imbalance = NegativeImbalanceOf::::zero(); - let mut reward_payout = unapplied_slash.payout; - - do_slash::( - &unapplied_slash.validator, - unapplied_slash.own, - &mut reward_payout, - &mut slashed_imbalance, - slash_era, - ); - - for &(ref nominator, nominator_slash) in &unapplied_slash.others { - do_slash::( - nominator, - nominator_slash, - &mut reward_payout, - &mut slashed_imbalance, - slash_era, - ); - } - - pay_reporters::(reward_payout, slashed_imbalance, &unapplied_slash.reporters); -} - -/// Apply a reward payout to some reporters, paying the rewards out of the slashed imbalance. -fn pay_reporters( - reward_payout: BalanceOf, - slashed_imbalance: NegativeImbalanceOf, - reporters: &[T::AccountId], -) { - if reward_payout.is_zero() || reporters.is_empty() { - // nobody to pay out to or nothing to pay; - // just treat the whole value as slashed. - T::Slash::on_unbalanced(slashed_imbalance); - return - } - - // take rewards out of the slashed imbalance. - let reward_payout = reward_payout.min(slashed_imbalance.peek()); - let (mut reward_payout, mut value_slashed) = slashed_imbalance.split(reward_payout); - - let per_reporter = reward_payout.peek() / (reporters.len() as u32).into(); - for reporter in reporters { - let (reporter_reward, rest) = reward_payout.split(per_reporter); - reward_payout = rest; - - // this cancels out the reporter reward imbalance internally, leading - // to no change in total issuance. - T::Currency::resolve_creating(reporter, reporter_reward); - } - - // the rest goes to the on-slash imbalance handler (e.g. treasury) - value_slashed.subsume(reward_payout); // remainder of reward division remains. - T::Slash::on_unbalanced(value_slashed); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn span_contains_era() { - // unbounded end - let span = SlashingSpan { index: 0, start: 1000, length: None }; - assert!(!span.contains_era(0)); - assert!(!span.contains_era(999)); - - assert!(span.contains_era(1000)); - assert!(span.contains_era(1001)); - assert!(span.contains_era(10000)); - - // bounded end - non-inclusive range. - let span = SlashingSpan { index: 0, start: 1000, length: Some(10) }; - assert!(!span.contains_era(0)); - assert!(!span.contains_era(999)); - - assert!(span.contains_era(1000)); - assert!(span.contains_era(1001)); - assert!(span.contains_era(1009)); - assert!(!span.contains_era(1010)); - assert!(!span.contains_era(1011)); - } - - #[test] - fn single_slashing_span() { - let spans = SlashingSpans { - span_index: 0, - last_start: 1000, - last_nonzero_slash: 0, - prior: Vec::new(), - }; - - assert_eq!( - spans.iter().collect::>(), - vec![SlashingSpan { index: 0, start: 1000, length: None }], - ); - } - - #[test] - fn many_prior_spans() { - let spans = SlashingSpans { - span_index: 10, - last_start: 1000, - last_nonzero_slash: 0, - prior: vec![10, 9, 8, 10], - }; - - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, - SlashingSpan { index: 7, start: 973, length: Some(8) }, - SlashingSpan { index: 6, start: 963, length: Some(10) }, - ], - ) - } - - #[test] - fn pruning_spans() { - let mut spans = SlashingSpans { - span_index: 10, - last_start: 1000, - last_nonzero_slash: 0, - prior: vec![10, 9, 8, 10], - }; - - assert_eq!(spans.prune(981), Some((6, 8))); - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, - ], - ); - - assert_eq!(spans.prune(982), None); - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, - ], - ); - - assert_eq!(spans.prune(989), None); - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, - ], - ); - - assert_eq!(spans.prune(1000), Some((8, 10))); - assert_eq!( - spans.iter().collect::>(), - vec![SlashingSpan { index: 10, start: 1000, length: None },], - ); - - assert_eq!(spans.prune(2000), None); - assert_eq!( - spans.iter().collect::>(), - vec![SlashingSpan { index: 10, start: 2000, length: None },], - ); - - // now all in one shot. - let mut spans = SlashingSpans { - span_index: 10, - last_start: 1000, - last_nonzero_slash: 0, - prior: vec![10, 9, 8, 10], - }; - assert_eq!(spans.prune(2000), Some((6, 10))); - assert_eq!( - spans.iter().collect::>(), - vec![SlashingSpan { index: 10, start: 2000, length: None },], - ); - } - - #[test] - fn ending_span() { - let mut spans = SlashingSpans { - span_index: 1, - last_start: 10, - last_nonzero_slash: 0, - prior: Vec::new(), - }; - - assert!(spans.end_span(10)); - - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 2, start: 11, length: None }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, - ], - ); - - assert!(spans.end_span(15)); - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 3, start: 16, length: None }, - SlashingSpan { index: 2, start: 11, length: Some(5) }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, - ], - ); - - // does nothing if not a valid end. - assert!(!spans.end_span(15)); - assert_eq!( - spans.iter().collect::>(), - vec![ - SlashingSpan { index: 3, start: 16, length: None }, - SlashingSpan { index: 2, start: 11, length: Some(5) }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, - ], - ); - } -} diff --git a/pallets/staking/src/testing_utils.rs b/pallets/staking/src/testing_utils.rs deleted file mode 100644 index 6182f66f0..000000000 --- a/pallets/staking/src/testing_utils.rs +++ /dev/null @@ -1,240 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Testing utils for staking. Provides some common functions to setup staking state, such as -//! bonding validators, nominators, and generating different types of solutions. - -use frame_benchmarking::account; -use frame_election_provider_support::SortedListProvider; -use frame_support::{pallet_prelude::*, traits::Currency}; -use frame_system::RawOrigin; -use rand_chacha::{ - rand_core::{RngCore, SeedableRng}, - ChaChaRng, -}; -use sp_io::hashing::blake2_256; -use sp_runtime::{traits::StaticLookup, Perbill}; -use sp_std::prelude::*; - -use crate::{Pallet as Staking, *}; - -const SEED: u32 = 0; - -/// This function removes all validators and nominators from storage. -pub fn clear_validators_and_nominators() { - #[allow(deprecated)] - Validators::::remove_all(); - - // whenever we touch nominators counter we should update `T::VoterList` as well. - #[allow(deprecated)] - Nominators::::remove_all(); - - // NOTE: safe to call outside block production - T::VoterList::unsafe_clear(); -} - -/// Grab a funded user. -pub fn create_funded_user( - string: &'static str, - n: u32, - balance_factor: u32, -) -> T::AccountId { - let user = account(string, n, SEED); - let balance = T::Currency::minimum_balance() * balance_factor.into(); - let _ = T::Currency::make_free_balance_be(&user, balance); - user -} - -/// Grab a funded user with max Balance. -pub fn create_funded_user_with_balance( - string: &'static str, - n: u32, - balance: BalanceOf, -) -> T::AccountId { - let user = account(string, n, SEED); - let _ = T::Currency::make_free_balance_be(&user, balance); - user -} - -/// Create a stash and controller pair. -pub fn create_stash_controller( - n: u32, - balance_factor: u32, - destination: RewardDestination, -) -> Result<(T::AccountId, T::AccountId), &'static str> { - let staker = create_funded_user::("stash", n, balance_factor); - let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond(RawOrigin::Signed(staker.clone()).into(), amount, destination)?; - Ok((staker.clone(), staker)) -} - -/// Create a unique stash and controller pair. -pub fn create_unique_stash_controller( - n: u32, - balance_factor: u32, - destination: RewardDestination, - dead_controller: bool, -) -> Result<(T::AccountId, T::AccountId), &'static str> { - let stash = create_funded_user::("stash", n, balance_factor); - - let controller = if dead_controller { - create_funded_user::("controller", n, 0) - } else { - create_funded_user::("controller", n, balance_factor) - }; - let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond(RawOrigin::Signed(stash.clone()).into(), amount, destination)?; - - // update ledger to be a *different* controller to stash - if let Some(l) = Ledger::::take(&stash) { - >::insert(&controller, l); - } - // update bonded account to be unique controller - >::insert(&stash, &controller); - - Ok((stash, controller)) -} - -/// Create a stash and controller pair with fixed balance. -pub fn create_stash_controller_with_balance( - n: u32, - balance: crate::BalanceOf, - destination: RewardDestination, -) -> Result<(T::AccountId, T::AccountId), &'static str> { - let staker = create_funded_user_with_balance::("stash", n, balance); - Staking::::bond(RawOrigin::Signed(staker.clone()).into(), balance, destination)?; - Ok((staker.clone(), staker)) -} - -/// Create a stash and controller pair, where payouts go to a dead payee account. This is used to -/// test worst case payout scenarios. -pub fn create_stash_and_dead_payee( - n: u32, - balance_factor: u32, -) -> Result<(T::AccountId, T::AccountId), &'static str> { - let staker = create_funded_user::("stash", n, 0); - // payee has no funds - let payee = create_funded_user::("payee", n, 0); - let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond( - RawOrigin::Signed(staker.clone()).into(), - amount, - RewardDestination::Account(payee), - )?; - Ok((staker.clone(), staker)) -} - -/// create `max` validators. -pub fn create_validators( - max: u32, - balance_factor: u32, -) -> Result>, &'static str> { - create_validators_with_seed::(max, balance_factor, 0) -} - -/// create `max` validators, with a seed to help unintentional prevent account collisions. -pub fn create_validators_with_seed( - max: u32, - balance_factor: u32, - seed: u32, -) -> Result>, &'static str> { - let mut validators: Vec> = Vec::with_capacity(max as usize); - for i in 0..max { - let (stash, controller) = - create_stash_controller::(i + seed, balance_factor, RewardDestination::Staked)?; - let validator_prefs = - ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; - let stash_lookup = T::Lookup::unlookup(stash); - validators.push(stash_lookup); - } - Ok(validators) -} - -/// This function generates validators and nominators who are randomly nominating -/// `edge_per_nominator` random validators (until `to_nominate` if provided). -/// -/// NOTE: This function will remove any existing validators or nominators to ensure -/// we are working with a clean state. -/// -/// Parameters: -/// - `validators`: number of bonded validators -/// - `nominators`: number of bonded nominators. -/// - `edge_per_nominator`: number of edge (vote) per nominator. -/// - `randomize_stake`: whether to randomize the stakes. -/// - `to_nominate`: if `Some(n)`, only the first `n` bonded validator are voted upon. Else, all of -/// them are considered and `edge_per_nominator` random validators are voted for. -/// -/// Return the validators chosen to be nominated. -pub fn create_validators_with_nominators_for_era( - validators: u32, - nominators: u32, - edge_per_nominator: usize, - randomize_stake: bool, - to_nominate: Option, -) -> Result>, &'static str> { - clear_validators_and_nominators::(); - - let mut validators_stash: Vec> = Vec::with_capacity(validators as usize); - let mut rng = ChaChaRng::from_seed(SEED.using_encoded(blake2_256)); - - // Create validators - for i in 0..validators { - let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 }; - let (v_stash, v_controller) = - create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; - let validator_prefs = - ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(RawOrigin::Signed(v_controller.clone()).into(), validator_prefs)?; - let stash_lookup = T::Lookup::unlookup(v_stash.clone()); - validators_stash.push(stash_lookup.clone()); - } - - let to_nominate = to_nominate.unwrap_or(validators_stash.len() as u32) as usize; - let validator_chosen = validators_stash[0..to_nominate].to_vec(); - - // Create nominators - for j in 0..nominators { - let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 }; - let (_n_stash, n_controller) = - create_stash_controller::(u32::MAX - j, balance_factor, RewardDestination::Staked)?; - - // Have them randomly validate - let mut available_validators = validator_chosen.clone(); - let mut selected_validators: Vec> = - Vec::with_capacity(edge_per_nominator); - - for _ in 0..validators.min(edge_per_nominator as u32) { - let selected = rng.next_u32() as usize % available_validators.len(); - let validator = available_validators.remove(selected); - selected_validators.push(validator); - } - Staking::::nominate( - RawOrigin::Signed(n_controller.clone()).into(), - selected_validators, - )?; - } - - ValidatorCount::::put(validators); - - Ok(validator_chosen) -} - -/// get the current era. -pub fn current_era() -> EraIndex { - >::current_era().unwrap_or(0) -} diff --git a/pallets/staking/src/tests.rs b/pallets/staking/src/tests.rs deleted file mode 100644 index 6c1514376..000000000 --- a/pallets/staking/src/tests.rs +++ /dev/null @@ -1,5847 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests for the module. - -use frame_election_provider_support::{ElectionProvider, SortedListProvider, Support}; -use frame_support::{ - assert_noop, assert_ok, assert_storage_noop, bounded_vec, - dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo}, - pallet_prelude::*, - traits::{Currency, Get, ReservableCurrency}, -}; -use mock::*; -use pallet_balances::Error as BalancesError; -use sp_runtime::{ - assert_eq_error_rate, - traits::{BadOrigin, Dispatchable}, - Perbill, Percent, Rounding, TokenError, -}; -use sp_staking::{ - offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - SessionIndex, -}; -use sp_std::prelude::*; -use substrate_test_utils::assert_eq_uvec; - -use super::{ConfigOp, Event, *}; - -#[test] -fn set_staking_configs_works() { - ExtBuilder::default().build_and_execute(|| { - // setting works - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Set(1_500), - ConfigOp::Set(2_000), - ConfigOp::Set(10), - ConfigOp::Set(20), - ConfigOp::Set(Percent::from_percent(75)), - ConfigOp::Set(Zero::zero()) - )); - assert_eq!(MinNominatorBond::::get(), 1_500); - assert_eq!(MinValidatorBond::::get(), 2_000); - assert_eq!(MaxNominatorsCount::::get(), Some(10)); - assert_eq!(MaxValidatorsCount::::get(), Some(20)); - assert_eq!(ChillThreshold::::get(), Some(Percent::from_percent(75))); - assert_eq!(MinCommission::::get(), Perbill::from_percent(0)); - - // noop does nothing - assert_storage_noop!(assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop - ))); - - // removing works - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove - )); - assert_eq!(MinNominatorBond::::get(), 0); - assert_eq!(MinValidatorBond::::get(), 0); - assert_eq!(MaxNominatorsCount::::get(), None); - assert_eq!(MaxValidatorsCount::::get(), None); - assert_eq!(ChillThreshold::::get(), None); - assert_eq!(MinCommission::::get(), Perbill::from_percent(0)); - }); -} - -#[test] -fn force_unstake_works() { - ExtBuilder::default().build_and_execute(|| { - // Account 11 (also controller) is stashed and locked - assert_eq!(Staking::bonded(&11), Some(11)); - // Adds 2 slashing spans - add_slash(&11); - // Cant transfer - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10), - TokenError::Frozen, - ); - // Force unstake requires root. - assert_noop!(Staking::force_unstake(RuntimeOrigin::signed(11), 11, 2), BadOrigin); - // Force unstake needs correct number of slashing spans (for weight calculation) - assert_noop!( - Staking::force_unstake(RuntimeOrigin::root(), 11, 0), - Error::::IncorrectSlashingSpans - ); - // We now force them to unstake - assert_ok!(Staking::force_unstake(RuntimeOrigin::root(), 11, 2)); - // No longer bonded. - assert_eq!(Staking::bonded(&11), None); - // Transfer works. - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10)); - }); -} - -#[test] -fn kill_stash_works() { - ExtBuilder::default().build_and_execute(|| { - // Account 11 (also controller) is stashed and locked - assert_eq!(Staking::bonded(&11), Some(11)); - // Adds 2 slashing spans - add_slash(&11); - // Only can kill a stash account - assert_noop!(Staking::kill_stash(&12, 0), Error::::NotStash); - // Respects slashing span count - assert_noop!(Staking::kill_stash(&11, 0), Error::::IncorrectSlashingSpans); - // Correct inputs, everything works - assert_ok!(Staking::kill_stash(&11, 2)); - // No longer bonded. - assert_eq!(Staking::bonded(&11), None); - }); -} - -#[test] -fn basic_setup_works() { - // Verifies initial conditions of mock - ExtBuilder::default().build_and_execute(|| { - // Account 11 is stashed and locked, and is the controller - assert_eq!(Staking::bonded(&11), Some(11)); - // Account 21 is stashed and locked and is the controller - assert_eq!(Staking::bonded(&21), Some(21)); - // Account 1 is not a stashed - assert_eq!(Staking::bonded(&1), None); - - // Account 11 controls its own stash, which is 100 * balance_factor units - assert_eq!( - Staking::ledger(&11).unwrap(), - StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - } - ); - // Account 21 controls its own stash, which is 200 * balance_factor units - assert_eq!( - Staking::ledger(&21), - Some(StakingLedger { - stash: 21, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - // Account 1 does not control any stash - assert_eq!(Staking::ledger(&1), None); - - // ValidatorPrefs are default - assert_eq_uvec!( - >::iter().collect::>(), - vec![ - (31, ValidatorPrefs::default()), - (21, ValidatorPrefs::default()), - (11, ValidatorPrefs::default()) - ] - ); - - assert_eq!( - Staking::ledger(101), - Some(StakingLedger { - stash: 101, - total: 500, - active: 500, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - assert_eq!( - Staking::eras_stakers(active_era(), 11), - Exposure { - total: 1125, - own: 1000, - others: vec![IndividualExposure { who: 101, value: 125 }] - }, - ); - assert_eq!( - Staking::eras_stakers(active_era(), 21), - Exposure { - total: 1375, - own: 1000, - others: vec![IndividualExposure { who: 101, value: 375 }] - }, - ); - - // initial total stake = 1125 + 1375 - assert_eq!(Staking::eras_total_stake(active_era()), 2500); - - // The number of validators required. - assert_eq!(Staking::validator_count(), 2); - - // Initial Era and session - assert_eq!(active_era(), 0); - - // Account 10 has `balance_factor` free balance - assert_eq!(Balances::free_balance(10), 1); - assert_eq!(Balances::free_balance(10), 1); - - // New era is not being forced - assert_eq!(Staking::force_era(), Forcing::NotForcing); - }); -} - -#[test] -fn change_controller_works() { - ExtBuilder::default().build_and_execute(|| { - let (stash, controller) = testing_utils::create_unique_stash_controller::( - 0, - 100, - RewardDestination::Staked, - false, - ) - .unwrap(); - - // ensure `stash` and `controller` are bonded as stash controller pair. - assert_eq!(Staking::bonded(&stash), Some(controller)); - - // `controller` can control `stash` who is initially a validator. - assert_ok!(Staking::chill(RuntimeOrigin::signed(controller))); - - // sets controller back to `stash`. - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(stash))); - assert_eq!(Staking::bonded(&stash), Some(stash)); - mock::start_active_era(1); - - // `controller` is no longer in control. `stash` is now controller. - assert_noop!( - Staking::validate(RuntimeOrigin::signed(controller), ValidatorPrefs::default()), - Error::::NotController, - ); - assert_ok!(Staking::validate(RuntimeOrigin::signed(stash), ValidatorPrefs::default())); - }) -} - -#[test] -fn change_controller_already_paired_once_stash() { - ExtBuilder::default().build_and_execute(|| { - // 10 and 11 are bonded as controller and stash respectively. - assert_eq!(Staking::bonded(&11), Some(11)); - - // 11 is initially a validator. - assert_ok!(Staking::chill(RuntimeOrigin::signed(11))); - - // Controller cannot change once matching with stash. - assert_noop!( - Staking::set_controller(RuntimeOrigin::signed(11)), - Error::::AlreadyPaired - ); - assert_eq!(Staking::bonded(&11), Some(11)); - mock::start_active_era(1); - - // 10 is no longer in control. - assert_noop!( - Staking::validate(RuntimeOrigin::signed(10), ValidatorPrefs::default()), - Error::::NotController, - ); - assert_ok!(Staking::validate(RuntimeOrigin::signed(11), ValidatorPrefs::default())); - }) -} - -#[test] -fn rewards_should_work() { - ExtBuilder::default().nominate(true).session_per_era(3).build_and_execute(|| { - let init_balance_11 = Balances::total_balance(&11); - let init_balance_21 = Balances::total_balance(&21); - let init_balance_101 = Balances::total_balance(&101); - - // Set payees - Payee::::insert(11, RewardDestination::Controller); - Payee::::insert(21, RewardDestination::Controller); - Payee::::insert(101, RewardDestination::Controller); - - Pallet::::reward_by_ids(vec![(11, 50)]); - Pallet::::reward_by_ids(vec![(11, 50)]); - // This is the second validator of the current elected set. - Pallet::::reward_by_ids(vec![(21, 50)]); - - // Compute total payout now for whole duration of the session. - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - let maximum_payout = maximum_payout_for_duration(reward_time_per_era()); - - start_session(1); - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - assert_eq!(Balances::total_balance(&11), init_balance_11); - assert_eq!(Balances::total_balance(&21), init_balance_21); - assert_eq!(Balances::total_balance(&101), init_balance_101); - assert_eq!( - Staking::eras_reward_points(active_era()), - EraRewardPoints { - total: 50 * 3, - individual: vec![(11, 100), (21, 50)].into_iter().collect(), - } - ); - let part_for_11 = Perbill::from_rational::(1000, 1125); - let part_for_21 = Perbill::from_rational::(1000, 1375); - let part_for_101_from_11 = Perbill::from_rational::(125, 1125); - let part_for_101_from_21 = Perbill::from_rational::(375, 1375); - - start_session(2); - start_session(3); - - assert_eq!(active_era(), 1); - assert_eq!(mock::RewardRemainderUnbalanced::get(), maximum_payout - total_payout_0,); - assert_eq!( - *mock::staking_events().last().unwrap(), - Event::EraPaid { - era_index: 0, - validator_payout: total_payout_0, - remainder: maximum_payout - total_payout_0 - } - ); - mock::make_all_reward_payment(0); - - assert_eq_error_rate!( - Balances::total_balance(&11), - init_balance_11 + part_for_11 * total_payout_0 * 2 / 3, - 2, - ); - assert_eq_error_rate!( - Balances::total_balance(&21), - init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, - 2, - ); - assert_eq_error_rate!( - Balances::total_balance(&101), - init_balance_101 + - part_for_101_from_11 * total_payout_0 * 2 / 3 + - part_for_101_from_21 * total_payout_0 * 1 / 3, - 2 - ); - - assert_eq_uvec!(Session::validators(), vec![11, 21]); - Pallet::::reward_by_ids(vec![(11, 1)]); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - - mock::start_active_era(2); - assert_eq!( - mock::RewardRemainderUnbalanced::get(), - maximum_payout * 2 - total_payout_0 - total_payout_1, - ); - assert_eq!( - *mock::staking_events().last().unwrap(), - Event::EraPaid { - era_index: 1, - validator_payout: total_payout_1, - remainder: maximum_payout - total_payout_1 - } - ); - mock::make_all_reward_payment(1); - - assert_eq_error_rate!( - Balances::total_balance(&11), - init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1), - 2, - ); - assert_eq_error_rate!( - Balances::total_balance(&21), - init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, - 2, - ); - assert_eq_error_rate!( - Balances::total_balance(&101), - init_balance_101 + - part_for_101_from_11 * (total_payout_0 * 2 / 3 + total_payout_1) + - part_for_101_from_21 * total_payout_0 * 1 / 3, - 2 - ); - }); -} - -#[test] -fn staking_should_work() { - ExtBuilder::default().nominate(false).build_and_execute(|| { - // remember + compare this along with the test. - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // put some money in account that we'll use. - for i in 1..5 { - let _ = Balances::make_free_balance_be(&i, 2000); - } - - // --- Block 2: - start_session(2); - // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); - assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(3), - SessionKeys { other: 4.into() }, - vec![] - )); - - // No effects will be seen so far. - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // --- Block 3: - start_session(3); - - // No effects will be seen so far. Era has not been yet triggered. - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // --- Block 4: the validators will now be queued. - start_session(4); - assert_eq!(active_era(), 1); - - // --- Block 5: the validators are still in queue. - start_session(5); - - // --- Block 6: the validators will now be changed. - start_session(6); - - assert_eq_uvec!(validator_controllers(), vec![21, 3]); - // --- Block 6: Unstake 4 as a validator, freeing up the balance stashed in 3 - // 4 will chill - Staking::chill(RuntimeOrigin::signed(3)).unwrap(); - - // --- Block 7: nothing. 3 is still there. - start_session(7); - assert_eq_uvec!(validator_controllers(), vec![21, 3]); - - // --- Block 8: - start_session(8); - - // --- Block 9: 4 will not be a validator. - start_session(9); - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // Note: the stashed value of 4 is still lock - assert_eq!( - Staking::ledger(&3), - Some(StakingLedger { - stash: 3, - total: 1500, - active: 1500, - unlocking: Default::default(), - claimed_rewards: bounded_vec![0], - }) - ); - // e.g. it cannot reserve more than 500 that it has free from the total 2000 - assert_noop!(Balances::reserve(&3, 501), BalancesError::::LiquidityRestrictions); - assert_ok!(Balances::reserve(&3, 409)); - }); -} - -#[test] -fn blocking_and_kicking_works() { - ExtBuilder::default() - .minimum_validator_count(1) - .validator_count(4) - .nominate(true) - .build_and_execute(|| { - // block validator 10/11 - assert_ok!(Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { blocked: true, ..Default::default() } - )); - // attempt to nominate from 100/101... - assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![11])); - // should have worked since we're already nominated them - assert_eq!(Nominators::::get(&101).unwrap().targets, vec![11]); - // kick the nominator - assert_ok!(Staking::kick(RuntimeOrigin::signed(11), vec![101])); - // should have been kicked now - assert!(Nominators::::get(&101).unwrap().targets.is_empty()); - // attempt to nominate from 100/101... - assert_noop!( - Staking::nominate(RuntimeOrigin::signed(101), vec![11]), - Error::::BadTarget - ); - }); -} - -#[test] -fn less_than_needed_candidates_works() { - ExtBuilder::default() - .minimum_validator_count(1) - .validator_count(4) - .nominate(false) - .build_and_execute(|| { - assert_eq!(Staking::validator_count(), 4); - assert_eq!(Staking::minimum_validator_count(), 1); - assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]); - - mock::start_active_era(1); - - // Previous set is selected. NO election algorithm is even executed. - assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]); - - // But the exposure is updated in a simple way. No external votes exists. - // This is purely self-vote. - assert!(ErasStakers::::iter_prefix_values(active_era()) - .all(|exposure| exposure.others.is_empty())); - }); -} - -#[test] -fn no_candidate_emergency_condition() { - ExtBuilder::default() - .minimum_validator_count(1) - .validator_count(15) - .set_status(41, StakerStatus::Validator) - .nominate(false) - .build_and_execute(|| { - // initial validators - assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]); - let prefs = ValidatorPrefs { commission: Perbill::one(), ..Default::default() }; - Validators::::insert(11, prefs.clone()); - - // set the minimum validator count. - MinimumValidatorCount::::put(11); - - // try to chill - let res = Staking::chill(RuntimeOrigin::signed(11)); - assert_ok!(res); - - let current_era = CurrentEra::::get(); - - // try trigger new era - mock::run_to_block(21); - assert_eq!(*staking_events().last().unwrap(), Event::StakingElectionFailed); - // No new era is created - assert_eq!(current_era, CurrentEra::::get()); - - // Go to far further session to see if validator have changed - mock::run_to_block(100); - - // Previous ones are elected. chill is not effective in active era (as era hasn't - // changed) - assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]); - // The chill is still pending. - assert!(!Validators::::contains_key(11)); - // No new era is created. - assert_eq!(current_era, CurrentEra::::get()); - }); -} - -#[test] -fn nominating_and_rewards_should_work() { - ExtBuilder::default() - .nominate(false) - .set_status(41, StakerStatus::Validator) - .set_status(11, StakerStatus::Idle) - .set_status(31, StakerStatus::Idle) - .build_and_execute(|| { - // initial validators. - assert_eq_uvec!(validator_controllers(), vec![41, 21]); - - // re-validate with 11 and 31. - assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); - assert_ok!(Staking::validate(RuntimeOrigin::signed(31), Default::default())); - - // Set payee to controller. - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(11), - RewardDestination::Controller - )); - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(21), - RewardDestination::Controller - )); - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(31), - RewardDestination::Controller - )); - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(41), - RewardDestination::Controller - )); - - // give the man some money - let initial_balance = 1000; - for i in [1, 3, 5, 11, 21].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); - } - - // bond two account pairs and state interest in nomination. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 21, 31])); - - assert_ok!(Staking::bond( - RuntimeOrigin::signed(3), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11, 21, 41])); - - // the total reward for era 0 - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(41, 1)]); - Pallet::::reward_by_ids(vec![(21, 1)]); - - mock::start_active_era(1); - - // 10 and 20 have more votes, they will be chosen. - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // old validators must have already received some rewards. - let initial_balance_41 = Balances::total_balance(&41); - let mut initial_balance_21 = Balances::total_balance(&21); - mock::make_all_reward_payment(0); - assert_eq!(Balances::total_balance(&41), initial_balance_41 + total_payout_0 / 2); - assert_eq!(Balances::total_balance(&21), initial_balance_21 + total_payout_0 / 2); - initial_balance_21 = Balances::total_balance(&21); - - assert_eq!(ErasStakers::::iter_prefix_values(active_era()).count(), 2); - assert_eq!( - Staking::eras_stakers(active_era(), 11), - Exposure { - total: 1000 + 800, - own: 1000, - others: vec![ - IndividualExposure { who: 1, value: 400 }, - IndividualExposure { who: 3, value: 400 }, - ] - }, - ); - assert_eq!( - Staking::eras_stakers(active_era(), 21), - Exposure { - total: 1000 + 1200, - own: 1000, - others: vec![ - IndividualExposure { who: 1, value: 600 }, - IndividualExposure { who: 3, value: 600 }, - ] - }, - ); - - // the total reward for era 1 - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(21, 2)]); - Pallet::::reward_by_ids(vec![(11, 1)]); - - mock::start_active_era(2); - - // nothing else will happen, era ends and rewards are paid again, it is expected that - // nominators will also be paid. See below - - mock::make_all_reward_payment(1); - let payout_for_11 = total_payout_1 / 3; - let payout_for_21 = 2 * total_payout_1 / 3; - // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> - // 2/9 + 3/11 - assert_eq_error_rate!( - Balances::total_balance(&1), - initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11), - 2, - ); - // Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> - // 2/9 + 3/11 - assert_eq_error_rate!( - Balances::total_balance(&3), - initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11), - 2, - ); - - // Validator 11: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 - assert_eq_error_rate!( - Balances::total_balance(&11), - initial_balance + 5 * payout_for_11 / 9, - 2, - ); - // Validator 21: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = - // 5/11 - assert_eq_error_rate!( - Balances::total_balance(&21), - initial_balance_21 + 5 * payout_for_21 / 11, - 2, - ); - }); -} - -#[test] -fn nominators_also_get_slashed_pro_rata() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - let slash_percent = Perbill::from_percent(5); - let initial_exposure = Staking::eras_stakers(active_era(), 11); - // 101 is a nominator for 11 - assert_eq!(initial_exposure.others.first().unwrap().who, 101); - - // staked values; - let nominator_stake = Staking::ledger(101).unwrap().active; - let nominator_balance = balances(&101).0; - let validator_stake = Staking::ledger(11).unwrap().active; - let validator_balance = balances(&11).0; - let exposed_stake = initial_exposure.total; - let exposed_validator = initial_exposure.own; - let exposed_nominator = initial_exposure.others.first().unwrap().value; - - // 11 goes offline - on_offence_now( - &[OffenceDetails { offender: (11, initial_exposure.clone()), reporters: vec![] }], - &[slash_percent], - ); - - // both stakes must have been decreased. - assert!(Staking::ledger(101).unwrap().active < nominator_stake); - assert!(Staking::ledger(11).unwrap().active < validator_stake); - - let slash_amount = slash_percent * exposed_stake; - let validator_share = - Perbill::from_rational(exposed_validator, exposed_stake) * slash_amount; - let nominator_share = - Perbill::from_rational(exposed_nominator, exposed_stake) * slash_amount; - - // both slash amounts need to be positive for the test to make sense. - assert!(validator_share > 0); - assert!(nominator_share > 0); - - // both stakes must have been decreased pro-rata. - assert_eq!(Staking::ledger(101).unwrap().active, nominator_stake - nominator_share); - assert_eq!(Staking::ledger(11).unwrap().active, validator_stake - validator_share); - assert_eq!( - balances(&101).0, // free balance - nominator_balance - nominator_share, - ); - assert_eq!( - balances(&11).0, // free balance - validator_balance - validator_share, - ); - // Because slashing happened. - assert!(is_disabled(11)); - }); -} - -#[test] -fn double_staking_should_fail() { - // should test (in the same order): - // * an account already bonded as stash cannot be be stashed again. - // * an account already bonded as stash cannot nominate. - // * an account already bonded as controller can nominate. - ExtBuilder::default().build_and_execute(|| { - let arbitrary_value = 5; - let (stash, controller) = testing_utils::create_unique_stash_controller::( - 0, - arbitrary_value, - RewardDestination::default(), - false, - ) - .unwrap(); - - // 4 = not used so far, stash => not allowed. - assert_noop!( - Staking::bond( - RuntimeOrigin::signed(stash), - arbitrary_value.into(), - RewardDestination::default() - ), - Error::::AlreadyBonded, - ); - // stash => attempting to nominate should fail. - assert_noop!( - Staking::nominate(RuntimeOrigin::signed(stash), vec![1]), - Error::::NotController - ); - // controller => nominating should work. - assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![1])); - }); -} - -#[test] -fn double_controlling_attempt_should_fail() { - // should test (in the same order): - // * an account already bonded as controller CANNOT be reused as the controller of another - // account. - ExtBuilder::default().build_and_execute(|| { - let arbitrary_value = 5; - let (stash, _) = testing_utils::create_unique_stash_controller::( - 0, - arbitrary_value, - RewardDestination::default(), - false, - ) - .unwrap(); - - // Note that controller (same as stash) is reused => no-op. - assert_noop!( - Staking::bond( - RuntimeOrigin::signed(stash), - arbitrary_value.into(), - RewardDestination::default() - ), - Error::::AlreadyBonded, - ); - }); -} - -#[test] -fn session_and_eras_work_simple() { - ExtBuilder::default().period(1).build_and_execute(|| { - assert_eq!(active_era(), 0); - assert_eq!(current_era(), 0); - assert_eq!(Session::current_index(), 1); - assert_eq!(System::block_number(), 1); - - // Session 1: this is basically a noop. This has already been started. - start_session(1); - assert_eq!(Session::current_index(), 1); - assert_eq!(active_era(), 0); - assert_eq!(System::block_number(), 1); - - // Session 2: No change. - start_session(2); - assert_eq!(Session::current_index(), 2); - assert_eq!(active_era(), 0); - assert_eq!(System::block_number(), 2); - - // Session 3: Era increment. - start_session(3); - assert_eq!(Session::current_index(), 3); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 3); - - // Session 4: No change. - start_session(4); - assert_eq!(Session::current_index(), 4); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 4); - - // Session 5: No change. - start_session(5); - assert_eq!(Session::current_index(), 5); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 5); - - // Session 6: Era increment. - start_session(6); - assert_eq!(Session::current_index(), 6); - assert_eq!(active_era(), 2); - assert_eq!(System::block_number(), 6); - }); -} - -#[test] -fn session_and_eras_work_complex() { - ExtBuilder::default().period(5).build_and_execute(|| { - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 0); - assert_eq!(System::block_number(), 1); - - start_session(1); - assert_eq!(Session::current_index(), 1); - assert_eq!(active_era(), 0); - assert_eq!(System::block_number(), 5); - - start_session(2); - assert_eq!(Session::current_index(), 2); - assert_eq!(active_era(), 0); - assert_eq!(System::block_number(), 10); - - start_session(3); - assert_eq!(Session::current_index(), 3); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 15); - - start_session(4); - assert_eq!(Session::current_index(), 4); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 20); - - start_session(5); - assert_eq!(Session::current_index(), 5); - assert_eq!(active_era(), 1); - assert_eq!(System::block_number(), 25); - - start_session(6); - assert_eq!(Session::current_index(), 6); - assert_eq!(active_era(), 2); - assert_eq!(System::block_number(), 30); - }); -} - -#[test] -fn forcing_new_era_works() { - ExtBuilder::default().build_and_execute(|| { - // normal flow of session. - start_session(1); - assert_eq!(active_era(), 0); - - start_session(2); - assert_eq!(active_era(), 0); - - start_session(3); - assert_eq!(active_era(), 1); - - // no era change. - Staking::set_force_era(Forcing::ForceNone); - - start_session(4); - assert_eq!(active_era(), 1); - - start_session(5); - assert_eq!(active_era(), 1); - - start_session(6); - assert_eq!(active_era(), 1); - - start_session(7); - assert_eq!(active_era(), 1); - - // back to normal. - // this immediately starts a new session. - Staking::set_force_era(Forcing::NotForcing); - - start_session(8); - assert_eq!(active_era(), 1); - - start_session(9); - assert_eq!(active_era(), 2); - // forceful change - Staking::set_force_era(Forcing::ForceAlways); - - start_session(10); - assert_eq!(active_era(), 2); - - start_session(11); - assert_eq!(active_era(), 3); - - start_session(12); - assert_eq!(active_era(), 4); - - // just one forceful change - Staking::set_force_era(Forcing::ForceNew); - start_session(13); - assert_eq!(active_era(), 5); - assert_eq!(ForceEra::::get(), Forcing::NotForcing); - - start_session(14); - assert_eq!(active_era(), 6); - - start_session(15); - assert_eq!(active_era(), 6); - }); -} - -#[test] -fn cannot_transfer_staked_balance() { - // Tests that a stash account cannot transfer funds - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Confirm account 11 is stashed - assert_eq!(Staking::bonded(&11), Some(11)); - // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(11), 1000); - // Confirm account 11 (via controller) is totally staked - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - // Confirm account 11 cannot transfer as a result - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1), - TokenError::Frozen, - ); - - // Give account 11 extra free balance - let _ = Balances::make_free_balance_be(&11, 10000); - // Confirm that account 11 can now transfer some balance - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1)); - }); -} - -#[test] -fn cannot_transfer_staked_balance_2() { - // Tests that a stash account cannot transfer funds - // Same test as above but with 20, and more accurate. - // 21 has 2000 free balance but 1000 at stake - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Confirm account 21 is stashed - assert_eq!(Staking::bonded(&21), Some(21)); - // Confirm account 21 has some free balance - assert_eq!(Balances::free_balance(21), 2000); - // Confirm account 21 (via controller) is totally staked - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 1000); - // Confirm account 21 can transfer at most 1000 - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1001), - TokenError::Frozen, - ); - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1000)); - }); -} - -#[test] -fn cannot_reserve_staked_balance() { - // Checks that a bonded account cannot reserve balance from free balance - ExtBuilder::default().build_and_execute(|| { - // Confirm account 11 is stashed - assert_eq!(Staking::bonded(&11), Some(11)); - // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(11), 1000); - // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::eras_stakers(active_era(), 11).own, 1000); - // Confirm account 11 cannot reserve as a result - assert_noop!(Balances::reserve(&11, 1), BalancesError::::LiquidityRestrictions); - - // Give account 11 extra free balance - let _ = Balances::make_free_balance_be(&11, 10000); - // Confirm account 11 can now reserve balance - assert_ok!(Balances::reserve(&11, 1)); - }); -} - -#[test] -fn reward_destination_works() { - // Rewards go to the correct destination as determined in Payee - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Check that account 11 is a validator - assert!(Session::validators().contains(&11)); - // Check the balance of the validator account - assert_eq!(Balances::free_balance(10), 1); - // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 1000); - // Check how much is at stake - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(11, 1)]); - - mock::start_active_era(1); - mock::make_all_reward_payment(0); - - // Check that RewardDestination is Staked (default) - assert_eq!(Staking::payee(&11), RewardDestination::Staked); - // Check that reward went to the stash account of validator - assert_eq!(Balances::free_balance(11), 1000 + total_payout_0); - // Check that amount at stake increased accordingly - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: Default::default(), - claimed_rewards: bounded_vec![0], - }) - ); - - // Change RewardDestination to Stash - >::insert(&11, RewardDestination::Stash); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(11, 1)]); - - mock::start_active_era(2); - mock::make_all_reward_payment(1); - - // Check that RewardDestination is Stash - assert_eq!(Staking::payee(&11), RewardDestination::Stash); - // Check that reward went to the stash account - assert_eq!(Balances::free_balance(11), 1000 + total_payout_0 + total_payout_1); - // Check that amount at stake is NOT increased - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: Default::default(), - claimed_rewards: bounded_vec![0, 1], - }) - ); - - // Change RewardDestination to Controller - >::insert(&11, RewardDestination::Controller); - - // Check controller balance - assert_eq!(Balances::free_balance(11), 23150); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(11, 1)]); - - mock::start_active_era(3); - mock::make_all_reward_payment(2); - - // Check that RewardDestination is Controller - assert_eq!(Staking::payee(&11), RewardDestination::Controller); - // Check that reward went to the controller account - assert_eq!(Balances::free_balance(11), 23150 + total_payout_2); - // Check that amount at stake is NOT increased - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: Default::default(), - claimed_rewards: bounded_vec![0, 1, 2], - }) - ); - }); -} - -#[test] -fn validator_payment_prefs_work() { - // Test that validator preferences are correctly honored - // Note: unstake threshold is being directly tested in slashing tests. - // This test will focus on validator payment. - ExtBuilder::default().build_and_execute(|| { - let commission = Perbill::from_percent(40); - >::insert(&11, ValidatorPrefs { commission, ..Default::default() }); - - // Reward controller so staked ratio doesn't change. - >::insert(&11, RewardDestination::Controller); - >::insert(&101, RewardDestination::Controller); - - mock::start_active_era(1); - mock::make_all_reward_payment(0); - - let balance_era_1_11 = Balances::total_balance(&11); - let balance_era_1_101 = Balances::total_balance(&101); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - let exposure_1 = Staking::eras_stakers(active_era(), 11); - Pallet::::reward_by_ids(vec![(11, 1)]); - - mock::start_active_era(2); - mock::make_all_reward_payment(1); - - let taken_cut = commission * total_payout_1; - let shared_cut = total_payout_1 - taken_cut; - let reward_of_10 = shared_cut * exposure_1.own / exposure_1.total + taken_cut; - let reward_of_100 = shared_cut * exposure_1.others[0].value / exposure_1.total; - assert_eq_error_rate!(Balances::total_balance(&11), balance_era_1_11 + reward_of_10, 2); - assert_eq_error_rate!(Balances::total_balance(&101), balance_era_1_101 + reward_of_100, 2); - }); -} - -#[test] -fn bond_extra_works() { - // Tests that extra `free_balance` in the stash can be added to stake - // NOTE: this tests only verifies `StakingLedger` for correct updates - // See `bond_extra_and_withdraw_unbonded_works` for more details and updates on `Exposure`. - ExtBuilder::default().build_and_execute(|| { - // Check that account 10 is a validator - assert!(>::contains_key(11)); - // Check that account 10 is bonded to account 11 - assert_eq!(Staking::bonded(&11), Some(11)); - // Check how much is at stake - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); - - // Call the bond_extra function from controller, add only 100 - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 100)); - // There should be 100 more `total` and `active` in the ledger - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - // Call the bond_extra function with a large number, should handle it - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), Balance::max_value())); - // The full amount of the funds should now be in the total and active - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000000, - active: 1000000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - }); -} - -#[test] -fn bond_extra_and_withdraw_unbonded_works() { - // - // * Should test - // * Given an account being bonded [and chosen as a validator](not mandatory) - // * It can add extra funds to the bonded account. - // * it can unbond a portion of its funds from the stash account. - // * Once the unbonding period is done, it can actually take the funds out of the stash. - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); - - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); - - // Initial config should be correct - assert_eq!(active_era(), 0); - - // check the balance of a validator accounts. - assert_eq!(Balances::total_balance(&11), 1000000); - - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_active_era(1); - - // Initial state of 11 - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - assert_eq!( - Staking::eras_stakers(active_era(), 11), - Exposure { total: 1000, own: 1000, others: vec![] } - ); - - // deposit the extra 100 units - Staking::bond_extra(RuntimeOrigin::signed(11), 100).unwrap(); - - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - // Exposure is a snapshot! only updated after the next era update. - assert_ne!( - Staking::eras_stakers(active_era(), 11), - Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } - ); - - // trigger next era. - mock::start_active_era(2); - assert_eq!(active_era(), 2); - - // ledger should be the same. - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - // Exposure is now updated. - assert_eq!( - Staking::eras_stakers(active_era(), 11), - Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } - ); - - // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 1000).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], - }), - ); - - // Attempting to free the balances now will fail. 2 eras need to pass. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], - }), - ); - - // trigger next era. - mock::start_active_era(3); - - // nothing yet - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], - }), - ); - - // trigger next era. - mock::start_active_era(5); - - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); - // Now the value is free and the staking ledger is updated. - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 100, - active: 100, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }), - ); - }) -} - -#[test] -fn many_unbond_calls_should_work() { - ExtBuilder::default().build_and_execute(|| { - let mut current_era = 0; - // locked at era MaxUnlockingChunks - 1 until 3 - - let max_unlocking_chunks = <::MaxUnlockingChunks as Get>::get(); - - for i in 0..max_unlocking_chunks - 1 { - // There is only 1 chunk per era, so we need to be in a new era to create a chunk. - current_era = i as u32; - mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); - } - - current_era += 1; - mock::start_active_era(current_era); - - // This chunk is locked at `current_era` through `current_era + 2` (because - // `BondingDuration` == 3). - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); - assert_eq!( - Staking::ledger(&11).map(|l| l.unlocking.len()).unwrap(), - <::MaxUnlockingChunks as Get>::get() as usize - ); - - // even though the number of unlocked chunks is the same as `MaxUnlockingChunks`, - // unbonding works as expected. - for i in current_era..(current_era + max_unlocking_chunks) - 1 { - // There is only 1 chunk per era, so we need to be in a new era to create a chunk. - current_era = i as u32; - mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); - } - - // only slots within last `BondingDuration` are filled. - assert_eq!( - Staking::ledger(&11).map(|l| l.unlocking.len()).unwrap(), - <::BondingDuration>::get() as usize - ); - }) -} - -#[test] -fn auto_withdraw_may_not_unlock_all_chunks() { - ExtBuilder::default().build_and_execute(|| { - // set `MaxUnlockingChunks` to a low number to test case when the unbonding period - // is larger than the number of unlocking chunks available, which may result on a - // `Error::NoMoreChunks`, even when the auto-withdraw tries to release locked chunks. - MaxUnlockingChunks::set(1); - - let mut current_era = 0; - - // fills the chunking slots for account - mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); - - current_era += 1; - mock::start_active_era(current_era); - - // unbonding will fail because i) there are no remaining chunks and ii) no filled chunks - // can be released because current chunk hasn't stay in the queue for at least - // `BondingDuration` - assert_noop!(Staking::unbond(RuntimeOrigin::signed(11), 1), Error::::NoMoreChunks); - - // fast-forward a few eras for unbond to be successful with implicit withdraw - current_era += 10; - mock::start_active_era(current_era); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1)); - }) -} - -#[test] -fn rebond_works() { - // - // * Should test - // * Given an account being bonded [and chosen as a validator](not mandatory) - // * it can unbond a portion of its funds from the stash account. - // * it can re-bond a portion of the funds scheduled to unlock. - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); - - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); - - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_active_era(1); - - // Initial state of 11 - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - mock::start_active_era(2); - assert_eq!(active_era(), 2); - - // Try to rebond some funds. We get an error since no fund is unbonded. - assert_noop!(Staking::rebond(RuntimeOrigin::signed(11), 500), Error::::NoUnlockChunk); - - // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 900, era: 2 + 3 }], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond all the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(11), 900).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 900, era: 5 }], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond part of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: bounded_vec![UnlockChunk { value: 400, era: 5 }], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond the remainder of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - // Unbond parts of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); - Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); - Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 900, era: 5 }], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond part of the funds unbonded. - Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: bounded_vec![UnlockChunk { value: 400, era: 5 }], - claimed_rewards: bounded_vec![], - }) - ); - }) -} - -#[test] -fn rebond_is_fifo() { - // Rebond should proceed by reversing the most recent bond operations. - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); - - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); - - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_active_era(1); - - // Initial state of 10 - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - - mock::start_active_era(2); - - // Unbond some of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 400).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: bounded_vec![UnlockChunk { value: 400, era: 2 + 3 }], - claimed_rewards: bounded_vec![], - }) - ); - - mock::start_active_era(3); - - // Unbond more of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 300, - unlocking: bounded_vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 300, era: 3 + 3 }, - ], - claimed_rewards: bounded_vec![], - }) - ); - - mock::start_active_era(4); - - // Unbond yet more of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 200).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: bounded_vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 300, era: 3 + 3 }, - UnlockChunk { value: 200, era: 4 + 3 }, - ], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond half of the unbonding funds. - Staking::rebond(RuntimeOrigin::signed(11), 400).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 500, - unlocking: bounded_vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 100, era: 3 + 3 }, - ], - claimed_rewards: bounded_vec![], - }) - ); - }) -} - -#[test] -fn rebond_emits_right_value_in_event() { - // When a user calls rebond with more than can be rebonded, things succeed, - // and the rebond event emits the actual value rebonded. - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller)); - - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); - - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_active_era(1); - - // Unbond almost all of the funds in stash. - Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }], - claimed_rewards: bounded_vec![], - }) - ); - - // Re-bond less than the total - Staking::rebond(RuntimeOrigin::signed(11), 100).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 200, - unlocking: bounded_vec![UnlockChunk { value: 800, era: 1 + 3 }], - claimed_rewards: bounded_vec![], - }) - ); - // Event emitted should be correct - assert_eq!(*staking_events().last().unwrap(), Event::Bonded { stash: 11, amount: 100 }); - - // Re-bond way more than available - Staking::rebond(RuntimeOrigin::signed(11), 100_000).unwrap(); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - // Event emitted should be correct, only 800 - assert_eq!(*staking_events().last().unwrap(), Event::Bonded { stash: 11, amount: 800 }); - }); -} - -#[test] -fn reward_to_stake_works() { - ExtBuilder::default() - .nominate(false) - .set_status(31, StakerStatus::Idle) - .set_status(41, StakerStatus::Idle) - .set_stake(21, 2000) - .build_and_execute(|| { - assert_eq!(Staking::validator_count(), 2); - // Confirm account 10 and 20 are validators - assert!(>::contains_key(&11) && >::contains_key(&21)); - - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 2000); - - // Give the man some money. - let _ = Balances::make_free_balance_be(&10, 1000); - let _ = Balances::make_free_balance_be(&20, 1000); - - // Bypass logic and change current exposure - ErasStakers::::insert(0, 21, Exposure { total: 69, own: 69, others: vec![] }); - >::insert( - &20, - StakingLedger { - stash: 21, - total: 69, - active: 69, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }, - ); - - // Compute total payout now for whole duration as other parameter won't change - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - Pallet::::reward_by_ids(vec![(11, 1)]); - Pallet::::reward_by_ids(vec![(21, 1)]); - - // New era --> rewards are paid --> stakes are changed - mock::start_active_era(1); - mock::make_all_reward_payment(0); - - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 2000); - - let _11_balance = Balances::free_balance(&11); - let _21_balance = Balances::free_balance(&21); - - assert_eq!(_11_balance, 1000 + total_payout_0 / 2); - assert_eq!(_21_balance, 2000 + total_payout_0 / 2); - - // Trigger another new era as the info are frozen before the era start. - mock::start_active_era(2); - - // -- new infos - assert_eq!(Staking::eras_stakers(active_era(), 11).total, _11_balance); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, _21_balance); - }); -} - -#[test] -fn reap_stash_works() { - ExtBuilder::default() - .existential_deposit(10) - .balance_factor(10) - .build_and_execute(|| { - // given - assert_eq!(Balances::free_balance(11), 10 * 1000); - assert_eq!(Staking::bonded(&11), Some(11)); - - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // stash is not reapable - assert_noop!( - Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0), - Error::::FundedTarget - ); - - // no easy way to cause an account to go below ED, we tweak their staking ledger - // instead. - Ledger::::insert( - 11, - StakingLedger { - stash: 11, - total: 5, - active: 5, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }, - ); - - // reap-able - assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0)); - - // then - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - }); -} - -#[test] -fn switching_roles() { - // Test that it should be possible to switch between roles (nominator, validator, idle) with - // minimal overhead. - ExtBuilder::default().nominate(false).build_and_execute(|| { - // Reset reward destination - for i in &[11, 21] { - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(*i), - RewardDestination::Controller - )); - } - - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // put some money in account that we'll use. - for i in 1..7 { - let _ = Balances::deposit_creating(&i, 5000); - } - - // add 2 nominators - assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 2000, RewardDestination::Controller)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 5])); - - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Controller)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21, 1])); - - // add a new validator candidate - assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 1000, RewardDestination::Controller)); - assert_ok!(Staking::validate(RuntimeOrigin::signed(5), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(5), - SessionKeys { other: 6.into() }, - vec![] - )); - - mock::start_active_era(1); - - // with current nominators 11 and 5 have the most stake - assert_eq_uvec!(validator_controllers(), vec![5, 11]); - - // 2 decides to be a validator. Consequences: - assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(1), - SessionKeys { other: 2.into() }, - vec![] - )); - // new stakes: - // 11: 1000 self vote - // 21: 1000 self vote + 250 vote - // 5 : 1000 self vote - // 1 : 2000 self vote + 250 vote. - // Winners: 21 and 1 - - mock::start_active_era(2); - - assert_eq_uvec!(validator_controllers(), vec![1, 21]); - }); -} - -#[test] -fn wrong_vote_is_moot() { - ExtBuilder::default() - .add_staker( - 61, - 61, - 500, - StakerStatus::Nominator(vec![ - 11, 21, // good votes - 1, 2, 15, 1000, 25, // crap votes. No effect. - ]), - ) - .build_and_execute(|| { - // the genesis validators already reflect the above vote, nonetheless start a new era. - mock::start_active_era(1); - - // new validators - assert_eq_uvec!(validator_controllers(), vec![21, 11]); - - // our new voter is taken into account - assert!(Staking::eras_stakers(active_era(), 11).others.iter().any(|i| i.who == 61)); - assert!(Staking::eras_stakers(active_era(), 21).others.iter().any(|i| i.who == 61)); - }); -} - -#[test] -fn bond_with_no_staked_value() { - // Behavior when someone bonds with no staked value. - // Particularly when they votes and the candidate is elected. - ExtBuilder::default() - .validator_count(3) - .existential_deposit(5) - .balance_factor(5) - .nominate(false) - .minimum_validator_count(1) - .build_and_execute(|| { - // Can't bond with 1 - assert_noop!( - Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Controller), - Error::::InsufficientBond, - ); - // bonded with absolute minimum value possible. - assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Controller)); - assert_eq!(Balances::locks(&1)[0].amount, 5); - - // unbonding even 1 will cause all to be unbonded. - assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1)); - assert_eq!( - Staking::ledger(1), - Some(StakingLedger { - stash: 1, - active: 0, - total: 5, - unlocking: bounded_vec![UnlockChunk { value: 5, era: 3 }], - claimed_rewards: bounded_vec![], - }) - ); - - mock::start_active_era(1); - mock::start_active_era(2); - - // not yet removed. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); - assert!(Staking::ledger(1).is_some()); - assert_eq!(Balances::locks(&1)[0].amount, 5); - - mock::start_active_era(3); - - // poof. Account 1 is removed from the staking system. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); - assert!(Staking::ledger(1).is_none()); - assert_eq!(Balances::locks(&1).len(), 0); - }); -} - -#[test] -fn bond_with_little_staked_value_bounded() { - ExtBuilder::default() - .validator_count(3) - .nominate(false) - .minimum_validator_count(1) - .build_and_execute(|| { - // setup - assert_ok!(Staking::chill(RuntimeOrigin::signed(31))); - assert_ok!(Staking::set_payee( - RuntimeOrigin::signed(11), - RewardDestination::Controller - )); - let init_balance_1 = Balances::free_balance(&1); - let init_balance_11 = Balances::free_balance(&11); - - // Stingy validator. - assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Controller)); - assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(1), - SessionKeys { other: 1.into() }, - vec![] - )); - - // 1 era worth of reward. BUT, we set the timestamp after on_initialize, so outdated by - // one block. - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - - reward_all_elected(); - mock::start_active_era(1); - mock::make_all_reward_payment(0); - - // 2 is elected. - assert_eq_uvec!(validator_controllers(), vec![21, 11, 1]); - assert_eq!(Staking::eras_stakers(active_era(), 2).total, 0); - - // Old ones are rewarded. - assert_eq_error_rate!( - Balances::free_balance(11), - init_balance_11 + total_payout_0 / 3, - 1 - ); - // no rewards paid to 2. This was initial election. - assert_eq!(Balances::free_balance(1), init_balance_1); - - // reward era 2 - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - reward_all_elected(); - mock::start_active_era(2); - mock::make_all_reward_payment(1); - - assert_eq_uvec!(validator_controllers(), vec![21, 11, 1]); - assert_eq!(Staking::eras_stakers(active_era(), 2).total, 0); - - // 2 is now rewarded. - assert_eq_error_rate!( - Balances::free_balance(1), - init_balance_1 + total_payout_1 / 3, - 1 - ); - assert_eq_error_rate!( - Balances::free_balance(&11), - init_balance_11 + total_payout_0 / 3 + total_payout_1 / 3, - 2, - ); - }); -} - -#[test] -fn bond_with_duplicate_vote_should_be_ignored_by_election_provider() { - ExtBuilder::default() - .validator_count(2) - .nominate(false) - .minimum_validator_count(1) - .set_stake(31, 1000) - .build_and_execute(|| { - // ensure all have equal stake. - assert_eq!( - >::iter() - .map(|(v, _)| (v, Staking::ledger(v).unwrap().total)) - .collect::>(), - vec![(31, 1000), (21, 1000), (11, 1000)], - ); - // no nominators shall exist. - assert!(>::iter().map(|(n, _)| n).collect::>().is_empty()); - - // give the man some money. - let initial_balance = 1000; - for i in [1, 2, 3, 4].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); - } - - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 11, 11, 21, 31])); - - assert_ok!(Staking::bond( - RuntimeOrigin::signed(3), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21, 31])); - - // winners should be 21 and 31. Otherwise this election is taking duplicates into - // account. - let supports = ::ElectionProvider::elect().unwrap(); - assert_eq!( - supports, - vec![ - (21, Support { total: 1800, voters: vec![(21, 1000), (1, 400), (3, 400)] }), - (31, Support { total: 2200, voters: vec![(31, 1000), (1, 600), (3, 600)] }) - ], - ); - }); -} - -#[test] -fn bond_with_duplicate_vote_should_be_ignored_by_election_provider_elected() { - // same as above but ensures that even when the dupe is being elected, everything is sane. - ExtBuilder::default() - .validator_count(2) - .nominate(false) - .set_stake(31, 1000) - .minimum_validator_count(1) - .build_and_execute(|| { - // ensure all have equal stake. - assert_eq!( - >::iter() - .map(|(v, _)| (v, Staking::ledger(v).unwrap().total)) - .collect::>(), - vec![(31, 1000), (21, 1000), (11, 1000)], - ); - - // no nominators shall exist. - assert!(>::iter().collect::>().is_empty()); - - // give the man some money. - let initial_balance = 1000; - for i in [1, 2, 3, 4].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); - } - - assert_ok!(Staking::bond( - RuntimeOrigin::signed(1), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 11, 11, 21])); - - assert_ok!(Staking::bond( - RuntimeOrigin::signed(3), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21])); - - // winners should be 21 and 11. - let supports = ::ElectionProvider::elect().unwrap(); - assert_eq!( - supports, - vec![ - (11, Support { total: 1500, voters: vec![(11, 1000), (1, 500)] }), - (21, Support { total: 2500, voters: vec![(21, 1000), (1, 500), (3, 1000)] }) - ], - ); - }); -} - -#[test] -fn new_era_elects_correct_number_of_validators() { - ExtBuilder::default().nominate(true).validator_count(1).build_and_execute(|| { - assert_eq!(Staking::validator_count(), 1); - assert_eq!(validator_controllers().len(), 1); - - Session::on_initialize(System::block_number()); - - assert_eq!(validator_controllers().len(), 1); - }) -} - -#[test] -fn phragmen_should_not_overflow() { - ExtBuilder::default().nominate(false).build_and_execute(|| { - // This is the maximum value that we can have as the outcome of CurrencyToVote. - type Votes = u64; - - let _ = Staking::chill(RuntimeOrigin::signed(10)); - let _ = Staking::chill(RuntimeOrigin::signed(20)); - - bond_validator(3, Votes::max_value() as Balance); - bond_validator(5, Votes::max_value() as Balance); - - bond_nominator(7, Votes::max_value() as Balance, vec![3, 5]); - bond_nominator(9, Votes::max_value() as Balance, vec![3, 5]); - - mock::start_active_era(1); - - assert_eq_uvec!(validator_controllers(), vec![3, 5]); - - // We can safely convert back to values within [u64, u128]. - assert!(Staking::eras_stakers(active_era(), 3).total > Votes::max_value() as Balance); - assert!(Staking::eras_stakers(active_era(), 5).total > Votes::max_value() as Balance); - }) -} - -#[test] -fn reward_validator_slashing_validator_does_not_overflow() { - ExtBuilder::default().build_and_execute(|| { - let stake = u64::MAX as Balance * 2; - let reward_slash = u64::MAX as Balance * 2; - - // Assert multiplication overflows in balance arithmetic. - assert!(stake.checked_mul(reward_slash).is_none()); - - // Set staker - let _ = Balances::make_free_balance_be(&11, stake); - - let exposure = Exposure:: { total: stake, own: stake, others: vec![] }; - let reward = EraRewardPoints:: { - total: 1, - individual: vec![(11, 1)].into_iter().collect(), - }; - - // Check reward - ErasRewardPoints::::insert(0, reward); - ErasStakers::::insert(0, 11, &exposure); - ErasStakersClipped::::insert(0, 11, exposure); - ErasValidatorReward::::insert(0, stake); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 0)); - assert_eq!(Balances::total_balance(&11), stake * 2); - - // Set staker - let _ = Balances::make_free_balance_be(&11, stake); - let _ = Balances::make_free_balance_be(&2, stake); - - // only slashes out of bonded stake are applied. without this line, it is 0. - Staking::bond(RuntimeOrigin::signed(2), stake - 1, RewardDestination::default()).unwrap(); - // Override exposure of 11 - ErasStakers::::insert( - 0, - 11, - Exposure { - total: stake, - own: 1, - others: vec![IndividualExposure { who: 2, value: stake - 1 }], - }, - ); - - // Check slashing - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(100)], - ); - - assert_eq!(Balances::total_balance(&11), stake - 1); - assert_eq!(Balances::total_balance(&2), 1); - }) -} - -#[test] -fn reward_from_authorship_event_handler_works() { - ExtBuilder::default().build_and_execute(|| { - use pallet_authorship::EventHandler; - - assert_eq!(>::author(), Some(11)); - - Pallet::::note_author(11); - Pallet::::note_author(11); - - // Not mandatory but must be coherent with rewards - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - // 21 is rewarded as an uncle producer - // 11 is rewarded as a block producer and uncle referencer and uncle producer - assert_eq!( - ErasRewardPoints::::get(active_era()), - EraRewardPoints { individual: vec![(11, 20 * 2)].into_iter().collect(), total: 40 }, - ); - }) -} - -#[test] -fn add_reward_points_fns_works() { - ExtBuilder::default().build_and_execute(|| { - // Not mandatory but must be coherent with rewards - assert_eq_uvec!(Session::validators(), vec![21, 11]); - - Pallet::::reward_by_ids(vec![(21, 1), (11, 1), (11, 1)]); - - Pallet::::reward_by_ids(vec![(21, 1), (11, 1), (11, 1)]); - - assert_eq!( - ErasRewardPoints::::get(active_era()), - EraRewardPoints { individual: vec![(11, 4), (21, 2)].into_iter().collect(), total: 6 }, - ); - }) -} - -#[test] -fn unbonded_balance_is_not_slashable() { - ExtBuilder::default().build_and_execute(|| { - // total amount staked is slashable. - assert_eq!(Staking::slashable_balance_of(&11), 1000); - - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 800)); - - // only the active portion. - assert_eq!(Staking::slashable_balance_of(&11), 200); - }) -} - -#[test] -fn era_is_always_same_length() { - // This ensures that the sessions is always of the same length if there is no forcing no - // session changes. - ExtBuilder::default().build_and_execute(|| { - let session_per_era = >::get(); - - mock::start_active_era(1); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session_per_era); - - mock::start_active_era(2); - assert_eq!( - Staking::eras_start_session_index(current_era()).unwrap(), - session_per_era * 2u32 - ); - - let session = Session::current_index(); - Staking::set_force_era(Forcing::ForceNew); - advance_session(); - advance_session(); - assert_eq!(current_era(), 3); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session + 2); - - mock::start_active_era(4); - assert_eq!( - Staking::eras_start_session_index(current_era()).unwrap(), - session + 2u32 + session_per_era - ); - }); -} - -#[test] -fn offence_forces_new_era() { - ExtBuilder::default().build_and_execute(|| { - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(5)], - ); - - assert_eq!(Staking::force_era(), Forcing::ForceNew); - }); -} - -#[test] -fn offence_ensures_new_era_without_clobbering() { - ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::force_new_era_always(RuntimeOrigin::root())); - assert_eq!(Staking::force_era(), Forcing::ForceAlways); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(5)], - ); - - assert_eq!(Staking::force_era(), Forcing::ForceAlways); - }); -} - -#[test] -fn offence_deselects_validator_even_when_slash_is_zero() { - ExtBuilder::default().build_and_execute(|| { - assert!(Session::validators().contains(&11)); - assert!(>::contains_key(11)); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(0)], - ); - - assert_eq!(Staking::force_era(), Forcing::ForceNew); - assert!(!>::contains_key(11)); - - mock::start_active_era(1); - - assert!(!Session::validators().contains(&11)); - assert!(!>::contains_key(11)); - }); -} - -#[test] -fn slashing_performed_according_exposure() { - // This test checks that slashing is performed according the exposure (or more precisely, - // historical exposure), not the current balance. - ExtBuilder::default().build_and_execute(|| { - assert_eq!(Staking::eras_stakers(active_era(), 11).own, 1000); - - // Handle an offence with a historical exposure. - on_offence_now( - &[OffenceDetails { - offender: (11, Exposure { total: 500, own: 500, others: vec![] }), - reporters: vec![], - }], - &[Perbill::from_percent(50)], - ); - - // The stash account should be slashed for 250 (50% of 500). - assert_eq!(Balances::free_balance(11), 1000 - 250); - }); -} - -#[test] -fn slash_in_old_span_does_not_deselect() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - - assert!(>::contains_key(11)); - assert!(Session::validators().contains(&11)); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(0)], - ); - - assert_eq!(Staking::force_era(), Forcing::ForceNew); - assert!(!>::contains_key(11)); - - mock::start_active_era(2); - - Staking::validate(RuntimeOrigin::signed(11), Default::default()).unwrap(); - assert_eq!(Staking::force_era(), Forcing::NotForcing); - assert!(>::contains_key(11)); - assert!(!Session::validators().contains(&11)); - - mock::start_active_era(3); - - // this staker is in a new slashing span now, having re-registered after - // their prior slash. - - on_offence_in_era( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(0)], - 1, - DisableStrategy::WhenSlashed, - ); - - // the validator doesn't get chilled again - assert!(Validators::::iter().any(|(stash, _)| stash == 11)); - - // but we are still forcing a new era - assert_eq!(Staking::force_era(), Forcing::ForceNew); - - on_offence_in_era( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - // NOTE: A 100% slash here would clean up the account, causing de-registration. - &[Perbill::from_percent(95)], - 1, - DisableStrategy::WhenSlashed, - ); - - // the validator doesn't get chilled again - assert!(Validators::::iter().any(|(stash, _)| stash == 11)); - - // but it's disabled - assert!(is_disabled(11)); - // and we are still forcing a new era - assert_eq!(Staking::force_era(), Forcing::ForceNew); - }); -} - -#[test] -fn reporters_receive_their_slice() { - // This test verifies that the reporters of the offence receive their slice from the slashed - // amount. - ExtBuilder::default().build_and_execute(|| { - // The reporters' reward is calculated from the total exposure. - let initial_balance = 1125; - - assert_eq!(Staking::eras_stakers(active_era(), 11).total, initial_balance); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![1, 2], - }], - &[Perbill::from_percent(50)], - ); - - // F1 * (reward_proportion * slash - 0) - // 50% * (10% * initial_balance / 2) - let reward = (initial_balance / 20) / 2; - let reward_each = reward / 2; // split into two pieces. - assert_eq!(Balances::free_balance(1), 10 + reward_each); - assert_eq!(Balances::free_balance(2), 20 + reward_each); - }); -} - -#[test] -fn subsequent_reports_in_same_span_pay_out_less() { - // This test verifies that the reporters of the offence receive their slice from the slashed - // amount, but less and less if they submit multiple reports in one span. - ExtBuilder::default().build_and_execute(|| { - // The reporters' reward is calculated from the total exposure. - let initial_balance = 1125; - - assert_eq!(Staking::eras_stakers(active_era(), 11).total, initial_balance); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![1], - }], - &[Perbill::from_percent(20)], - ); - - // F1 * (reward_proportion * slash - 0) - // 50% * (10% * initial_balance * 20%) - let reward = (initial_balance / 5) / 20; - assert_eq!(Balances::free_balance(1), 10 + reward); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![1], - }], - &[Perbill::from_percent(50)], - ); - - let prior_payout = reward; - - // F1 * (reward_proportion * slash - prior_payout) - // 50% * (10% * (initial_balance / 2) - prior_payout) - let reward = ((initial_balance / 20) - prior_payout) / 2; - assert_eq!(Balances::free_balance(1), 10 + prior_payout + reward); - }); -} - -#[test] -fn invulnerables_are_not_slashed() { - // For invulnerable validators no slashing is performed. - ExtBuilder::default().invulnerables(vec![11]).build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(21), 2000); - - let exposure = Staking::eras_stakers(active_era(), 21); - let initial_balance = Staking::slashable_balance_of(&21); - - let nominator_balances: Vec<_> = - exposure.others.iter().map(|o| Balances::free_balance(&o.who)).collect(); - - on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }, - OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), - reporters: vec![], - }, - ], - &[Perbill::from_percent(50), Perbill::from_percent(20)], - ); - - // The validator 11 hasn't been slashed, but 21 has been. - assert_eq!(Balances::free_balance(11), 1000); - // 2000 - (0.2 * initial_balance) - assert_eq!(Balances::free_balance(21), 2000 - (2 * initial_balance / 10)); - - // ensure that nominators were slashed as well. - for (initial_balance, other) in nominator_balances.into_iter().zip(exposure.others) { - assert_eq!( - Balances::free_balance(&other.who), - initial_balance - (2 * other.value / 10), - ); - } - }); -} - -#[test] -fn dont_slash_if_fraction_is_zero() { - // Don't slash if the fraction is zero. - ExtBuilder::default().build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(0)], - ); - - // The validator hasn't been slashed. The new era is not forced. - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Staking::force_era(), Forcing::ForceNew); - }); -} - -#[test] -fn only_slash_for_max_in_era() { - // multiple slashes within one era are only applied if it is more than any previous slash in the - // same era. - ExtBuilder::default().build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(50)], - ); - - // The validator has been slashed and has been force-chilled. - assert_eq!(Balances::free_balance(11), 500); - assert_eq!(Staking::force_era(), Forcing::ForceNew); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(25)], - ); - - // The validator has not been slashed additionally. - assert_eq!(Balances::free_balance(11), 500); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(60)], - ); - - // The validator got slashed 10% more. - assert_eq!(Balances::free_balance(11), 400); - }) -} - -#[test] -fn garbage_collection_after_slashing() { - // ensures that `SlashingSpans` and `SpanSlash` of an account is removed after reaping. - ExtBuilder::default() - .existential_deposit(2) - .balance_factor(2) - .build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 2000); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - assert_eq!(Balances::free_balance(11), 2000 - 200); - assert!(SlashingSpans::::get(&11).is_some()); - assert_eq!(SpanSlash::::get(&(11, 0)).amount(), &200); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(100)], - ); - - // validator and nominator slash in era are garbage-collected by era change, - // so we don't test those here. - - assert_eq!(Balances::free_balance(11), 2); - assert_eq!(Balances::total_balance(&11), 2); - - let slashing_spans = SlashingSpans::::get(&11).unwrap(); - assert_eq!(slashing_spans.iter().count(), 2); - - // reap_stash respects num_slashing_spans so that weight is accurate - assert_noop!( - Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0), - Error::::IncorrectSlashingSpans - ); - assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 2)); - - assert!(SlashingSpans::::get(&11).is_none()); - assert_eq!(SpanSlash::::get(&(11, 0)).amount(), &0); - }) -} - -#[test] -fn garbage_collection_on_window_pruning() { - // ensures that `ValidatorSlashInEra` and `NominatorSlashInEra` are cleared after - // `BondingDuration`. - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - let now = active_era(); - - let exposure = Staking::eras_stakers(now, 11); - assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - - on_offence_now( - &[OffenceDetails { offender: (11, Staking::eras_stakers(now, 11)), reporters: vec![] }], - &[Perbill::from_percent(10)], - ); - - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); - - assert!(ValidatorSlashInEra::::get(&now, &11).is_some()); - assert!(NominatorSlashInEra::::get(&now, &101).is_some()); - - // + 1 because we have to exit the bonding window. - for era in (0..(BondingDuration::get() + 1)).map(|offset| offset + now + 1) { - assert!(ValidatorSlashInEra::::get(&now, &11).is_some()); - assert!(NominatorSlashInEra::::get(&now, &101).is_some()); - - mock::start_active_era(era); - } - - assert!(ValidatorSlashInEra::::get(&now, &11).is_none()); - assert!(NominatorSlashInEra::::get(&now, &101).is_none()); - }) -} - -#[test] -fn slashing_nominators_by_span_max() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - mock::start_active_era(2); - mock::start_active_era(3); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(21), 2000); - assert_eq!(Balances::free_balance(101), 2000); - assert_eq!(Staking::slashable_balance_of(&21), 1000); - - let exposure_11 = Staking::eras_stakers(active_era(), 11); - let exposure_21 = Staking::eras_stakers(active_era(), 21); - let nominated_value_11 = exposure_11.others.iter().find(|o| o.who == 101).unwrap().value; - let nominated_value_21 = exposure_21.others.iter().find(|o| o.who == 101).unwrap().value; - - on_offence_in_era( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - 2, - DisableStrategy::WhenSlashed, - ); - - assert_eq!(Balances::free_balance(11), 900); - - let slash_1_amount = Perbill::from_percent(10) * nominated_value_11; - assert_eq!(Balances::free_balance(101), 2000 - slash_1_amount); - - let expected_spans = vec![ - slashing::SlashingSpan { index: 1, start: 4, length: None }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, - ]; - - let get_span = |account| SlashingSpans::::get(&account).unwrap(); - - assert_eq!(get_span(11).iter().collect::>(), expected_spans); - - assert_eq!(get_span(101).iter().collect::>(), expected_spans); - - // second slash: higher era, higher value, same span. - on_offence_in_era( - &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), - reporters: vec![], - }], - &[Perbill::from_percent(30)], - 3, - DisableStrategy::WhenSlashed, - ); - - // 11 was not further slashed, but 21 and 101 were. - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(21), 1700); - - let slash_2_amount = Perbill::from_percent(30) * nominated_value_21; - assert!(slash_2_amount > slash_1_amount); - - // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); - - // third slash: in same era and on same validator as first, higher - // in-era value, but lower slash value than slash 2. - on_offence_in_era( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(20)], - 2, - DisableStrategy::WhenSlashed, - ); - - // 11 was further slashed, but 21 and 101 were not. - assert_eq!(Balances::free_balance(11), 800); - assert_eq!(Balances::free_balance(21), 1700); - - let slash_3_amount = Perbill::from_percent(20) * nominated_value_21; - assert!(slash_3_amount < slash_2_amount); - assert!(slash_3_amount > slash_1_amount); - - // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); - }); -} - -#[test] -fn slashes_are_summed_across_spans() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - mock::start_active_era(2); - mock::start_active_era(3); - - assert_eq!(Balances::free_balance(21), 2000); - assert_eq!(Staking::slashable_balance_of(&21), 1000); - - let get_span = |account| SlashingSpans::::get(&account).unwrap(); - - on_offence_now( - &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - let expected_spans = vec![ - slashing::SlashingSpan { index: 1, start: 4, length: None }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, - ]; - - assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(21), 1900); - - // 21 has been force-chilled. re-signal intent to validate. - Staking::validate(RuntimeOrigin::signed(21), Default::default()).unwrap(); - - mock::start_active_era(4); - - assert_eq!(Staking::slashable_balance_of(&21), 900); - - on_offence_now( - &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - let expected_spans = vec![ - slashing::SlashingSpan { index: 2, start: 5, length: None }, - slashing::SlashingSpan { index: 1, start: 4, length: Some(1) }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, - ]; - - assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(21), 1810); - }); -} - -#[test] -fn deferred_slashes_are_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - - let exposure = Staking::eras_stakers(active_era(), 11); - assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - - System::reset_events(); - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - // nominations are not removed regardless of the deferring. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - mock::start_active_era(2); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - mock::start_active_era(3); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - // at the start of era 4, slashes from era 1 are processed, - // after being deferred for at least 2 full eras. - mock::start_active_era(4); - - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); - - assert!(matches!( - staking_events_since_last_call().as_slice(), - &[ - Event::Chilled { stash: 11 }, - Event::ForceEra { mode: Forcing::ForceNew }, - Event::SlashReported { validator: 11, slash_era: 1, .. }, - Event::StakersElected, - Event::ForceEra { mode: Forcing::NotForcing }, - .., - Event::Slashed { staker: 11, amount: 100 }, - Event::Slashed { staker: 101, amount: 12 } - ] - )); - }) -} - -#[test] -fn retroactive_deferred_slashes_two_eras_before() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - assert_eq!(BondingDuration::get(), 3); - - mock::start_active_era(1); - let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); - - mock::start_active_era(3); - - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - System::reset_events(); - on_offence_in_era( - &[OffenceDetails { offender: (11, exposure_11_at_era1), reporters: vec![] }], - &[Perbill::from_percent(10)], - 1, // should be deferred for two full eras, and applied at the beginning of era 4. - DisableStrategy::Never, - ); - - mock::start_active_era(4); - - assert!(matches!( - staking_events_since_last_call().as_slice(), - &[ - Event::Chilled { stash: 11 }, - Event::ForceEra { mode: Forcing::ForceNew }, - Event::SlashReported { validator: 11, slash_era: 1, .. }, - .., - Event::Slashed { staker: 11, amount: 100 }, - Event::Slashed { staker: 101, amount: 12 } - ] - )); - }) -} - -#[test] -fn retroactive_deferred_slashes_one_before() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - assert_eq!(BondingDuration::get(), 3); - - mock::start_active_era(1); - let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); - - // unbond at slash era. - mock::start_active_era(2); - assert_ok!(Staking::chill(RuntimeOrigin::signed(11))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 100)); - - mock::start_active_era(3); - System::reset_events(); - on_offence_in_era( - &[OffenceDetails { offender: (11, exposure_11_at_era1), reporters: vec![] }], - &[Perbill::from_percent(10)], - 2, // should be deferred for two full eras, and applied at the beginning of era 5. - DisableStrategy::Never, - ); - - mock::start_active_era(4); - - assert_eq!(Staking::ledger(11).unwrap().total, 1000); - // slash happens after the next line. - - mock::start_active_era(5); - assert!(matches!( - staking_events_since_last_call().as_slice(), - &[ - Event::SlashReported { validator: 11, slash_era: 2, .. }, - .., - Event::Slashed { staker: 11, amount: 100 }, - Event::Slashed { staker: 101, amount: 12 } - ] - )); - - // their ledger has already been slashed. - assert_eq!(Staking::ledger(11).unwrap().total, 900); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000)); - assert_eq!(Staking::ledger(11).unwrap().total, 900); - }) -} - -#[test] -fn staker_cannot_bail_deferred_slash() { - // as long as SlashDeferDuration is less than BondingDuration, this should not be possible. - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - let exposure = Staking::eras_stakers(active_era(), 11); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - // now we chill - assert_ok!(Staking::chill(RuntimeOrigin::signed(101))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(101), 500)); - - assert_eq!(Staking::current_era().unwrap(), 1); - assert_eq!(active_era(), 1); - - assert_eq!( - Ledger::::get(101).unwrap(), - StakingLedger { - active: 0, - total: 500, - stash: 101, - claimed_rewards: bounded_vec![], - unlocking: bounded_vec![UnlockChunk { era: 4u32, value: 500 }], - } - ); - - // no slash yet. - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - // no slash yet. - mock::start_active_era(2); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - assert_eq!(Staking::current_era().unwrap(), 2); - assert_eq!(active_era(), 2); - - // no slash yet. - mock::start_active_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - assert_eq!(Staking::current_era().unwrap(), 3); - assert_eq!(active_era(), 3); - - // and cannot yet unbond: - assert_storage_noop!(assert!( - Staking::withdraw_unbonded(RuntimeOrigin::signed(101), 0).is_ok() - )); - assert_eq!( - Ledger::::get(101).unwrap().unlocking.into_inner(), - vec![UnlockChunk { era: 4u32, value: 500 as Balance }], - ); - - // at the start of era 4, slashes from era 1 are processed, - // after being deferred for at least 2 full eras. - mock::start_active_era(4); - - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); - - // and the leftover of the funds can now be unbonded. - }) -} - -#[test] -fn remove_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - - let exposure = Staking::eras_stakers(active_era(), 11); - assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - - // deferred to start of era 4. - on_offence_now( - &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(10)], - ); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - mock::start_active_era(2); - - // reported later, but deferred to start of era 4 as well. - System::reset_events(); - on_offence_in_era( - &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(15)], - 1, - DisableStrategy::WhenSlashed, - ); - - // fails if empty - assert_noop!( - Staking::cancel_deferred_slash(RuntimeOrigin::root(), 1, vec![]), - Error::::EmptyTargets - ); - - // cancel one of them. - assert_ok!(Staking::cancel_deferred_slash(RuntimeOrigin::root(), 4, vec![0])); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - mock::start_active_era(3); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - // at the start of era 4, slashes from era 1 are processed, - // after being deferred for at least 2 full eras. - mock::start_active_era(4); - - // the first slash for 10% was cancelled, but the 15% one not. - assert!(matches!( - staking_events_since_last_call().as_slice(), - &[ - Event::SlashReported { validator: 11, slash_era: 1, .. }, - .., - Event::Slashed { staker: 11, amount: 50 }, - Event::Slashed { staker: 101, amount: 7 } - ] - )); - - let slash_10 = Perbill::from_percent(10); - let slash_15 = Perbill::from_percent(15); - let initial_slash = slash_10 * nominated_value; - - let total_slash = slash_15 * nominated_value; - let actual_slash = total_slash - initial_slash; - - // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(11), 950); - assert_eq!(Balances::free_balance(101), 2000 - actual_slash); - }) -} - -#[test] -fn remove_multi_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - - let exposure = Staking::eras_stakers(active_era(), 11); - assert_eq!(Balances::free_balance(101), 2000); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(10)], - ); - - on_offence_now( - &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - on_offence_now( - &[OffenceDetails { offender: (42, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - on_offence_now( - &[OffenceDetails { offender: (69, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - assert_eq!(UnappliedSlashes::::get(&4).len(), 5); - - // fails if list is not sorted - assert_noop!( - Staking::cancel_deferred_slash(RuntimeOrigin::root(), 1, vec![2, 0, 4]), - Error::::NotSortedAndUnique - ); - // fails if list is not unique - assert_noop!( - Staking::cancel_deferred_slash(RuntimeOrigin::root(), 1, vec![0, 2, 2]), - Error::::NotSortedAndUnique - ); - // fails if bad index - assert_noop!( - Staking::cancel_deferred_slash(RuntimeOrigin::root(), 1, vec![1, 2, 3, 4, 5]), - Error::::InvalidSlashIndex - ); - - assert_ok!(Staking::cancel_deferred_slash(RuntimeOrigin::root(), 4, vec![0, 2, 4])); - - let slashes = UnappliedSlashes::::get(&4); - assert_eq!(slashes.len(), 2); - assert_eq!(slashes[0].validator, 21); - assert_eq!(slashes[1].validator, 42); - }) -} - -#[test] -fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_validator() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - // pre-slash balance - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - // 100 has approval for 11 as of now - assert!(Staking::nominators(101).unwrap().targets.contains(&11)); - - // 11 and 21 both have the support of 100 - let exposure_11 = Staking::eras_stakers(active_era(), &11); - let exposure_21 = Staking::eras_stakers(active_era(), &21); - - assert_eq!(exposure_11.total, 1000 + 125); - assert_eq!(exposure_21.total, 1000 + 375); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::from_percent(10)], - ); - - assert_eq!( - staking_events_since_last_call(), - vec![ - Event::StakersElected, - Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, - Event::Chilled { stash: 11 }, - Event::ForceEra { mode: Forcing::ForceNew }, - Event::SlashReported { - validator: 11, - fraction: Perbill::from_percent(10), - slash_era: 1 - }, - Event::Slashed { staker: 11, amount: 100 }, - Event::Slashed { staker: 101, amount: 12 }, - ] - ); - - // post-slash balance - let nominator_slash_amount_11 = 125 / 10; - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); - - // check that validator was chilled. - assert!(Validators::::iter().all(|(stash, _)| stash != 11)); - - // actually re-bond the slashed validator - assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); - - mock::start_active_era(2); - let exposure_11 = Staking::eras_stakers(active_era(), &11); - let exposure_21 = Staking::eras_stakers(active_era(), &21); - - // 11's own expo is reduced. sum of support from 11 is less (448), which is 500 - // 900 + 146 - assert!(matches!(exposure_11, Exposure { own: 900, total: 1046, .. })); - // 1000 + 342 - assert!(matches!(exposure_21, Exposure { own: 1000, total: 1342, .. })); - assert_eq!(500 - 146 - 342, nominator_slash_amount_11); - }); -} - -#[test] -fn non_slashable_offence_doesnt_disable_validator() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11); - let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21); - - // offence with no slash associated - on_offence_now( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::zero()], - ); - - // it does NOT affect the nominator. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - // offence that slashes 25% of the bond - on_offence_now( - &[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - // it DOES NOT affect the nominator. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - assert_eq!( - staking_events_since_last_call(), - vec![ - Event::StakersElected, - Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, - Event::Chilled { stash: 11 }, - Event::ForceEra { mode: Forcing::ForceNew }, - Event::SlashReported { - validator: 11, - fraction: Perbill::from_percent(0), - slash_era: 1 - }, - Event::Chilled { stash: 21 }, - Event::SlashReported { - validator: 21, - fraction: Perbill::from_percent(25), - slash_era: 1 - }, - Event::Slashed { staker: 21, amount: 250 }, - Event::Slashed { staker: 101, amount: 94 } - ] - ); - - // the offence for validator 10 wasn't slashable so it wasn't disabled - assert!(!is_disabled(11)); - // whereas validator 20 gets disabled - assert!(is_disabled(21)); - }); -} - -#[test] -fn slashing_independent_of_disabling_validator() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11); - let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21); - - let now = Staking::active_era().unwrap().index; - - // offence with no slash associated, BUT disabling - on_offence_in_era( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::zero()], - now, - DisableStrategy::Always, - ); - - // nomination remains untouched. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - // offence that slashes 25% of the bond, BUT not disabling - on_offence_in_era( - &[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - now, - DisableStrategy::Never, - ); - - // nomination remains untouched. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - assert_eq!( - staking_events_since_last_call(), - vec![ - Event::StakersElected, - Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, - Event::Chilled { stash: 11 }, - Event::ForceEra { mode: Forcing::ForceNew }, - Event::SlashReported { - validator: 11, - fraction: Perbill::from_percent(0), - slash_era: 1 - }, - Event::Chilled { stash: 21 }, - Event::SlashReported { - validator: 21, - fraction: Perbill::from_percent(25), - slash_era: 1 - }, - Event::Slashed { staker: 21, amount: 250 }, - Event::Slashed { staker: 101, amount: 94 } - ] - ); - - // the offence for validator 10 was explicitly disabled - assert!(is_disabled(11)); - // whereas validator 21 is explicitly not disabled - assert!(!is_disabled(21)); - }); -} - -#[test] -fn offence_threshold_triggers_new_era() { - ExtBuilder::default() - .validator_count(4) - .set_status(41, StakerStatus::Validator) - .build_and_execute(|| { - mock::start_active_era(1); - assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 41]); - - assert_eq!( - ::OffendingValidatorsThreshold::get(), - Perbill::from_percent(75), - ); - - // we have 4 validators and an offending validator threshold of 75%, - // once the third validator commits an offence a new era should be forced - - let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11); - let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21); - let exposure_31 = Staking::eras_stakers(Staking::active_era().unwrap().index, &31); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::zero()], - ); - - assert_eq!(ForceEra::::get(), Forcing::NotForcing); - - on_offence_now( - &[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }], - &[Perbill::zero()], - ); - - assert_eq!(ForceEra::::get(), Forcing::NotForcing); - - on_offence_now( - &[OffenceDetails { offender: (31, exposure_31.clone()), reporters: vec![] }], - &[Perbill::zero()], - ); - - assert_eq!(ForceEra::::get(), Forcing::ForceNew); - }); -} - -#[test] -fn disabled_validators_are_kept_disabled_for_whole_era() { - ExtBuilder::default() - .validator_count(4) - .set_status(41, StakerStatus::Validator) - .build_and_execute(|| { - mock::start_active_era(1); - assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 41]); - assert_eq!(::SessionsPerEra::get(), 3); - - let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11); - let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::zero()], - ); - - on_offence_now( - &[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - // nominations are not updated. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - // validator 11 should not be disabled since the offence wasn't slashable - assert!(!is_disabled(11)); - // validator 21 gets disabled since it got slashed - assert!(is_disabled(21)); - - advance_session(); - - // disabled validators should carry-on through all sessions in the era - assert!(!is_disabled(11)); - assert!(is_disabled(21)); - - // validator 11 should now get disabled - on_offence_now( - &[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }], - &[Perbill::from_percent(25)], - ); - - // nominations are not updated. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - - advance_session(); - - // and both are disabled in the last session of the era - assert!(is_disabled(11)); - assert!(is_disabled(21)); - - mock::start_active_era(2); - - // when a new era starts disabled validators get cleared - assert!(!is_disabled(11)); - assert!(!is_disabled(21)); - }); -} - -#[test] -fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { - // should check that: - // * rewards get paid until history_depth for both validators and nominators - // * an invalid era to claim doesn't update last_reward - // * double claim of one era fails - ExtBuilder::default().nominate(true).build_and_execute(|| { - // Consumed weight for all payout_stakers dispatches that fail - let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - - let init_balance_11 = Balances::total_balance(&11); - let init_balance_101 = Balances::total_balance(&101); - - let part_for_11 = Perbill::from_rational::(1000, 1125); - let part_for_101 = Perbill::from_rational::(125, 1125); - - // Check state - Payee::::insert(11, RewardDestination::Controller); - Payee::::insert(101, RewardDestination::Controller); - - Pallet::::reward_by_ids(vec![(11, 1)]); - // Compute total payout now for whole duration as other parameter won't change - let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - - mock::start_active_era(1); - - Pallet::::reward_by_ids(vec![(11, 1)]); - // Change total issuance in order to modify total payout - let _ = Balances::deposit_creating(&999, 1_000_000_000); - // Compute total payout now for whole duration as other parameter won't change - let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - assert!(total_payout_1 != total_payout_0); - - mock::start_active_era(2); - - Pallet::::reward_by_ids(vec![(11, 1)]); - // Change total issuance in order to modify total payout - let _ = Balances::deposit_creating(&999, 1_000_000_000); - // Compute total payout now for whole duration as other parameter won't change - let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); - assert!(total_payout_2 != total_payout_0); - assert!(total_payout_2 != total_payout_1); - - mock::start_active_era(HistoryDepth::get() + 1); - - let active_era = active_era(); - - // This is the latest planned era in staking, not the active era - let current_era = Staking::current_era().unwrap(); - - // Last kept is 1: - assert!(current_era - HistoryDepth::get() == 1); - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 0), - // Fail: Era out of history - Error::::InvalidEraToReward.with_weight(err_weight) - ); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2)); - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2), - // Fail: Double claim - Error::::AlreadyClaimed.with_weight(err_weight) - ); - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, active_era), - // Fail: Era not finished yet - Error::::InvalidEraToReward.with_weight(err_weight) - ); - - // Era 0 can't be rewarded anymore and current era can't be rewarded yet - // only era 1 and 2 can be rewarded. - - assert_eq!( - Balances::total_balance(&11), - init_balance_11 + part_for_11 * (total_payout_1 + total_payout_2), - ); - assert_eq!( - Balances::total_balance(&101), - init_balance_101 + part_for_101 * (total_payout_1 + total_payout_2), - ); - }); -} - -#[test] -fn zero_slash_keeps_nominators() { - ExtBuilder::default().build_and_execute(|| { - mock::start_active_era(1); - - assert_eq!(Balances::free_balance(11), 1000); - - let exposure = Staking::eras_stakers(active_era(), 11); - assert_eq!(Balances::free_balance(101), 2000); - - on_offence_now( - &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], - &[Perbill::from_percent(0)], - ); - - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - // 11 is still removed.. - assert!(Validators::::iter().all(|(stash, _)| stash != 11)); - // but their nominations are kept. - assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - }); -} - -#[test] -fn six_session_delay() { - ExtBuilder::default().initialize_first_session(false).build_and_execute(|| { - use pallet_session::SessionManager; - - let val_set = Session::validators(); - let init_session = Session::current_index(); - let init_active_era = active_era(); - - // pallet-session is delaying session by one, thus the next session to plan is +2. - assert_eq!(>::new_session(init_session + 2), None); - assert_eq!( - >::new_session(init_session + 3), - Some(val_set.clone()) - ); - assert_eq!(>::new_session(init_session + 4), None); - assert_eq!(>::new_session(init_session + 5), None); - assert_eq!( - >::new_session(init_session + 6), - Some(val_set.clone()) - ); - - >::end_session(init_session); - >::start_session(init_session + 1); - assert_eq!(active_era(), init_active_era); - - >::end_session(init_session + 1); - >::start_session(init_session + 2); - assert_eq!(active_era(), init_active_era); - - // Reward current era - Staking::reward_by_ids(vec![(11, 1)]); - - // New active era is triggered here. - >::end_session(init_session + 2); - >::start_session(init_session + 3); - assert_eq!(active_era(), init_active_era + 1); - - >::end_session(init_session + 3); - >::start_session(init_session + 4); - assert_eq!(active_era(), init_active_era + 1); - - >::end_session(init_session + 4); - >::start_session(init_session + 5); - assert_eq!(active_era(), init_active_era + 1); - - // Reward current era - Staking::reward_by_ids(vec![(21, 2)]); - - // New active era is triggered here. - >::end_session(init_session + 5); - >::start_session(init_session + 6); - assert_eq!(active_era(), init_active_era + 2); - - // That reward are correct - assert_eq!(Staking::eras_reward_points(init_active_era).total, 1); - assert_eq!(Staking::eras_reward_points(init_active_era + 1).total, 2); - }); -} - -#[test] -fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward() { - ExtBuilder::default().build_and_execute(|| { - for i in 0..=<::MaxNominatorRewardedPerValidator as Get<_>>::get() { - let stash = 10_000 + i as AccountId; - let balance = 10_000 + i as Balance; - Balances::make_free_balance_be(&stash, balance); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(stash), - balance, - RewardDestination::Stash - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(stash), vec![11])); - } - mock::start_active_era(1); - - Pallet::::reward_by_ids(vec![(11, 1)]); - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - - mock::start_active_era(2); - mock::make_all_reward_payment(1); - - // Assert only nominators from 1 to Max are rewarded - for i in 0..=<::MaxNominatorRewardedPerValidator as Get<_>>::get() { - let stash = 10_000 + i as AccountId; - let balance = 10_000 + i as Balance; - if stash == 10_000 { - assert!(Balances::free_balance(&stash) == balance); - } else { - assert!(Balances::free_balance(&stash) > balance); - } - } - }); -} - -#[test] -fn test_payout_stakers() { - // Test that payout_stakers work in general, including that only the top - // `T::MaxNominatorRewardedPerValidator` nominators are rewarded. - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - let balance = 1000; - // Track the exposure of the validator and all nominators. - let mut total_exposure = balance; - // Track the exposure of the validator and the nominators that will get paid out. - let mut payout_exposure = balance; - // Create a validator: - bond_validator(11, balance); // Default(64) - assert_eq!(Validators::::count(), 1); - - // Create nominators, targeting stash of validators - for i in 0..100 { - let bond_amount = balance + i as Balance; - bond_nominator(1000 + i, bond_amount, vec![11]); - total_exposure += bond_amount; - if i >= 36 { - payout_exposure += bond_amount; - }; - } - let payout_exposure_part = Perbill::from_rational(payout_exposure, total_exposure); - - mock::start_active_era(1); - Staking::reward_by_ids(vec![(11, 1)]); - - // compute and ensure the reward amount is greater than zero. - let payout = current_total_payout_for_duration(reward_time_per_era()); - let actual_paid_out = payout_exposure_part * payout; - - mock::start_active_era(2); - - let pre_payout_total_issuance = Balances::total_issuance(); - RewardOnUnbalanceWasCalled::set(false); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); - assert_eq_error_rate!( - Balances::total_issuance(), - pre_payout_total_issuance + actual_paid_out, - 1 - ); - assert!(RewardOnUnbalanceWasCalled::get()); - - // Top 64 nominators of validator 11 automatically paid out, including the validator - // Validator payout goes to controller. - assert!(Balances::free_balance(&11) > balance); - for i in 36..100 { - assert!(Balances::free_balance(&(1000 + i)) > balance + i as Balance); - } - // The bottom 36 do not - for i in 0..36 { - assert_eq!(Balances::free_balance(&(1000 + i)), balance + i as Balance); - } - - // We track rewards in `claimed_rewards` vec - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![1] - }) - ); - - for i in 3..16 { - Staking::reward_by_ids(vec![(11, 1)]); - - // compute and ensure the reward amount is greater than zero. - let payout = current_total_payout_for_duration(reward_time_per_era()); - let actual_paid_out = payout_exposure_part * payout; - let pre_payout_total_issuance = Balances::total_issuance(); - - mock::start_active_era(i); - RewardOnUnbalanceWasCalled::set(false); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, i - 1)); - assert_eq_error_rate!( - Balances::total_issuance(), - pre_payout_total_issuance + actual_paid_out, - 1 - ); - assert!(RewardOnUnbalanceWasCalled::get()); - } - - // We track rewards in `claimed_rewards` vec - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: (1..=14).collect::>().try_into().unwrap() - }) - ); - - let last_era = 99; - let history_depth = HistoryDepth::get(); - let expected_last_reward_era = last_era - 1; - let expected_start_reward_era = last_era - history_depth; - for i in 16..=last_era { - Staking::reward_by_ids(vec![(11, 1)]); - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - mock::start_active_era(i); - } - - // We clean it up as history passes - assert_ok!(Staking::payout_stakers( - RuntimeOrigin::signed(1337), - 11, - expected_start_reward_era - )); - assert_ok!(Staking::payout_stakers( - RuntimeOrigin::signed(1337), - 11, - expected_last_reward_era - )); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![expected_start_reward_era, expected_last_reward_era] - }) - ); - - // Out of order claims works. - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 69)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 23)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 42)); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![ - expected_start_reward_era, - 23, - 42, - 69, - expected_last_reward_era - ] - }) - ); - }); -} - -#[test] -fn payout_stakers_handles_basic_errors() { - // Here we will test payouts handle all errors. - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - // Consumed weight for all payout_stakers dispatches that fail - let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - - // Same setup as the test above - let balance = 1000; - bond_validator(11, balance); // Default(64) - - // Create nominators, targeting stash - for i in 0..100 { - bond_nominator(1000 + i, balance + i as Balance, vec![11]); - } - - mock::start_active_era(1); - Staking::reward_by_ids(vec![(11, 1)]); - - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - - mock::start_active_era(2); - - // Wrong Era, too big - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2), - Error::::InvalidEraToReward.with_weight(err_weight) - ); - // Wrong Staker - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 10, 1), - Error::::NotStash.with_weight(err_weight) - ); - - let last_era = 99; - for i in 3..=last_era { - Staking::reward_by_ids(vec![(11, 1)]); - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - mock::start_active_era(i); - } - - let history_depth = HistoryDepth::get(); - let expected_last_reward_era = last_era - 1; - let expected_start_reward_era = last_era - history_depth; - - // We are at era last_era=99. Given history_depth=80, we should be able - // to payout era starting from expected_start_reward_era=19 through - // expected_last_reward_era=98 (80 total eras), but not 18 or 99. - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_start_reward_era - 1), - Error::::InvalidEraToReward.with_weight(err_weight) - ); - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_last_reward_era + 1), - Error::::InvalidEraToReward.with_weight(err_weight) - ); - assert_ok!(Staking::payout_stakers( - RuntimeOrigin::signed(1337), - 11, - expected_start_reward_era - )); - assert_ok!(Staking::payout_stakers( - RuntimeOrigin::signed(1337), - 11, - expected_last_reward_era - )); - - // Can't claim again - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_start_reward_era), - Error::::AlreadyClaimed.with_weight(err_weight) - ); - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_last_reward_era), - Error::::AlreadyClaimed.with_weight(err_weight) - ); - }); -} - -#[test] -fn payout_stakers_handles_weight_refund() { - // Note: this test relies on the assumption that `payout_stakers_alive_staked` is solely used by - // `payout_stakers` to calculate the weight of each payout op. - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - let max_nom_rewarded = - <::MaxNominatorRewardedPerValidator as Get<_>>::get(); - // Make sure the configured value is meaningful for our use. - assert!(max_nom_rewarded >= 4); - let half_max_nom_rewarded = max_nom_rewarded / 2; - // Sanity check our max and half max nominator quantities. - assert!(half_max_nom_rewarded > 0); - assert!(max_nom_rewarded > half_max_nom_rewarded); - - let max_nom_rewarded_weight = - ::WeightInfo::payout_stakers_alive_staked(max_nom_rewarded); - let half_max_nom_rewarded_weight = - ::WeightInfo::payout_stakers_alive_staked(half_max_nom_rewarded); - let zero_nom_payouts_weight = ::WeightInfo::payout_stakers_alive_staked(0); - assert!(zero_nom_payouts_weight.any_gt(Weight::zero())); - assert!(half_max_nom_rewarded_weight.any_gt(zero_nom_payouts_weight)); - assert!(max_nom_rewarded_weight.any_gt(half_max_nom_rewarded_weight)); - - let balance = 1000; - bond_validator(11, balance); - - // Era 1 - start_active_era(1); - - // Reward just the validator. - Staking::reward_by_ids(vec![(11, 1)]); - - // Add some `half_max_nom_rewarded` nominators who will start backing the validator in the - // next era. - for i in 0..half_max_nom_rewarded { - bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]); - } - - // Era 2 - start_active_era(2); - - // Collect payouts when there are no nominators - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 1 }); - let info = call.get_dispatch_info(); - let result = call.dispatch(RuntimeOrigin::signed(20)); - assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight); - - // The validator is not rewarded in this era; so there will be zero payouts to claim for - // this era. - - // Era 3 - start_active_era(3); - - // Collect payouts for an era where the validator did not receive any points. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 2 }); - let info = call.get_dispatch_info(); - let result = call.dispatch(RuntimeOrigin::signed(20)); - assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight); - - // Reward the validator and its nominators. - Staking::reward_by_ids(vec![(11, 1)]); - - // Era 4 - start_active_era(4); - - // Collect payouts when the validator has `half_max_nom_rewarded` nominators. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 3 }); - let info = call.get_dispatch_info(); - let result = call.dispatch(RuntimeOrigin::signed(20)); - assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), half_max_nom_rewarded_weight); - - // Add enough nominators so that we are at the limit. They will be active nominators - // in the next era. - for i in half_max_nom_rewarded..max_nom_rewarded { - bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]); - } - - // Era 5 - start_active_era(5); - // We now have `max_nom_rewarded` nominators actively nominating our validator. - - // Reward the validator so we can collect for everyone in the next era. - Staking::reward_by_ids(vec![(11, 1)]); - - // Era 6 - start_active_era(6); - - // Collect payouts when the validator had `half_max_nom_rewarded` nominators. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 }); - let info = call.get_dispatch_info(); - let result = call.dispatch(RuntimeOrigin::signed(20)); - assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight); - - // Try and collect payouts for an era that has already been collected. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 }); - let info = call.get_dispatch_info(); - let result = call.dispatch(RuntimeOrigin::signed(20)); - assert!(result.is_err()); - // When there is an error the consumed weight == weight when there are 0 nominator payouts. - assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight); - }); -} - -#[test] -fn bond_during_era_correctly_populates_claimed_rewards() { - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - // Era = None - bond_validator(9, 1000); - assert_eq!( - Staking::ledger(&9), - Some(StakingLedger { - stash: 9, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - }) - ); - mock::start_active_era(5); - bond_validator(11, 1000); - assert_eq!( - Staking::ledger(&11), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: (0..5).collect::>().try_into().unwrap(), - }) - ); - - // make sure only era upto history depth is stored - let current_era = 99; - let last_reward_era = 99 - HistoryDepth::get(); - mock::start_active_era(current_era); - bond_validator(13, 1000); - assert_eq!( - Staking::ledger(&13), - Some(StakingLedger { - stash: 13, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: (last_reward_era..current_era) - .collect::>() - .try_into() - .unwrap(), - }) - ); - }); -} - -#[test] -fn offences_weight_calculated_correctly() { - ExtBuilder::default().nominate(true).build_and_execute(|| { - // On offence with zero offenders: 4 Reads, 1 Write - let zero_offence_weight = - ::DbWeight::get().reads_writes(4, 1); - assert_eq!( - Staking::on_offence(&[], &[Perbill::from_percent(50)], 0, DisableStrategy::WhenSlashed), - zero_offence_weight - ); - - // On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes - let n_offence_unapplied_weight = ::DbWeight::get() - .reads_writes(4, 1) + - ::DbWeight::get().reads_writes(4, 5); - - let offenders: Vec< - OffenceDetails< - ::AccountId, - pallet_session::historical::IdentificationTuple, - >, - > = (1..10) - .map(|i| OffenceDetails { - offender: (i, Staking::eras_stakers(active_era(), i)), - reporters: vec![], - }) - .collect(); - assert_eq!( - Staking::on_offence( - &offenders, - &[Perbill::from_percent(50)], - 0, - DisableStrategy::WhenSlashed - ), - n_offence_unapplied_weight - ); - - // On Offence with one offenders, Applied - let one_offender = [OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![1], - }]; - - let n = 1; // Number of offenders - let rw = 3 + 3 * n; // rw reads and writes - let one_offence_unapplied_weight = - ::DbWeight::get().reads_writes(4, 1) - + - ::DbWeight::get().reads_writes(rw, rw) - // One `slash_cost` - + ::DbWeight::get().reads_writes(6, 5) - // `slash_cost` * nominators (1) - + ::DbWeight::get().reads_writes(6, 5) - // `reward_cost` * reporters (1) - + ::DbWeight::get().reads_writes(2, 2) - ; - - assert_eq!( - Staking::on_offence( - &one_offender, - &[Perbill::from_percent(50)], - 0, - DisableStrategy::WhenSlashed{} - ), - one_offence_unapplied_weight - ); - }); -} - -#[test] -fn payout_creates_controller() { - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - let balance = 1000; - // Create a validator: - bond_validator(11, balance); - - // create a stash/controller pair and nominate - let (stash, controller) = testing_utils::create_unique_stash_controller::( - 0, - 100, - RewardDestination::Controller, - false, - ) - .unwrap(); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11])); - - // kill controller - assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(controller), stash, 100)); - assert_eq!(Balances::free_balance(controller), 0); - - mock::start_active_era(1); - Staking::reward_by_ids(vec![(11, 1)]); - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - mock::start_active_era(2); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(controller), 11, 1)); - - // Controller is created - assert!(Balances::free_balance(controller) > 0); - }) -} - -#[test] -fn payout_to_any_account_works() { - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - let balance = 1000; - // Create a validator: - bond_validator(11, balance); // Default(64) - - // Create a stash/controller pair - bond_nominator(1234, 100, vec![11]); - - // Update payout location - assert_ok!(Staking::set_payee(RuntimeOrigin::signed(1234), RewardDestination::Account(42))); - - // Reward Destination account doesn't exist - assert_eq!(Balances::free_balance(42), 0); - - mock::start_active_era(1); - Staking::reward_by_ids(vec![(11, 1)]); - // compute and ensure the reward amount is greater than zero. - let _ = current_total_payout_for_duration(reward_time_per_era()); - mock::start_active_era(2); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); - - // Payment is successful - assert!(Balances::free_balance(42) > 0); - }) -} - -#[test] -fn session_buffering_with_offset() { - // similar to live-chains, have some offset for the first session - ExtBuilder::default() - .offset(2) - .period(5) - .session_per_era(5) - .build_and_execute(|| { - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 0); - - start_session(1); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 1); - assert_eq!(System::block_number(), 2); - - start_session(2); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 2); - assert_eq!(System::block_number(), 7); - - start_session(3); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 3); - assert_eq!(System::block_number(), 12); - - // active era is lagging behind by one session, because of how session module works. - start_session(4); - assert_eq!(current_era(), 1); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 4); - assert_eq!(System::block_number(), 17); - - start_session(5); - assert_eq!(current_era(), 1); - assert_eq!(active_era(), 1); - assert_eq!(Session::current_index(), 5); - assert_eq!(System::block_number(), 22); - - // go all the way to active 2. - start_active_era(2); - assert_eq!(current_era(), 2); - assert_eq!(active_era(), 2); - assert_eq!(Session::current_index(), 10); - }); -} - -#[test] -fn session_buffering_no_offset() { - // no offset, first session starts immediately - ExtBuilder::default() - .offset(0) - .period(5) - .session_per_era(5) - .build_and_execute(|| { - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 0); - - start_session(1); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 1); - assert_eq!(System::block_number(), 5); - - start_session(2); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 2); - assert_eq!(System::block_number(), 10); - - start_session(3); - assert_eq!(current_era(), 0); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 3); - assert_eq!(System::block_number(), 15); - - // active era is lagging behind by one session, because of how session module works. - start_session(4); - assert_eq!(current_era(), 1); - assert_eq!(active_era(), 0); - assert_eq!(Session::current_index(), 4); - assert_eq!(System::block_number(), 20); - - start_session(5); - assert_eq!(current_era(), 1); - assert_eq!(active_era(), 1); - assert_eq!(Session::current_index(), 5); - assert_eq!(System::block_number(), 25); - - // go all the way to active 2. - start_active_era(2); - assert_eq!(current_era(), 2); - assert_eq!(active_era(), 2); - assert_eq!(Session::current_index(), 10); - }); -} - -#[test] -fn cannot_rebond_to_lower_than_ed() { - ExtBuilder::default() - .existential_deposit(11) - .balance_factor(11) - .build_and_execute(|| { - // initial stuff. - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: 11 * 1000, - active: 11 * 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - } - ); - - // unbond all of it. must be chilled first. - assert_ok!(Staking::chill(RuntimeOrigin::signed(21))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000)); - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: 11 * 1000, - active: 0, - unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }], - claimed_rewards: bounded_vec![], - } - ); - - // now bond a wee bit more - assert_noop!( - Staking::rebond(RuntimeOrigin::signed(21), 5), - Error::::InsufficientBond - ); - }) -} - -#[test] -fn cannot_bond_extra_to_lower_than_ed() { - ExtBuilder::default() - .existential_deposit(11) - .balance_factor(11) - .build_and_execute(|| { - // initial stuff. - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: 11 * 1000, - active: 11 * 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - } - ); - - // unbond all of it. must be chilled first. - assert_ok!(Staking::chill(RuntimeOrigin::signed(21))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000)); - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: 11 * 1000, - active: 0, - unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }], - claimed_rewards: bounded_vec![], - } - ); - - // now bond a wee bit more - assert_noop!( - Staking::bond_extra(RuntimeOrigin::signed(21), 5), - Error::::InsufficientBond, - ); - }) -} - -#[test] -fn do_not_die_when_active_is_ed() { - let ed = 10; - ExtBuilder::default() - .existential_deposit(ed) - .balance_factor(ed) - .build_and_execute(|| { - // given - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: 1000 * ed, - active: 1000 * ed, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - } - ); - - // when unbond all of it except ed. - assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 999 * ed)); - start_active_era(3); - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(21), 100)); - - // then - assert_eq!( - Staking::ledger(&21).unwrap(), - StakingLedger { - stash: 21, - total: ed, - active: ed, - unlocking: Default::default(), - claimed_rewards: bounded_vec![], - } - ); - }) -} - -#[test] -fn on_finalize_weight_is_nonzero() { - ExtBuilder::default().build_and_execute(|| { - let on_finalize_weight = ::DbWeight::get().reads(1); - assert!(>::on_initialize(1).all_gte(on_finalize_weight)); - }) -} - -mod election_data_provider { - use frame_election_provider_support::ElectionDataProvider; - - use super::*; - - #[test] - fn targets_2sec_block() { - let mut validators = 1000; - while ::WeightInfo::get_npos_targets(validators).all_lt(Weight::from_parts( - 2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - u64::MAX, - )) { - validators += 1; - } - - println!("Can create a snapshot of {} validators in 2sec block", validators); - } - - #[test] - fn voters_2sec_block() { - // we assume a network only wants up to 1000 validators in most cases, thus having 2000 - // candidates is as high as it gets. - let validators = 2000; - let mut nominators = 1000; - - while ::WeightInfo::get_npos_voters(validators, nominators).all_lt( - Weight::from_parts( - 2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - u64::MAX, - ), - ) { - nominators += 1; - } - - println!( - "Can create a snapshot of {} nominators [{} validators, each 1 slashing] in 2sec block", - nominators, validators - ); - } - - #[test] - fn set_minimum_active_stake_is_correct() { - ExtBuilder::default() - .nominate(false) - .add_staker(61, 61, 2_000, StakerStatus::::Nominator(vec![21])) - .add_staker(71, 71, 10, StakerStatus::::Nominator(vec![21])) - .add_staker(81, 81, 50, StakerStatus::::Nominator(vec![21])) - .build_and_execute(|| { - assert_ok!(::electing_voters(None)); - assert_eq!(MinimumActiveStake::::get(), 10); - - // remove staker with lower bond by limiting the number of voters and check - // `MinimumActiveStake` again after electing voters. - assert_ok!(::electing_voters(Some(5))); - assert_eq!(MinimumActiveStake::::get(), 50); - }); - } - - #[test] - fn set_minimum_active_stake_zero_correct() { - ExtBuilder::default().has_stakers(false).build_and_execute(|| { - assert_ok!(::electing_voters(None)); - assert_eq!(MinimumActiveStake::::get(), 0); - }); - } - - #[test] - fn voters_include_self_vote() { - ExtBuilder::default().nominate(false).build_and_execute(|| { - assert!(>::iter().map(|(x, _)| x).all(|v| Staking::electing_voters( - None - ) - .unwrap() - .into_iter() - .any(|(w, _, t)| { v == w && t[0] == w }))) - }) - } - - #[test] - fn respects_snapshot_len_limits() { - ExtBuilder::default() - .set_status(41, StakerStatus::Validator) - .build_and_execute(|| { - // sum of all nominators who'd be voters (1), plus the self-votes (4). - assert_eq!(::VoterList::count(), 5); - - // if limits is less.. - assert_eq!(Staking::electing_voters(Some(1)).unwrap().len(), 1); - - // if limit is equal.. - assert_eq!(Staking::electing_voters(Some(5)).unwrap().len(), 5); - - // if limit is more. - assert_eq!(Staking::electing_voters(Some(55)).unwrap().len(), 5); - - // if target limit is more.. - assert_eq!(Staking::electable_targets(Some(6)).unwrap().len(), 4); - assert_eq!(Staking::electable_targets(Some(4)).unwrap().len(), 4); - - // if target limit is less, then we return an error. - assert_eq!( - Staking::electable_targets(Some(1)).unwrap_err(), - "Target snapshot too big" - ); - }); - } - - // Tests the criteria that in `ElectionDataProvider::voters` function, we try to get at most - // `maybe_max_len` voters, and if some of them end up being skipped, we iterate at most `2 * - // maybe_max_len`. - #[test] - fn only_iterates_max_2_times_max_allowed_len() { - ExtBuilder::default() - .nominate(false) - // the best way to invalidate a bunch of nominators is to have them nominate a lot of - // ppl, but then lower the MaxNomination limit. - .add_staker( - 61, - 61, - 2_000, - StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), - ) - .add_staker( - 71, - 71, - 2_000, - StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), - ) - .add_staker( - 81, - 81, - 2_000, - StakerStatus::::Nominator(vec![21, 22, 23, 24, 25]), - ) - .build_and_execute(|| { - // all voters ordered by stake, - assert_eq!( - ::VoterList::iter().collect::>(), - vec![61, 71, 81, 11, 21, 31] - ); - - MaxNominations::set(2); - - // we want 2 voters now, and in maximum we allow 4 iterations. This is what happens: - // 61 is pruned; - // 71 is pruned; - // 81 is pruned; - // 11 is taken; - // we finish since the 2x limit is reached. - assert_eq!( - Staking::electing_voters(Some(2)) - .unwrap() - .iter() - .map(|(stash, _, _)| stash) - .copied() - .collect::>(), - vec![11], - ); - }); - } - - #[test] - fn estimate_next_election_works() { - ExtBuilder::default().session_per_era(5).period(5).build_and_execute(|| { - // first session is always length 0. - for b in 1..20 { - run_to_block(b); - assert_eq!(Staking::next_election_prediction(System::block_number()), 20); - } - - // election - run_to_block(20); - assert_eq!(Staking::next_election_prediction(System::block_number()), 45); - assert_eq!(staking_events().len(), 1); - assert_eq!(*staking_events().last().unwrap(), Event::StakersElected); - - for b in 21..45 { - run_to_block(b); - assert_eq!(Staking::next_election_prediction(System::block_number()), 45); - } - - // election - run_to_block(45); - assert_eq!(Staking::next_election_prediction(System::block_number()), 70); - assert_eq!(staking_events().len(), 3); - assert_eq!(*staking_events().last().unwrap(), Event::StakersElected); - - Staking::force_no_eras(RuntimeOrigin::root()).unwrap(); - assert_eq!(Staking::next_election_prediction(System::block_number()), u64::MAX); - - Staking::force_new_era_always(RuntimeOrigin::root()).unwrap(); - assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5); - - Staking::force_new_era(RuntimeOrigin::root()).unwrap(); - assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5); - - // Do a fail election - MinimumValidatorCount::::put(1000); - run_to_block(50); - // Election: failed, next session is a new election - assert_eq!(Staking::next_election_prediction(System::block_number()), 50 + 5); - // The new era is still forced until a new era is planned. - assert_eq!(ForceEra::::get(), Forcing::ForceNew); - - MinimumValidatorCount::::put(2); - run_to_block(55); - assert_eq!(Staking::next_election_prediction(System::block_number()), 55 + 25); - assert_eq!(staking_events().len(), 10); - assert_eq!( - *staking_events().last().unwrap(), - Event::ForceEra { mode: Forcing::NotForcing } - ); - assert_eq!( - *staking_events().get(staking_events().len() - 2).unwrap(), - Event::StakersElected - ); - // The new era has been planned, forcing is changed from `ForceNew` to `NotForcing`. - assert_eq!(ForceEra::::get(), Forcing::NotForcing); - }) - } -} - -#[test] -#[should_panic] -fn count_check_works() { - ExtBuilder::default().build_and_execute(|| { - // We should never insert into the validators or nominators map directly as this will - // not keep track of the count. This test should panic as we verify the count is accurate - // after every test using the `post_checks` in `mock`. - Validators::::insert(987654321, ValidatorPrefs::default()); - Nominators::::insert( - 987654321, - Nominations { - targets: Default::default(), - submitted_in: Default::default(), - suppressed: false, - }, - ); - }) -} - -#[test] -fn min_bond_checks_work() { - ExtBuilder::default() - .existential_deposit(100) - .balance_factor(100) - .min_nominator_bond(1_000) - .min_validator_bond(1_500) - .build_and_execute(|| { - // 500 is not enough for any role - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Controller)); - assert_noop!( - Staking::nominate(RuntimeOrigin::signed(3), vec![1]), - Error::::InsufficientBond - ); - assert_noop!( - Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()), - Error::::InsufficientBond, - ); - - // 1000 is enough for nominator - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); - assert_noop!( - Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()), - Error::::InsufficientBond, - ); - - // 1500 is enough for validator - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500)); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); - assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default())); - - // Can't unbond anything as validator - assert_noop!( - Staking::unbond(RuntimeOrigin::signed(3), 500), - Error::::InsufficientBond - ); - - // Once they are a nominator, they can unbond 500 - assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1])); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 500)); - assert_noop!( - Staking::unbond(RuntimeOrigin::signed(3), 500), - Error::::InsufficientBond - ); - - // Once they are chilled they can unbond everything - assert_ok!(Staking::chill(RuntimeOrigin::signed(3))); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 1000)); - }) -} - -#[test] -fn chill_other_works() { - ExtBuilder::default() - .existential_deposit(100) - .balance_factor(100) - .min_nominator_bond(1_000) - .min_validator_bond(1_500) - .build_and_execute(|| { - let initial_validators = Validators::::count(); - let initial_nominators = Nominators::::count(); - for i in 0..15 { - let a = 4 * i; - let b = 4 * i + 2; - let c = 4 * i + 3; - Balances::make_free_balance_be(&a, 100_000); - Balances::make_free_balance_be(&b, 100_000); - Balances::make_free_balance_be(&c, 100_000); - - // Nominator - assert_ok!(Staking::bond( - RuntimeOrigin::signed(a), - 1000, - RewardDestination::Controller - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(a), vec![1])); - - // Validator - assert_ok!(Staking::bond( - RuntimeOrigin::signed(b), - 1500, - RewardDestination::Controller - )); - assert_ok!(Staking::validate(RuntimeOrigin::signed(b), ValidatorPrefs::default())); - } - - // To chill other users, we need to: - // * Set a minimum bond amount - // * Set a limit - // * Set a threshold - // - // If any of these are missing, we do not have enough information to allow the - // `chill_other` to succeed from one user to another. - - // Can't chill these users - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 0), - Error::::CannotChillOther - ); - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 2), - Error::::CannotChillOther - ); - - // Change the minimum bond... but no limits. - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Set(1_500), - ConfigOp::Set(2_000), - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove - )); - - // Still can't chill these users - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 0), - Error::::CannotChillOther - ); - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 2), - Error::::CannotChillOther - ); - - // Add limits, but no threshold - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Set(10), - ConfigOp::Set(10), - ConfigOp::Noop, - ConfigOp::Noop - )); - - // Still can't chill these users - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 0), - Error::::CannotChillOther - ); - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 2), - Error::::CannotChillOther - ); - - // Add threshold, but no limits - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Noop, - ConfigOp::Noop - )); - - // Still can't chill these users - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 0), - Error::::CannotChillOther - ); - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 2), - Error::::CannotChillOther - ); - - // Add threshold and limits - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Set(10), - ConfigOp::Set(10), - ConfigOp::Set(Percent::from_percent(75)), - ConfigOp::Noop - )); - - // 16 people total because tests start with 2 active one - assert_eq!(Nominators::::count(), 15 + initial_nominators); - assert_eq!(Validators::::count(), 15 + initial_validators); - - // Users can now be chilled down to 7 people, so we try to remove 9 of them (starting - // with 16) - for i in 6..15 { - let b = 4 * i; - let d = 4 * i + 2; - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), b)); - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), d)); - } - - // chill a nominator. Limit is not reached, not chill-able - assert_eq!(Nominators::::count(), 7); - assert_noop!( - Staking::chill_other(RuntimeOrigin::signed(1337), 0), - Error::::CannotChillOther - ); - // chill a validator. Limit is reached, chill-able. - assert_eq!(Validators::::count(), 9); - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), 2)); - }) -} - -#[test] -fn capped_stakers_works() { - ExtBuilder::default().build_and_execute(|| { - let validator_count = Validators::::count(); - assert_eq!(validator_count, 3); - let nominator_count = Nominators::::count(); - assert_eq!(nominator_count, 1); - - // Change the maximums - let max = 10; - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Set(10), - ConfigOp::Set(10), - ConfigOp::Set(max), - ConfigOp::Set(max), - ConfigOp::Remove, - ConfigOp::Remove, - )); - - // can create `max - validator_count` validators - let mut some_existing_validator = AccountId::default(); - for i in 0..max - validator_count { - let (_, controller) = testing_utils::create_stash_controller::( - i + 10_000_000, - 100, - RewardDestination::Controller, - ) - .unwrap(); - assert_ok!(Staking::validate( - RuntimeOrigin::signed(controller), - ValidatorPrefs::default() - )); - some_existing_validator = controller; - } - - // but no more - let (_, last_validator) = testing_utils::create_stash_controller::( - 1337, - 100, - RewardDestination::Controller, - ) - .unwrap(); - - assert_noop!( - Staking::validate(RuntimeOrigin::signed(last_validator), ValidatorPrefs::default()), - Error::::TooManyValidators, - ); - - // same with nominators - let mut some_existing_nominator = AccountId::default(); - for i in 0..max - nominator_count { - let (_, controller) = testing_utils::create_stash_controller::( - i + 20_000_000, - 100, - RewardDestination::Controller, - ) - .unwrap(); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![1])); - some_existing_nominator = controller; - } - - // one more is too many. - let (_, last_nominator) = testing_utils::create_stash_controller::( - 30_000_000, - 100, - RewardDestination::Controller, - ) - .unwrap(); - assert_noop!( - Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1]), - Error::::TooManyNominators - ); - - // Re-nominate works fine - assert_ok!(Staking::nominate(RuntimeOrigin::signed(some_existing_nominator), vec![1])); - // Re-validate works fine - assert_ok!(Staking::validate( - RuntimeOrigin::signed(some_existing_validator), - ValidatorPrefs::default() - )); - - // No problem when we set to `None` again - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Noop, - ConfigOp::Noop, - )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1])); - assert_ok!(Staking::validate( - RuntimeOrigin::signed(last_validator), - ValidatorPrefs::default() - )); - }) -} - -#[test] -fn min_commission_works() { - ExtBuilder::default().build_and_execute(|| { - // account 11 controls the stash of itself. - assert_ok!(Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false } - )); - - // event emitted should be correct - assert_eq!( - *staking_events().last().unwrap(), - Event::ValidatorPrefsSet { - stash: 11, - prefs: ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false } - } - ); - - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Set(Perbill::from_percent(10)), - )); - - // can't make it less than 10 now - assert_noop!( - Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false } - ), - Error::::CommissionTooLow - ); - - // can only change to higher. - assert_ok!(Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(10), blocked: false } - )); - - assert_ok!(Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false } - )); - }) -} - -#[test] -fn change_of_max_nominations() { - use frame_election_provider_support::ElectionDataProvider; - ExtBuilder::default() - .add_staker(61, 61, 10, StakerStatus::Nominator(vec![1])) - .add_staker(71, 71, 10, StakerStatus::Nominator(vec![1, 2, 3])) - .balance_factor(10) - .build_and_execute(|| { - // pre-condition - assert_eq!(MaxNominations::get(), 16); - - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(101, 2), (71, 3), (61, 1)] - ); - // 3 validators and 3 nominators - assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); - - // abrupt change from 16 to 4, everyone should be fine. - MaxNominations::set(4); - - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(101, 2), (71, 3), (61, 1)] - ); - assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); - - // abrupt change from 4 to 3, everyone should be fine. - MaxNominations::set(3); - - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(101, 2), (71, 3), (61, 1)] - ); - assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 3); - - // abrupt change from 3 to 2, this should cause some nominators to be non-decodable, and - // thus non-existent unless if they update. - MaxNominations::set(2); - - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(101, 2), (61, 1)] - ); - // 70 is still in storage.. - assert!(Nominators::::contains_key(71)); - // but its value cannot be decoded and default is returned. - assert!(Nominators::::get(71).is_none()); - - assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 2); - assert!(Nominators::::contains_key(101)); - - // abrupt change from 2 to 1, this should cause some nominators to be non-decodable, and - // thus non-existent unless if they update. - MaxNominations::set(1); - - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(61, 1)] - ); - assert!(Nominators::::contains_key(71)); - assert!(Nominators::::contains_key(61)); - assert!(Nominators::::get(71).is_none()); - assert!(Nominators::::get(61).is_some()); - assert_eq!(Staking::electing_voters(None).unwrap().len(), 3 + 1); - - // now one of them can revive themselves by re-nominating to a proper value. - assert_ok!(Staking::nominate(RuntimeOrigin::signed(71), vec![1])); - assert_eq!( - Nominators::::iter() - .map(|(k, n)| (k, n.targets.len())) - .collect::>(), - vec![(71, 1), (61, 1)] - ); - - // or they can be chilled by any account. - assert!(Nominators::::contains_key(101)); - assert!(Nominators::::get(101).is_none()); - assert_ok!(Staking::chill_other(RuntimeOrigin::signed(71), 101)); - assert!(!Nominators::::contains_key(101)); - assert!(Nominators::::get(101).is_none()); - }) -} - -mod sorted_list_provider { - use frame_election_provider_support::SortedListProvider; - - use super::*; - - #[test] - fn re_nominate_does_not_change_counters_or_list() { - ExtBuilder::default().nominate(true).build_and_execute(|| { - // given - let pre_insert_voter_count = - (Nominators::::count() + Validators::::count()) as u32; - assert_eq!(::VoterList::count(), pre_insert_voter_count); - - assert_eq!( - ::VoterList::iter().collect::>(), - vec![11, 21, 31, 101] - ); - - // when account 101 renominates - assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![41])); - - // then counts don't change - assert_eq!(::VoterList::count(), pre_insert_voter_count); - // and the list is the same - assert_eq!( - ::VoterList::iter().collect::>(), - vec![11, 21, 31, 101] - ); - }); - } - - #[test] - fn re_validate_does_not_change_counters_or_list() { - ExtBuilder::default().nominate(false).build_and_execute(|| { - // given - let pre_insert_voter_count = - (Nominators::::count() + Validators::::count()) as u32; - assert_eq!(::VoterList::count(), pre_insert_voter_count); - - assert_eq!(::VoterList::iter().collect::>(), vec![11, 21, 31]); - - // when account 11 re-validates - assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default())); - - // then counts don't change - assert_eq!(::VoterList::count(), pre_insert_voter_count); - // and the list is the same - assert_eq!(::VoterList::iter().collect::>(), vec![11, 21, 31]); - }); - } -} - -#[test] -fn force_apply_min_commission_works() { - let prefs = |c| ValidatorPrefs { commission: Perbill::from_percent(c), blocked: false }; - let validators = || Validators::::iter().collect::>(); - ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::validate(RuntimeOrigin::signed(31), prefs(10))); - assert_ok!(Staking::validate(RuntimeOrigin::signed(21), prefs(5))); - - // Given - assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]); - MinCommission::::set(Perbill::from_percent(5)); - - // When applying to a commission greater than min - assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 31)); - // Then the commission is not changed - assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]); - - // When applying to a commission that is equal to min - assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 21)); - // Then the commission is not changed - assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]); - - // When applying to a commission that is less than the min - assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 11)); - // Then the commission is bumped to the min - assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(5))]); - - // When applying commission to a validator that doesn't exist then storage is not altered - assert_noop!( - Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 420), - Error::::NotStash - ); - }); -} - -#[test] -fn proportional_slash_stop_slashing_if_remaining_zero() { - let c = |era, value| UnlockChunk:: { era, value }; - // Given - let mut ledger = StakingLedger:: { - stash: 123, - total: 40, - active: 20, - // we have some chunks, but they are not affected. - unlocking: bounded_vec![c(1, 10), c(2, 10)], - claimed_rewards: bounded_vec![], - }; - - assert_eq!(BondingDuration::get(), 3); - - // should not slash more than the amount requested, by accidentally slashing the first chunk. - assert_eq!(ledger.slash(18, 1, 0), 18); -} - -#[test] -fn proportional_ledger_slash_works() { - let c = |era, value| UnlockChunk:: { era, value }; - // Given - let mut ledger = StakingLedger:: { - stash: 123, - total: 10, - active: 10, - unlocking: bounded_vec![], - claimed_rewards: bounded_vec![], - }; - assert_eq!(BondingDuration::get(), 3); - - // When we slash a ledger with no unlocking chunks - assert_eq!(ledger.slash(5, 1, 0), 5); - // Then - assert_eq!(ledger.total, 5); - assert_eq!(ledger.active, 5); - assert_eq!(LedgerSlashPerEra::get().0, 5); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // When we slash a ledger with no unlocking chunks and the slash amount is greater then the - // total - assert_eq!(ledger.slash(11, 1, 0), 5); - // Then - assert_eq!(ledger.total, 0); - assert_eq!(ledger.active, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // Given - ledger.unlocking = bounded_vec![c(4, 10), c(5, 10)]; - ledger.total = 2 * 10; - ledger.active = 0; - // When all the chunks overlap with the slash eras - assert_eq!(ledger.slash(20, 0, 0), 20); - // Then - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.total = 4 * 100; - ledger.active = 0; - // When the first 2 chunks don't overlap with the affected range of unlock eras. - assert_eq!(ledger.slash(140, 0, 3), 140); - // Then - assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]); - assert_eq!(ledger.total, 4 * 100 - 140); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.total = 4 * 100; - ledger.active = 0; - // When the first 2 chunks don't overlap with the affected range of unlock eras. - assert_eq!(ledger.slash(15, 0, 3), 15); - // Then - assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 100 - 8), c(7, 100 - 7)]); - assert_eq!(ledger.total, 4 * 100 - 15); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 92), (7, 93)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - // 900 - ledger.total = 40 + 10 + 100 + 250 + 500; - // When we have a partial slash that touches all chunks - assert_eq!(ledger.slash(900 / 2, 0, 0), 450); - // Then - assert_eq!(ledger.active, 500 / 2); - assert_eq!(ledger.unlocking, vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)]); - assert_eq!(ledger.total, 900 / 2); - assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)]) - ); - - // slash 1/4th with not chunk. - ledger.unlocking = bounded_vec![]; - ledger.active = 500; - ledger.total = 500; - // When we have a partial slash that touches all chunks - assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4); - // Then - assert_eq!(ledger.active, 3 * 500 / 4); - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, ledger.active); - assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // Given we have the same as above, - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - ledger.total = 40 + 10 + 100 + 250 + 500; // 900 - assert_eq!(ledger.total, 900); - // When we have a higher min balance - assert_eq!( - ledger.slash( - 900 / 2, - 25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it to - * get swept */ - 0 - ), - 450 - ); - assert_eq!(ledger.active, 500 / 2); - // the last chunk was not slashed 50% like all the rest, because some other earlier chunks got - // dusted. - assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 150)]); - assert_eq!(ledger.total, 900 / 2); - assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 150)]) - ); - - // Given - // slash order --------------------NA--------2----------0----------1---- - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - ledger.total = 40 + 10 + 100 + 250 + 500; // 900 - assert_eq!( - ledger.slash( - 500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2 - 0, - 3 /* slash era 6 first, so the affected parts are era 6, era 7 and - * ledge.active. This will cause the affected to go to zero, and then we will - * start slashing older chunks */ - ), - 500 + 250 + 10 + 100 / 2 - ); - // Then - assert_eq!(ledger.active, 0); - assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]); - assert_eq!(ledger.total, 90); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)])); - - // Given - // iteration order------------------NA---------2----------0----------1---- - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.active = 100; - ledger.total = 5 * 100; - // When - assert_eq!( - ledger.slash( - 351, // active + era 6 + era 7 + era 5 / 2 + 1 - 50, // min balance - everything slashed below 50 will get dusted - 3 /* slash era 3+3 first, so the affected parts are era 6, era 7 and - * ledge.active. This will cause the affected to go to zero, and then we will - * start slashing older chunks */ - ), - 400 - ); - // Then - assert_eq!(ledger.active, 0); - assert_eq!(ledger.unlocking, vec![c(4, 100)]); - assert_eq!(ledger.total, 100); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)])); - - // Tests for saturating arithmetic - - // Given - let slash = u64::MAX as Balance * 2; - // The value of the other parts of ledger that will get slashed - let value = slash - (10 * 4); - - ledger.active = 10; - ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)]; - ledger.total = value + 40; - // When - let slash_amount = ledger.slash(slash, 0, 0); - assert_eq_error_rate!(slash_amount, slash, 5); - // Then - assert_eq!(ledger.active, 0); // slash of 9 - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)])); - - // Given - use sp_runtime::PerThing as _; - let slash = u64::MAX as Balance * 2; - let value = u64::MAX as Balance * 2; - let unit = 100; - // slash * value that will saturate - assert!(slash.checked_mul(value).is_none()); - // but slash * unit won't. - assert!(slash.checked_mul(unit).is_some()); - ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)]; - //--------------------------------------note value^^^ - ledger.active = unit; - ledger.total = unit * 4 + value; - // When - assert_eq!(ledger.slash(slash, 0, 0), slash); - // Then - // The amount slashed out of `unit` - let affected_balance = value + unit * 4; - let ratio = - Perquintill::from_rational_with_rounding(slash, affected_balance, Rounding::Up).unwrap(); - // `unit` after the slash is applied - let unit_slashed = { - let unit_slash = ratio.mul_ceil(unit); - unit - unit_slash - }; - let value_slashed = { - let value_slash = ratio.mul_ceil(value); - value - value_slash - }; - assert_eq!(ledger.active, unit_slashed); - assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]); - assert_eq!(ledger.total, value_slashed + 32); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)]) - ); -} - -#[test] -fn pre_bonding_era_cannot_be_claimed() { - // Verifies initial conditions of mock - ExtBuilder::default().nominate(false).build_and_execute(|| { - let history_depth = HistoryDepth::get(); - // jump to some era above history_depth - let mut current_era = history_depth + 10; - let last_reward_era = current_era - 1; - let start_reward_era = current_era - history_depth; - - // put some money in stash=3 and controller=4. - for i in 3..5 { - let _ = Balances::make_free_balance_be(&i, 2000); - } - - mock::start_active_era(current_era); - - // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); - - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&3).unwrap(), - StakingLedger { - stash: 3, - total: 1500, - active: 1500, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // start next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1)); - - // consumed weight for all payout_stakers dispatches that fail - let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - // cannot claim rewards for an era before bonding occured as it is - // already marked as claimed. - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 2), - Error::::AlreadyClaimed.with_weight(err_weight) - ); - - // decoding will fail now since Staking Ledger is in corrupt state - HistoryDepth::set(history_depth - 1); - assert_eq!(Staking::ledger(&4), None); - - // make sure stakers still cannot claim rewards that they are not meant to - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 2), - Error::::NotController - ); - - // fix the corrupted state for post conditions check - HistoryDepth::set(history_depth); - }); -} - -#[test] -fn reducing_history_depth_abrupt() { - // Verifies initial conditions of mock - ExtBuilder::default().nominate(false).build_and_execute(|| { - let original_history_depth = HistoryDepth::get(); - let mut current_era = original_history_depth + 10; - let last_reward_era = current_era - 1; - let start_reward_era = current_era - original_history_depth; - - // put some money in (stash, controller)=(3,3),(5,5). - for i in 3..7 { - let _ = Balances::make_free_balance_be(&i, 2000); - } - - // start current era - mock::start_active_era(current_era); - - // add a new candidate for being a staker. account 3 controlled by 3. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller)); - - // all previous era before the bonding action should be marked as - // claimed. - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&3).unwrap(), - StakingLedger { - stash: 3, - total: 1500, - active: 1500, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1)); - - // next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // history_depth reduced without migration - let history_depth = original_history_depth - 1; - HistoryDepth::set(history_depth); - // claiming reward does not work anymore - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(3), 3, current_era - 1), - Error::::NotController - ); - - // new stakers can still bond - assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 1200, RewardDestination::Controller)); - - // new staking ledgers created will be bounded by the current history depth - let last_reward_era = current_era - 1; - let start_reward_era = current_era - history_depth; - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&5).unwrap(), - StakingLedger { - stash: 5, - total: 1200, - active: 1200, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // fix the corrupted state for post conditions check - HistoryDepth::set(original_history_depth); - }); -} - -#[test] -fn reducing_max_unlocking_chunks_abrupt() { - // Concern is on validators only - // By Default 11, 10 are stash and ctrl and 21,20 - ExtBuilder::default().build_and_execute(|| { - // given a staker at era=10 and MaxUnlockChunks set to 2 - MaxUnlockingChunks::set(2); - start_active_era(10); - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 300, RewardDestination::Staked)); - assert!(matches!(Staking::ledger(3), Some(_))); - - // when staker unbonds - assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 20)); - - // then an unlocking chunk is added at `current_era + bonding_duration` - // => 10 + 3 = 13 - let expected_unlocking: BoundedVec, MaxUnlockingChunks> = - bounded_vec![UnlockChunk { value: 20 as Balance, era: 13 as EraIndex }]; - assert!(matches!(Staking::ledger(3), - Some(StakingLedger { - unlocking, - .. - }) if unlocking==expected_unlocking)); - - // when staker unbonds at next era - start_active_era(11); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 50)); - // then another unlock chunk is added - let expected_unlocking: BoundedVec, MaxUnlockingChunks> = - bounded_vec![UnlockChunk { value: 20, era: 13 }, UnlockChunk { value: 50, era: 14 }]; - assert!(matches!(Staking::ledger(3), - Some(StakingLedger { - unlocking, - .. - }) if unlocking==expected_unlocking)); - - // when staker unbonds further - start_active_era(12); - // then further unbonding not possible - assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::::NoMoreChunks); - - // when max unlocking chunks is reduced abruptly to a low value - MaxUnlockingChunks::set(1); - // then unbond, rebond ops are blocked with ledger in corrupt state - assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::::NotController); - assert_noop!(Staking::rebond(RuntimeOrigin::signed(3), 100), Error::::NotController); - - // reset the ledger corruption - MaxUnlockingChunks::set(2); - }) -} - -#[test] -fn cannot_set_unsupported_validator_count() { - ExtBuilder::default().build_and_execute(|| { - MaxWinners::set(50); - // set validator count works - assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 30)); - assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 50)); - // setting validator count above 100 does not work - assert_noop!( - Staking::set_validator_count(RuntimeOrigin::root(), 51), - Error::::TooManyValidators, - ); - }) -} - -#[test] -fn increase_validator_count_errors() { - ExtBuilder::default().build_and_execute(|| { - MaxWinners::set(50); - assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 40)); - - // increase works - assert_ok!(Staking::increase_validator_count(RuntimeOrigin::root(), 6)); - assert_eq!(ValidatorCount::::get(), 46); - - // errors - assert_noop!( - Staking::increase_validator_count(RuntimeOrigin::root(), 5), - Error::::TooManyValidators, - ); - }) -} - -#[test] -fn scale_validator_count_errors() { - ExtBuilder::default().build_and_execute(|| { - MaxWinners::set(50); - assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 20)); - - // scale value works - assert_ok!(Staking::scale_validator_count( - RuntimeOrigin::root(), - Percent::from_percent(200) - )); - assert_eq!(ValidatorCount::::get(), 40); - - // errors - assert_noop!( - Staking::scale_validator_count(RuntimeOrigin::root(), Percent::from_percent(126)), - Error::::TooManyValidators, - ); - }) -} - -#[test] -fn set_min_commission_works_with_admin_origin() { - ExtBuilder::default().build_and_execute(|| { - // no minimum commission set initially - assert_eq!(MinCommission::::get(), Zero::zero()); - - // root can set min commission - assert_ok!(Staking::set_min_commission(RuntimeOrigin::root(), Perbill::from_percent(10))); - - assert_eq!(MinCommission::::get(), Perbill::from_percent(10)); - - // Non privileged origin can not set min_commission - assert_noop!( - Staking::set_min_commission(RuntimeOrigin::signed(2), Perbill::from_percent(15)), - BadOrigin - ); - - // Admin Origin can set min commission - assert_ok!(Staking::set_min_commission( - RuntimeOrigin::signed(1), - Perbill::from_percent(15), - )); - - // setting commission below min_commission fails - assert_noop!( - Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(14), blocked: false } - ), - Error::::CommissionTooLow - ); - - // setting commission >= min_commission works - assert_ok!(Staking::validate( - RuntimeOrigin::signed(11), - ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false } - )); - }) -} - -mod staking_interface { - use frame_support::storage::with_storage_layer; - use sp_staking::StakingInterface; - - use super::*; - - #[test] - fn force_unstake_with_slash_works() { - ExtBuilder::default().build_and_execute(|| { - // without slash - let _ = with_storage_layer::<(), _, _>(|| { - // bond an account, can unstake - assert_eq!(Staking::bonded(&11), Some(11)); - assert_ok!(::force_unstake(11)); - Err(DispatchError::from("revert")) - }); - - // bond again and add a slash, still can unstake. - assert_eq!(Staking::bonded(&11), Some(11)); - add_slash(&11); - assert_ok!(::force_unstake(11)); - }); - } - - #[test] - fn do_withdraw_unbonded_with_wrong_slash_spans_works_as_expected() { - ExtBuilder::default().build_and_execute(|| { - on_offence_now( - &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), - reporters: vec![], - }], - &[Perbill::from_percent(100)], - ); - - assert_eq!(Staking::bonded(&11), Some(11)); - - assert_noop!( - Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0), - Error::::IncorrectSlashingSpans - ); - - let num_slashing_spans = Staking::slashing_spans(&11).map_or(0, |s| s.iter().count()); - assert_ok!(Staking::withdraw_unbonded( - RuntimeOrigin::signed(11), - num_slashing_spans as u32 - )); - }); - } - - #[test] - fn status() { - ExtBuilder::default().build_and_execute(|| { - // stash of a validator is identified as a validator - assert_eq!(Staking::status(&11).unwrap(), StakerStatus::Validator); - // .. but not the controller. - assert!(Staking::status(&10).is_err()); - - // stash of nominator is identified as a nominator - assert_eq!(Staking::status(&101).unwrap(), StakerStatus::Nominator(vec![11, 21])); - // .. but not the controller. - assert!(Staking::status(&100).is_err()); - - // stash of chilled is identified as a chilled - assert_eq!(Staking::status(&41).unwrap(), StakerStatus::Idle); - // .. but not the controller. - assert!(Staking::status(&40).is_err()); - - // random other account. - assert!(Staking::status(&42).is_err()); - }) - } -} diff --git a/pallets/staking/src/weights.rs b/pallets/staking/src/weights.rs deleted file mode 100644 index f2c65e677..000000000 --- a/pallets/staking/src/weights.rs +++ /dev/null @@ -1,1506 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for pallet_staking -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/substrate -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_staking -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./frame/staking/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -/// Weight functions needed for pallet_staking. -pub trait WeightInfo { - fn bond() -> Weight; - fn bond_extra() -> Weight; - fn unbond() -> Weight; - fn withdraw_unbonded_update(s: u32, ) -> Weight; - fn withdraw_unbonded_kill(s: u32, ) -> Weight; - fn validate() -> Weight; - fn kick(k: u32, ) -> Weight; - fn nominate(n: u32, ) -> Weight; - fn chill() -> Weight; - fn set_payee() -> Weight; - fn set_controller() -> Weight; - fn set_validator_count() -> Weight; - fn force_no_eras() -> Weight; - fn force_new_era() -> Weight; - fn force_new_era_always() -> Weight; - fn set_invulnerables(v: u32, ) -> Weight; - fn force_unstake(s: u32, ) -> Weight; - fn cancel_deferred_slash(s: u32, ) -> Weight; - fn payout_stakers_dead_controller(n: u32, ) -> Weight; - fn payout_stakers_alive_staked(n: u32, ) -> Weight; - fn rebond(l: u32, ) -> Weight; - fn reap_stash(s: u32, ) -> Weight; - fn new_era(v: u32, n: u32, ) -> Weight; - fn get_npos_voters(v: u32, n: u32, ) -> Weight; - fn get_npos_targets(v: u32, ) -> Weight; - fn set_staking_configs_all_set() -> Weight; - fn set_staking_configs_all_remove() -> Weight; - fn chill_other() -> Weight; - fn force_apply_min_commission() -> Weight; - fn set_min_commission() -> Weight; -} - -/// Weights for pallet_staking using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - fn bond() -> Weight { - // Proof Size summary in bytes: - // Measured: `1047` - // Estimated: `4764` - // Minimum execution time: 53_983_000 picoseconds. - Weight::from_parts(55_296_000, 4764) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn bond_extra() -> Weight { - // Proof Size summary in bytes: - // Measured: `2028` - // Estimated: `8877` - // Minimum execution time: 96_590_000 picoseconds. - Weight::from_parts(98_921_000, 8877) - .saturating_add(T::DbWeight::get().reads(9_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn unbond() -> Weight { - // Proof Size summary in bytes: - // Measured: `2233` - // Estimated: `8877` - // Minimum execution time: 99_901_000 picoseconds. - Weight::from_parts(102_919_000, 8877) - .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1021` - // Estimated: `4764` - // Minimum execution time: 45_230_000 picoseconds. - Weight::from_parts(47_052_829, 4764) - // Standard Error: 1_044 - .saturating_add(Weight::from_parts(43_887, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2294 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 97_534_000 picoseconds. - Weight::from_parts(104_772_163, 6248) - // Standard Error: 3_674 - .saturating_add(Weight::from_parts(1_470_124, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(13_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:1 w:0) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MinCommission (r:1 w:0) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:1) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:1 w:0) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:1 w:1) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForValidators (r:1 w:1) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn validate() -> Weight { - // Proof Size summary in bytes: - // Measured: `1414` - // Estimated: `4556` - // Minimum execution time: 57_467_000 picoseconds. - Weight::from_parts(59_437_000, 4556) - .saturating_add(T::DbWeight::get().reads(11_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:128 w:128) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// The range of component `k` is `[1, 128]`. - fn kick(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1285 + k * (569 ±0)` - // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 32_857_000 picoseconds. - Weight::from_parts(37_116_967, 4556) - // Standard Error: 9_522 - .saturating_add(Weight::from_parts(8_796_167, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:1 w:0) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:17 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// The range of component `n` is `[1, 16]`. - fn nominate(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1908 + n * (102 ±0)` - // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 69_613_000 picoseconds. - Weight::from_parts(68_079_061, 6248) - // Standard Error: 18_554 - .saturating_add(Weight::from_parts(4_012_761, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(6_u64)) - .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn chill() -> Weight { - // Proof Size summary in bytes: - // Measured: `1748` - // Estimated: `6248` - // Minimum execution time: 60_430_000 picoseconds. - Weight::from_parts(62_702_000, 6248) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - fn set_payee() -> Weight { - // Proof Size summary in bytes: - // Measured: `808` - // Estimated: `4556` - // Minimum execution time: 14_276_000 picoseconds. - Weight::from_parts(14_766_000, 4556) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2 w:2) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - fn set_controller() -> Weight { - // Proof Size summary in bytes: - // Measured: `907` - // Estimated: `8122` - // Minimum execution time: 21_710_000 picoseconds. - Weight::from_parts(22_430_000, 8122) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: Staking ValidatorCount (r:0 w:1) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn set_validator_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_970_000 picoseconds. - Weight::from_parts(3_120_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_no_eras() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_362_000 picoseconds. - Weight::from_parts(9_785_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_new_era() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_275_000 picoseconds. - Weight::from_parts(9_678_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_new_era_always() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_414_000 picoseconds. - Weight::from_parts(9_848_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking Invulnerables (r:0 w:1) - /// Proof Skipped: Staking Invulnerables (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `v` is `[0, 1000]`. - fn set_invulnerables(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 3_061_000 picoseconds. - Weight::from_parts(3_618_535, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(10_774, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, 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 Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:0 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn force_unstake(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2018 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 87_914_000 picoseconds. - Weight::from_parts(95_688_129, 6248) - // Standard Error: 5_030 - .saturating_add(Weight::from_parts(1_487_249, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().writes(12_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: Staking UnappliedSlashes (r:1 w:1) - /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) - /// The range of component `s` is `[1, 1000]`. - fn cancel_deferred_slash(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `66639` - // Estimated: `70104` - // Minimum execution time: 99_269_000 picoseconds. - Weight::from_parts(1_154_264_637, 70104) - // Standard Error: 76_592 - .saturating_add(Weight::from_parts(6_490_888, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:257 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasRewardPoints (r:1 w:0) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:1 w:0) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:257 w:0) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: System Account (r:257 w:257) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 256]`. - fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `20217 + n * (143 ±0)` - // Estimated: `19844 + n * (2603 ±1)` - // Minimum execution time: 91_767_000 picoseconds. - Weight::from_parts(146_781_264, 19844) - // Standard Error: 31_341 - .saturating_add(Weight::from_parts(30_553_008, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) - } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:257 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:257 w:257) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasRewardPoints (r:1 w:0) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:1 w:0) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:257 w:0) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: System Account (r:257 w:257) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:257 w:257) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:257 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 256]`. - fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `33190 + n * (377 ±0)` - // Estimated: `30845 + n * (3774 ±0)` - // Minimum execution time: 121_303_000 picoseconds. - Weight::from_parts(151_046_907, 30845) - // Standard Error: 41_899 - .saturating_add(Weight::from_parts(49_837_804, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) - .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// The range of component `l` is `[1, 32]`. - fn rebond(l: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2029 + l * (7 ±0)` - // Estimated: `8877` - // Minimum execution time: 90_068_000 picoseconds. - Weight::from_parts(93_137_456, 8877) - // Standard Error: 4_799 - .saturating_add(Weight::from_parts(54_421, 0).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[1, 100]`. - fn reap_stash(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2294 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 103_139_000 picoseconds. - Weight::from_parts(107_036_296, 6248) - // Standard Error: 3_935 - .saturating_add(Weight::from_parts(1_465_860, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: VoterList CounterForListNodes (r:1 w:0) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:200 w:0) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:110 w:0) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:110 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:11 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:110 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:110 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CounterForValidators (r:1 w:0) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinimumValidatorCount (r:1 w:0) - /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:1) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:0 w:10) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:0 w:10) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:0 w:10) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasTotalStake (r:0 w:1) - /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking ErasStartSessionIndex (r:0 w:1) - /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Staking MinimumActiveStake (r:0 w:1) - /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// The range of component `v` is `[1, 10]`. - /// The range of component `n` is `[0, 100]`. - fn new_era(v: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` - // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 587_156_000 picoseconds. - Weight::from_parts(590_176_000, 512390) - // Standard Error: 2_008_420 - .saturating_add(Weight::from_parts(64_526_052, 0).saturating_mul(v.into())) - // Standard Error: 200_128 - .saturating_add(Weight::from_parts(18_070_222, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(206_u64)) - .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) - } - /// Storage: VoterList CounterForListNodes (r:1 w:0) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:200 w:0) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2000 w:0) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:2000 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1000 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:2000 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2000 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinimumActiveStake (r:0 w:1) - /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// The range of component `v` is `[500, 1000]`. - /// The range of component `n` is `[500, 1000]`. - fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3217 + n * (911 ±0) + v * (395 ±0)` - // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 34_399_721_000 picoseconds. - Weight::from_parts(34_605_803_000, 512390) - // Standard Error: 380_106 - .saturating_add(Weight::from_parts(5_426_220, 0).saturating_mul(v.into())) - // Standard Error: 380_106 - .saturating_add(Weight::from_parts(3_318_197, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(201_u64)) - .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) - } - /// Storage: Staking CounterForValidators (r:1 w:0) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1001 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// The range of component `v` is `[500, 1000]`. - fn get_npos_targets(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `983 + v * (50 ±0)` - // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_392_849_000 picoseconds. - Weight::from_parts(64_373_879, 3510) - // Standard Error: 8_995 - .saturating_add(Weight::from_parts(4_721_536, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:0 w:1) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:0 w:1) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:0 w:1) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:0 w:1) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:0 w:1) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - fn set_staking_configs_all_set() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 7_529_000 picoseconds. - Weight::from_parts(7_970_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:0 w:1) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:0 w:1) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:0 w:1) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:0 w:1) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:0 w:1) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - fn set_staking_configs_all_remove() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 7_011_000 picoseconds. - Weight::from_parts(7_317_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:1 w:0) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:1 w:0) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn chill_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `1871` - // Estimated: `6248` - // Minimum execution time: 75_982_000 picoseconds. - Weight::from_parts(77_412_000, 6248) - .saturating_add(T::DbWeight::get().reads(11_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } - /// Storage: Staking MinCommission (r:1 w:0) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:1) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - fn force_apply_min_commission() -> Weight { - // Proof Size summary in bytes: - // Measured: `694` - // Estimated: `3510` - // Minimum execution time: 13_923_000 picoseconds. - Weight::from_parts(14_356_000, 3510) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn set_min_commission() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 3_415_000 picoseconds. - Weight::from_parts(3_679_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - fn bond() -> Weight { - // Proof Size summary in bytes: - // Measured: `1047` - // Estimated: `4764` - // Minimum execution time: 53_983_000 picoseconds. - Weight::from_parts(55_296_000, 4764) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn bond_extra() -> Weight { - // Proof Size summary in bytes: - // Measured: `2028` - // Estimated: `8877` - // Minimum execution time: 96_590_000 picoseconds. - Weight::from_parts(98_921_000, 8877) - .saturating_add(RocksDbWeight::get().reads(9_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - fn unbond() -> Weight { - // Proof Size summary in bytes: - // Measured: `2233` - // Estimated: `8877` - // Minimum execution time: 99_901_000 picoseconds. - Weight::from_parts(102_919_000, 8877) - .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1021` - // Estimated: `4764` - // Minimum execution time: 45_230_000 picoseconds. - Weight::from_parts(47_052_829, 4764) - // Standard Error: 1_044 - .saturating_add(Weight::from_parts(43_887, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2294 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 97_534_000 picoseconds. - Weight::from_parts(104_772_163, 6248) - // Standard Error: 3_674 - .saturating_add(Weight::from_parts(1_470_124, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(13_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:1 w:0) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MinCommission (r:1 w:0) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:1) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:1 w:0) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:1 w:1) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForValidators (r:1 w:1) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn validate() -> Weight { - // Proof Size summary in bytes: - // Measured: `1414` - // Estimated: `4556` - // Minimum execution time: 57_467_000 picoseconds. - Weight::from_parts(59_437_000, 4556) - .saturating_add(RocksDbWeight::get().reads(11_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:128 w:128) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// The range of component `k` is `[1, 128]`. - fn kick(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1285 + k * (569 ±0)` - // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 32_857_000 picoseconds. - Weight::from_parts(37_116_967, 4556) - // Standard Error: 9_522 - .saturating_add(Weight::from_parts(8_796_167, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:1 w:0) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:17 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// The range of component `n` is `[1, 16]`. - fn nominate(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1908 + n * (102 ±0)` - // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 69_613_000 picoseconds. - Weight::from_parts(68_079_061, 6248) - // Standard Error: 18_554 - .saturating_add(Weight::from_parts(4_012_761, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn chill() -> Weight { - // Proof Size summary in bytes: - // Measured: `1748` - // Estimated: `6248` - // Minimum execution time: 60_430_000 picoseconds. - Weight::from_parts(62_702_000, 6248) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - fn set_payee() -> Weight { - // Proof Size summary in bytes: - // Measured: `808` - // Estimated: `4556` - // Minimum execution time: 14_276_000 picoseconds. - Weight::from_parts(14_766_000, 4556) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2 w:2) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - fn set_controller() -> Weight { - // Proof Size summary in bytes: - // Measured: `907` - // Estimated: `8122` - // Minimum execution time: 21_710_000 picoseconds. - Weight::from_parts(22_430_000, 8122) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: Staking ValidatorCount (r:0 w:1) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn set_validator_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_970_000 picoseconds. - Weight::from_parts(3_120_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_no_eras() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_362_000 picoseconds. - Weight::from_parts(9_785_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_new_era() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_275_000 picoseconds. - Weight::from_parts(9_678_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking ForceEra (r:0 w:1) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - fn force_new_era_always() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_414_000 picoseconds. - Weight::from_parts(9_848_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking Invulnerables (r:0 w:1) - /// Proof Skipped: Staking Invulnerables (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `v` is `[0, 1000]`. - fn set_invulnerables(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 3_061_000 picoseconds. - Weight::from_parts(3_618_535, 0) - // Standard Error: 44 - .saturating_add(Weight::from_parts(10_774, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, 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 Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:0 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[0, 100]`. - fn force_unstake(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2018 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 87_914_000 picoseconds. - Weight::from_parts(95_688_129, 6248) - // Standard Error: 5_030 - .saturating_add(Weight::from_parts(1_487_249, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().writes(12_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: Staking UnappliedSlashes (r:1 w:1) - /// Proof Skipped: Staking UnappliedSlashes (max_values: None, max_size: None, mode: Measured) - /// The range of component `s` is `[1, 1000]`. - fn cancel_deferred_slash(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `66639` - // Estimated: `70104` - // Minimum execution time: 99_269_000 picoseconds. - Weight::from_parts(1_154_264_637, 70104) - // Standard Error: 76_592 - .saturating_add(Weight::from_parts(6_490_888, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:257 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasRewardPoints (r:1 w:0) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:1 w:0) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:257 w:0) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: System Account (r:257 w:257) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 256]`. - fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `20217 + n * (143 ±0)` - // Estimated: `19844 + n * (2603 ±1)` - // Minimum execution time: 91_767_000 picoseconds. - Weight::from_parts(146_781_264, 19844) - // Standard Error: 31_341 - .saturating_add(Weight::from_parts(30_553_008, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) - .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) - } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:257 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:257 w:257) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasRewardPoints (r:1 w:0) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:1 w:0) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:257 w:0) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: System Account (r:257 w:257) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:257 w:257) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:257 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// The range of component `n` is `[0, 256]`. - fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `33190 + n * (377 ±0)` - // Estimated: `30845 + n * (3774 ±0)` - // Minimum execution time: 121_303_000 picoseconds. - Weight::from_parts(151_046_907, 30845) - // Standard Error: 41_899 - .saturating_add(Weight::from_parts(49_837_804, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) - .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) - } - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// The range of component `l` is `[1, 32]`. - fn rebond(l: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2029 + l * (7 ±0)` - // Estimated: `8877` - // Minimum execution time: 90_068_000 picoseconds. - Weight::from_parts(93_137_456, 8877) - // Standard Error: 4_799 - .saturating_add(Weight::from_parts(54_421, 0).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: Staking Bonded (r:1 w:1) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:1 w:1) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:1) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Staking SpanSlash (r:0 w:100) - /// Proof: Staking SpanSlash (max_values: None, max_size: Some(76), added: 2551, mode: MaxEncodedLen) - /// The range of component `s` is `[1, 100]`. - fn reap_stash(s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `2294 + s * (4 ±0)` - // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 103_139_000 picoseconds. - Weight::from_parts(107_036_296, 6248) - // Standard Error: 3_935 - .saturating_add(Weight::from_parts(1_465_860, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) - } - /// Storage: VoterList CounterForListNodes (r:1 w:0) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:200 w:0) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:110 w:0) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:110 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:11 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:110 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:110 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CounterForValidators (r:1 w:0) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinimumValidatorCount (r:1 w:0) - /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:1) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:0 w:10) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasValidatorPrefs (r:0 w:10) - /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:0 w:10) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking ErasTotalStake (r:0 w:1) - /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Staking ErasStartSessionIndex (r:0 w:1) - /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Staking MinimumActiveStake (r:0 w:1) - /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// The range of component `v` is `[1, 10]`. - /// The range of component `n` is `[0, 100]`. - fn new_era(v: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` - // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 587_156_000 picoseconds. - Weight::from_parts(590_176_000, 512390) - // Standard Error: 2_008_420 - .saturating_add(Weight::from_parts(64_526_052, 0).saturating_mul(v.into())) - // Standard Error: 200_128 - .saturating_add(Weight::from_parts(18_070_222, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(206_u64)) - .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) - } - /// Storage: VoterList CounterForListNodes (r:1 w:0) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:200 w:0) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2000 w:0) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:2000 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1000 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:2000 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2000 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking MinimumActiveStake (r:0 w:1) - /// Proof: Staking MinimumActiveStake (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// The range of component `v` is `[500, 1000]`. - /// The range of component `n` is `[500, 1000]`. - fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3217 + n * (911 ±0) + v * (395 ±0)` - // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 34_399_721_000 picoseconds. - Weight::from_parts(34_605_803_000, 512390) - // Standard Error: 380_106 - .saturating_add(Weight::from_parts(5_426_220, 0).saturating_mul(v.into())) - // Standard Error: 380_106 - .saturating_add(Weight::from_parts(3_318_197, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(201_u64)) - .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(v.into())) - } - /// Storage: Staking CounterForValidators (r:1 w:0) - /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1001 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// The range of component `v` is `[500, 1000]`. - fn get_npos_targets(v: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `983 + v * (50 ±0)` - // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_392_849_000 picoseconds. - Weight::from_parts(64_373_879, 3510) - // Standard Error: 8_995 - .saturating_add(Weight::from_parts(4_721_536, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:0 w:1) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:0 w:1) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:0 w:1) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:0 w:1) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:0 w:1) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - fn set_staking_configs_all_set() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 7_529_000 picoseconds. - Weight::from_parts(7_970_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinValidatorBond (r:0 w:1) - /// Proof: Staking MinValidatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking MaxValidatorsCount (r:0 w:1) - /// Proof: Staking MaxValidatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:0 w:1) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:0 w:1) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:0 w:1) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - fn set_staking_configs_all_remove() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 7_011_000 picoseconds. - Weight::from_parts(7_317_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking ChillThreshold (r:1 w:0) - /// Proof: Staking ChillThreshold (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Staking MaxNominatorsCount (r:1 w:0) - /// Proof: Staking MaxNominatorsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking MinNominatorBond (r:1 w:0) - /// Proof: Staking MinNominatorBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:2 w:2) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn chill_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `1871` - // Estimated: `6248` - // Minimum execution time: 75_982_000 picoseconds. - Weight::from_parts(77_412_000, 6248) - .saturating_add(RocksDbWeight::get().reads(11_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: Staking MinCommission (r:1 w:0) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:1) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - fn force_apply_min_commission() -> Weight { - // Proof Size summary in bytes: - // Measured: `694` - // Estimated: `3510` - // Minimum execution time: 13_923_000 picoseconds. - Weight::from_parts(14_356_000, 3510) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Staking MinCommission (r:0 w:1) - /// Proof: Staking MinCommission (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn set_min_commission() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 3_415_000 picoseconds. - Weight::from_parts(3_679_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 7e1b48fd8..e89aebc6d 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -9,12 +9,14 @@ readme.workspace = true repository.workspace = true [dependencies] +blake2 = { workspace = true } codec = { workspace = true } -scale-info = { workspace = true } -serde = { workspace = true } - frame-support = { workspace = true } frame-system = { workspace = true } +polkadot-ckb-merkle-mountain-range = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true } +sp-application-crypto = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } @@ -22,11 +24,14 @@ sp-std = { workspace = true } [features] default = ["std"] std = [ + "blake2/std", + "polkadot-ckb-merkle-mountain-range/std", "codec/std", "scale-info/std", "sp-core/std", "sp-std/std", "sp-runtime/std", + "sp-application-crypto/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 509db89d7..894bdeb40 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,22 +1,53 @@ #![cfg_attr(not(feature = "std"), no_std)] +use blake2::{Blake2s256, Digest}; use codec::{Decode, Encode}; +use frame_support::parameter_types; +use polkadot_ckb_merkle_mountain_range::Merge; use scale_info::{prelude::vec::Vec, TypeInfo}; use serde::{Deserialize, Serialize}; -use sp_core::hash::H160; +use sp_core::{crypto::KeyTypeId, hash::H160}; use sp_runtime::{AccountId32, Perquintill, RuntimeDebug}; pub mod traits; +parameter_types! { + pub MaxHostLen: u8 = 255; + pub MaxDomainLen: u8 = 255; +} + +pub const MAX_PAYOUT_BATCH_COUNT: u16 = 1000; +pub const MAX_PAYOUT_BATCH_SIZE: u16 = 1000; pub const MILLICENTS: u128 = 100_000; pub const CENTS: u128 = 1_000 * MILLICENTS; // assume this is worth about a cent. pub const DOLLARS: u128 = 100 * CENTS; pub type ClusterId = H160; pub type DdcEra = u32; pub type BucketId = u64; +pub type ClusterNodesCount = u16; pub type StorageNodePubKey = AccountId32; +pub type ActivityHash = [u8; 32]; +pub type BatchIndex = u16; + +pub struct MergeActivityHash; +impl Merge for MergeActivityHash { + type Item = ActivityHash; + fn merge( + lhs: &Self::Item, // Left side of tree + rhs: &Self::Item, // Right side of tree + ) -> Result { + let mut hasher = Blake2s256::new(); + + hasher.update(lhs.as_slice()); + hasher.update(rhs.as_slice()); + let hash = hasher.finalize(); + + Ok(ActivityHash::from(sp_core::H256::from_slice(hash.as_slice()))) + } +} // ClusterParams includes Governance non-sensetive parameters only +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct ClusterParams { pub node_provider_auth_contract: Option, @@ -25,12 +56,24 @@ pub struct ClusterParams { pub replication_total: u32, } -// ClusterGovParams includes Governance sensitive parameters +#[cfg(feature = "std")] +impl Default for ClusterParams { + fn default() -> Self { + ClusterParams { + node_provider_auth_contract: None, + erasure_coding_required: 0, + erasure_coding_total: 0, + replication_total: 0, + } + } +} + +// ClusterProtocolParams includes Governance sensitive parameters #[derive( Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Default, Serialize, Deserialize, )] #[scale_info(skip_type_params(Balance, BlockNumber, T))] -pub struct ClusterGovParams { +pub struct ClusterProtocolParams { pub treasury_share: Perquintill, pub validators_share: Perquintill, pub cluster_reserve_share: Perquintill, @@ -65,7 +108,17 @@ pub struct ClusterBondingParams { pub storage_unbonding_delay: BlockNumber, } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] +#[derive( + Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, TypeInfo, +)] +pub struct AggregatorInfo { + pub node_pub_key: NodePubKey, + pub node_params: StorageNodeParams, +} + +#[derive( + Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, TypeInfo, +)] pub enum NodePubKey { StoragePubKey(StorageNodePubKey), } @@ -93,7 +146,20 @@ impl TryFrom for NodeType { } } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] +#[derive( + Debug, + Serialize, + Deserialize, + Clone, + Hash, + Ord, + PartialOrd, + PartialEq, + Eq, + Encode, + Decode, + TypeInfo, +)] pub enum StorageNodeMode { /// DDC Storage node operates with enabled caching in RAM and stores data in Hard Drive Full = 1, @@ -101,9 +167,24 @@ pub enum StorageNodeMode { Storage = 2, /// DDC Storage node operates with enabled caching in RAM and doesn't store data in Hard Drive Cache = 3, + // DAC node + DAC = 4, } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +#[derive( + Debug, + Serialize, + Deserialize, + Clone, + Hash, + Ord, + PartialOrd, + PartialEq, + Eq, + Encode, + Decode, + TypeInfo, +)] pub struct StorageNodeParams { pub mode: StorageNodeMode, pub host: Vec, @@ -114,8 +195,168 @@ pub struct StorageNodeParams { pub p2p_port: u16, } +#[cfg(feature = "std")] +impl Default for StorageNodeParams { + fn default() -> Self { + StorageNodeParams { + mode: StorageNodeMode::Full, + host: Default::default(), + domain: Default::default(), + ssl: Default::default(), + http_port: Default::default(), + grpc_port: Default::default(), + p2p_port: Default::default(), + } + } +} + // Params fields are always coming from extrinsic input #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub enum NodeParams { StorageParams(StorageNodeParams), } + +/// DDC cluster status +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] +pub enum ClusterStatus { + Unbonded, + Bonded, + Activated, + Unbonding, +} + +/// DDC node kind added to DDC cluster +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] +pub enum ClusterNodeKind { + Genesis, + External, +} + +/// DDC node status in to DDC cluster +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Serialize, Deserialize)] +pub enum ClusterNodeStatus { + AwaitsValidation, + ValidationSucceeded, + ValidationFailed, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub struct ClusterNodeState { + pub kind: ClusterNodeKind, + pub status: ClusterNodeStatus, + pub added_at: BlockNumber, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Default)] +pub struct ClusterNodesStats { + pub await_validation: ClusterNodesCount, + pub validation_succeeded: ClusterNodesCount, + pub validation_failed: ClusterNodesCount, +} + +/// Stores usage of customers +#[derive( + PartialEq, Eq, Encode, Decode, Debug, TypeInfo, Default, Clone, Serialize, Deserialize, +)] +pub struct CustomerUsage { + pub transferred_bytes: u64, + pub stored_bytes: i64, + pub number_of_puts: u64, + pub number_of_gets: u64, +} + +/// Stores usage of node provider +#[derive( + PartialEq, Eq, Encode, Decode, Debug, TypeInfo, Default, Clone, Serialize, Deserialize, +)] +pub struct NodeUsage { + pub transferred_bytes: u64, + pub stored_bytes: i64, + pub number_of_puts: u64, + pub number_of_gets: u64, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Default)] +pub struct MMRProof { + pub mmr_size: u64, + pub proof: Vec, + pub leaf_with_position: (u64, ActivityHash), +} + +#[derive(Debug, PartialEq)] +pub enum NodeRepositoryError { + StorageNodeAlreadyExists, + StorageNodeDoesNotExist, +} + +#[derive(Debug, PartialEq)] +pub enum BucketVisitorError { + NoBucketWithId, + NotBucketOwner, + IncorrectClusterId, +} + +#[derive(Debug, PartialEq)] +pub enum PayoutError { + BillingReportDoesNotExist, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Default)] +// don't remove or change numbers, if needed add a new state to the end with new number +// DAC uses the state value for integration! +pub enum PayoutState { + #[default] + NotInitialized = 1, + Initialized = 2, + ChargingCustomers = 3, + CustomersChargedWithFees = 4, + RewardingProviders = 5, + ProvidersRewarded = 6, + Finalized = 7, +} + +pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"cer!"); + +pub mod sr25519 { + mod app_sr25519 { + use scale_info::prelude::string::String; + use sp_application_crypto::{app_crypto, sr25519}; + + use crate::KEY_TYPE; + app_crypto!(sr25519, KEY_TYPE); + } + + sp_application_crypto::with_pair! { + pub type AuthorityPair = app_sr25519::Pair; + } + pub type AuthoritySignature = app_sr25519::Signature; + pub type AuthorityId = app_sr25519::Public; +} + +pub mod crypto { + use scale_info::prelude::string::String; + use sp_core::sr25519::Signature as Sr25519Signature; + use sp_runtime::{ + app_crypto::{app_crypto, sr25519}, + traits::Verify, + MultiSignature, MultiSigner, + }; + + use super::KEY_TYPE; + app_crypto!(sr25519, KEY_TYPE); + pub struct OffchainIdentifierId; + impl frame_system::offchain::AppCrypto for OffchainIdentifierId { + type RuntimeAppPublic = Public; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; + } + + // implemented for mock runtime in test + impl frame_system::offchain::AppCrypto<::Signer, Sr25519Signature> + for OffchainIdentifierId + { + type RuntimeAppPublic = Public; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; + } +} diff --git a/primitives/src/traits/bucket.rs b/primitives/src/traits/bucket.rs new file mode 100644 index 000000000..908d005d0 --- /dev/null +++ b/primitives/src/traits/bucket.rs @@ -0,0 +1,20 @@ +use sp_runtime::DispatchResult; + +use crate::{BucketId, BucketVisitorError, ClusterId, CustomerUsage}; + +pub trait BucketManager { + fn inc_total_customer_usage( + cluster_id: &ClusterId, + bucket_id: BucketId, + content_owner: T::AccountId, + customer_usage: &CustomerUsage, + ) -> DispatchResult; +} + +pub trait BucketVisitor { + fn get_total_customer_usage( + cluster_id: &ClusterId, + bucket_id: BucketId, + content_owner: &T::AccountId, + ) -> Result, BucketVisitorError>; +} diff --git a/primitives/src/traits/cluster.rs b/primitives/src/traits/cluster.rs index 3ca47e726..563d613b2 100644 --- a/primitives/src/traits/cluster.rs +++ b/primitives/src/traits/cluster.rs @@ -1,76 +1,116 @@ -use codec::{Decode, Encode}; -use frame_support::dispatch::DispatchResult; use frame_system::{pallet_prelude::BlockNumberFor, Config}; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; +use sp_runtime::{DispatchError, DispatchResult}; +use sp_std::prelude::*; use crate::{ - ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterId, ClusterParams, - ClusterPricingParams, NodePubKey, NodeType, + ClusterBondingParams, ClusterFeesParams, ClusterId, ClusterNodeKind, ClusterNodeState, + ClusterNodeStatus, ClusterNodesStats, ClusterParams, ClusterPricingParams, + ClusterProtocolParams, ClusterStatus, DdcEra, NodePubKey, NodeType, }; -pub trait ClusterVisitor { - fn ensure_cluster(cluster_id: &ClusterId) -> Result<(), ClusterVisitorError>; - - fn get_bond_size( +pub trait ClusterQuery { + fn cluster_exists(cluster_id: &ClusterId) -> bool; + fn get_cluster_status(cluster_id: &ClusterId) -> Result; + fn get_manager_and_reserve_id( cluster_id: &ClusterId, - node_type: NodeType, - ) -> Result; + ) -> Result<(T::AccountId, T::AccountId), DispatchError>; +} - fn get_pricing_params( - cluster_id: &ClusterId, - ) -> Result; +pub trait ClusterProtocol: ClusterQuery { + fn get_bond_size(cluster_id: &ClusterId, node_type: NodeType) -> Result; - fn get_fees_params(cluster_id: &ClusterId) -> Result; + fn get_pricing_params(cluster_id: &ClusterId) -> Result; - fn get_reserve_account_id(cluster_id: &ClusterId) -> Result; + fn get_fees_params(cluster_id: &ClusterId) -> Result; fn get_chill_delay( cluster_id: &ClusterId, node_type: NodeType, - ) -> Result, ClusterVisitorError>; + ) -> Result, DispatchError>; fn get_unbonding_delay( cluster_id: &ClusterId, node_type: NodeType, - ) -> Result, ClusterVisitorError>; + ) -> Result, DispatchError>; fn get_bonding_params( cluster_id: &ClusterId, - ) -> Result>, ClusterVisitorError>; + ) -> Result>, DispatchError>; + + fn get_reserve_account_id(cluster_id: &ClusterId) -> Result; + + fn activate_cluster_protocol(cluster_id: &ClusterId) -> DispatchResult; + + fn update_cluster_protocol( + cluster_id: &ClusterId, + cluster_protocol_params: ClusterProtocolParams>, + ) -> DispatchResult; + + fn bond_cluster(cluster_id: &ClusterId) -> DispatchResult; + + fn start_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult; + + fn end_unbond_cluster(cluster_id: &ClusterId) -> DispatchResult; } pub trait ClusterCreator { - fn create_new_cluster( + fn create_cluster( cluster_id: ClusterId, cluster_manager_id: T::AccountId, cluster_reserve_id: T::AccountId, cluster_params: ClusterParams, - cluster_gov_params: ClusterGovParams>, + initial_protocol_params: ClusterProtocolParams>, ) -> DispatchResult; } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub enum ClusterVisitorError { - ClusterDoesNotExist, - ClusterGovParamsNotSet, -} +pub trait ClusterManager: ClusterQuery { + fn get_manager_account_id(cluster_id: &ClusterId) -> Result; + + fn contains_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + validation_status: Option, + ) -> bool; + + fn get_nodes(cluster_id: &ClusterId) -> Result, DispatchError>; -pub trait ClusterManager { - fn contains_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool; fn add_node( cluster_id: &ClusterId, node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError>; - fn remove_node( + node_kind: &ClusterNodeKind, + ) -> Result<(), DispatchError>; + + fn remove_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> Result<(), DispatchError>; + + fn get_node_state( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result>, DispatchError>; + + fn get_nodes_stats(cluster_id: &ClusterId) -> Result; + + fn validate_node( cluster_id: &ClusterId, node_pub_key: &NodePubKey, - ) -> Result<(), ClusterManagerError>; + succeeded: bool, + ) -> Result<(), DispatchError>; } - -pub enum ClusterManagerError { - AttemptToAddNonExistentNode, - AttemptToAddAlreadyAssignedNode, - AttemptToRemoveNotAssignedNode, - AttemptToRemoveNonExistentNode, +pub trait ClusterValidator { + /// Updates the `last_validated_era_id` for the given cluster and emits an event indicating the + /// update. + /// + /// # Parameters + /// + /// - `cluster_id`: A reference to the unique identifier of the cluster that needs its last + /// validated era updated. + /// - `era_id`: The new era identifier to be set as the last validated era for the cluster. + /// + /// # Returns + /// + /// Returns `Ok(())` if the operation was successful, otherwise returns a `DispatchError`. + /// + /// # Events + /// + /// Emits `ClusterEraValidated` event if the operation is successful. + fn set_last_validated_era(cluster_id: &ClusterId, era_id: DdcEra) -> Result<(), DispatchError>; } diff --git a/primitives/src/traits/cluster_gov.rs b/primitives/src/traits/cluster_gov.rs new file mode 100644 index 000000000..4223e1a61 --- /dev/null +++ b/primitives/src/traits/cluster_gov.rs @@ -0,0 +1,25 @@ +/// A number of members. +/// +/// This also serves as a number of voting members, and since for motions, each member may +/// vote exactly once, therefore also the number of votes for any given motion. +pub type MemberCount = u32; + +/// Default voting strategy when a member is inactive. +pub trait DefaultVote { + /// Get the default voting strategy, given: + /// + /// - Whether the prime member voted Aye. + /// - Raw number of yes votes. + /// - Raw number of no votes. + /// - Total number of member count. + fn default_vote( + prime_vote: Option, + yes_votes: MemberCount, + no_votes: MemberCount, + len: MemberCount, + ) -> bool; +} + +pub trait SeatsConsensus { + fn get_threshold(seats: MemberCount) -> MemberCount; +} diff --git a/primitives/src/traits/customer.rs b/primitives/src/traits/customer.rs index ac428137f..3dd71a76a 100644 --- a/primitives/src/traits/customer.rs +++ b/primitives/src/traits/customer.rs @@ -2,10 +2,15 @@ use core::u128; use sp_runtime::DispatchError; +use crate::{BucketId, ClusterId, CustomerUsage}; + pub trait CustomerCharger { fn charge_content_owner( + cluster_id: &ClusterId, + bucket_id: BucketId, content_owner: T::AccountId, billing_vault: T::AccountId, + customer_usage: &CustomerUsage, amount: u128, ) -> Result; } @@ -14,3 +19,7 @@ pub trait CustomerDepositor { fn deposit(customer: T::AccountId, amount: u128) -> Result<(), DispatchError>; fn deposit_extra(customer: T::AccountId, amount: u128) -> Result<(), DispatchError>; } + +pub trait CustomerVisitor { + fn get_bucket_owner(bucket_id: &BucketId) -> Result; +} diff --git a/primitives/src/traits/mod.rs b/primitives/src/traits/mod.rs index 11c06c36f..31bce3b53 100644 --- a/primitives/src/traits/mod.rs +++ b/primitives/src/traits/mod.rs @@ -1,13 +1,19 @@ +pub mod bucket; pub mod cluster; +pub mod cluster_gov; pub mod customer; pub mod node; pub mod pallet; +pub mod payout; pub mod staking; pub mod validator; +pub use bucket::*; pub use cluster::*; +pub use cluster_gov::*; pub use customer::*; pub use node::*; pub use pallet::*; +pub use payout::*; pub use staking::*; pub use validator::*; diff --git a/primitives/src/traits/node.rs b/primitives/src/traits/node.rs index c6de69e02..4d24adb60 100644 --- a/primitives/src/traits/node.rs +++ b/primitives/src/traits/node.rs @@ -1,11 +1,15 @@ use frame_support::dispatch::DispatchResult; use frame_system::Config; +use sp_runtime::DispatchError; -use crate::{ClusterId, NodeParams, NodePubKey}; +use crate::{ClusterId, NodeParams, NodePubKey, NodeUsage}; pub trait NodeVisitor { - fn get_cluster_id(node_pub_key: &NodePubKey) -> Result, NodeVisitorError>; + fn get_cluster_id(node_pub_key: &NodePubKey) -> Result, DispatchError>; fn exists(node_pub_key: &NodePubKey) -> bool; + fn get_node_provider_id(node_pub_key: &NodePubKey) -> Result; + fn get_node_params(node_pub_key: &NodePubKey) -> Result; + fn get_total_usage(node_pub_key: &NodePubKey) -> Result, DispatchError>; } pub trait NodeCreator { @@ -15,7 +19,3 @@ pub trait NodeCreator { node_params: NodeParams, ) -> DispatchResult; } - -pub enum NodeVisitorError { - NodeDoesNotExist, -} diff --git a/primitives/src/traits/pallet.rs b/primitives/src/traits/pallet.rs index dec6f71b1..c36ac984b 100644 --- a/primitives/src/traits/pallet.rs +++ b/primitives/src/traits/pallet.rs @@ -1,5 +1,13 @@ +use frame_support::traits::OriginTrait; use frame_system::Config; +pub type PalletsOriginOf = + <::RuntimeOrigin as OriginTrait>::PalletsOrigin; + +pub trait GetDdcOrigin { + fn get() -> T::RuntimeOrigin; +} + pub trait PalletVisitor { fn get_account_id() -> T::AccountId; } diff --git a/primitives/src/traits/payout.rs b/primitives/src/traits/payout.rs new file mode 100644 index 000000000..e8c6a6351 --- /dev/null +++ b/primitives/src/traits/payout.rs @@ -0,0 +1,93 @@ +use sp_runtime::DispatchResult; + +use crate::{ + BatchIndex, BucketId, ClusterId, CustomerUsage, DdcEra, MMRProof, NodeUsage, PayoutError, + PayoutState, +}; + +pub trait PayoutProcessor {} + +pub trait PayoutVisitor { + // todo! factor out into PayoutProcessor + fn begin_billing_report( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + start_era: i64, + end_era: i64, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn begin_charging_customers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn send_charging_customers_batch( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payers: &[(T::AccountId, BucketId, CustomerUsage)], + batch_proof: MMRProof, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn end_charging_customers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn begin_rewarding_providers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + max_batch_index: BatchIndex, + total_node_usage: NodeUsage, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn send_rewarding_providers_batch( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + batch_index: BatchIndex, + payees: &[(T::AccountId, NodeUsage)], + batch_proof: MMRProof, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn end_rewarding_providers( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult; + + // todo! factor out into PayoutProcessor + fn end_billing_report( + origin: T::AccountId, + cluster_id: ClusterId, + era_id: DdcEra, + ) -> DispatchResult; + + fn get_billing_report_status(cluster_id: &ClusterId, era: DdcEra) -> PayoutState; + + fn all_customer_batches_processed(cluster_id: &ClusterId, era_id: DdcEra) -> bool; + + fn all_provider_batches_processed(cluster_id: &ClusterId, era_id: DdcEra) -> bool; + + fn get_next_customer_batch_for_payment( + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Result, PayoutError>; + + fn get_next_provider_batch_for_payment( + cluster_id: &ClusterId, + era_id: DdcEra, + ) -> Result, PayoutError>; +} diff --git a/primitives/src/traits/staking.rs b/primitives/src/traits/staking.rs index 2ce6e4fb3..62d81ea4a 100644 --- a/primitives/src/traits/staking.rs +++ b/primitives/src/traits/staking.rs @@ -1,4 +1,5 @@ use frame_system::Config; +use sp_runtime::DispatchResult; use crate::{ClusterId, NodePubKey}; @@ -11,6 +12,8 @@ pub trait StakingVisitor { fn has_stake(node_pub_key: &NodePubKey) -> bool; fn has_chilling_attempt(node_pub_key: &NodePubKey) -> Result; + + fn stash_by_ctrl(controller: &T::AccountId) -> Result; } pub trait StakerCreator { @@ -20,10 +23,17 @@ pub trait StakerCreator { node: NodePubKey, value: Balance, cluster_id: ClusterId, - ) -> sp_runtime::DispatchResult; + ) -> DispatchResult; + + fn bond_cluster( + cluster_stash: T::AccountId, + cluster_controller: T::AccountId, + cluster_id: ClusterId, + ) -> DispatchResult; } pub enum StakingVisitorError { NodeStakeDoesNotExist, NodeStakeIsInBadState, + ControllerDoesNotExist, } diff --git a/primitives/src/traits/treasury.rs b/primitives/src/traits/treasury.rs deleted file mode 100644 index f97529467..000000000 --- a/primitives/src/traits/treasury.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub trait TreasuryVisitor { - fn cluster_has_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool; diff --git a/primitives/src/traits/validator.rs b/primitives/src/traits/validator.rs index af7e29dc1..0dbc2a73a 100644 --- a/primitives/src/traits/validator.rs +++ b/primitives/src/traits/validator.rs @@ -1,6 +1,23 @@ use frame_system::Config; use sp_std::prelude::*; +use crate::{BatchIndex, BucketId, ClusterId, CustomerUsage, DdcEra, MMRProof, NodeUsage}; + pub trait ValidatorVisitor { - fn get_active_validators() -> Vec; + fn setup_validators(validators: Vec); + fn is_ocw_validator(caller: T::AccountId) -> bool; + fn is_customers_batch_valid( + cluster_id: ClusterId, + era: DdcEra, + batch_index: BatchIndex, + payers: &[(T::AccountId, BucketId, CustomerUsage)], + batch_proof: &MMRProof, + ) -> bool; + fn is_providers_batch_valid( + cluster_id: ClusterId, + era: DdcEra, + batch_index: BatchIndex, + payees: &[(T::AccountId, NodeUsage)], + batch_proof: &MMRProof, + ) -> bool; } diff --git a/runtime/cere-dev/Cargo.toml b/runtime/cere-dev/Cargo.toml index 7b0b75c2e..f33bc325d 100644 --- a/runtime/cere-dev/Cargo.toml +++ b/runtime/cere-dev/Cargo.toml @@ -53,8 +53,10 @@ pallet-insecure-randomness-collective-flip = { workspace = true } pallet-membership = { workspace = true } pallet-multisig = { workspace = true } pallet-nomination-pools = { workspace = true } +pallet-nomination-pools-benchmarking = { workspace = true } pallet-nomination-pools-runtime-api = { workspace = true } pallet-offences = { workspace = true } +pallet-offences-benchmarking = { workspace = true, optional = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-recovery = { workspace = true } @@ -95,12 +97,15 @@ cere-runtime-common = { workspace = true } ddc-primitives = { workspace = true } pallet-chainbridge = { workspace = true } pallet-ddc-clusters = { workspace = true } +pallet-ddc-clusters-gov = { workspace = true } pallet-ddc-customers = { workspace = true } pallet-ddc-nodes = { workspace = true } pallet-ddc-payouts = { workspace = true } pallet-ddc-staking = { workspace = true } +pallet-ddc-verification = { workspace = true } pallet-erc20 = { workspace = true } pallet-erc721 = { workspace = true } +pallet-origins = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true, default-features = true } @@ -182,12 +187,14 @@ std = [ "pallet-ddc-staking/std", "pallet-ddc-customers/std", "pallet-ddc-clusters/std", - "pallet-ddc-payouts/std", + "pallet-ddc-verification/std", "cere-runtime-common/std", - "sp-arithmetic/std", "pallet-conviction-voting/std", "pallet-referenda/std", "pallet-whitelist/std", + "pallet-ddc-clusters-gov/std", + "sp-arithmetic/std", + "pallet-origins/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -213,6 +220,8 @@ runtime-benchmarks = [ "pallet-membership/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", + "pallet-nomination-pools-benchmarking/runtime-benchmarks", + "pallet-offences-benchmarking/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", @@ -235,6 +244,10 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-referenda/runtime-benchmarks", "pallet-whitelist/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-ddc-clusters-gov/runtime-benchmarks", + "pallet-origins/runtime-benchmarks", + "pallet-ddc-verification/runtime-benchmarks", ] try-runtime = [ "frame-executive/try-runtime", @@ -286,4 +299,8 @@ try-runtime = [ "pallet-conviction-voting/try-runtime", "pallet-referenda/try-runtime", "pallet-whitelist/try-runtime", + "pallet-preimage/try-runtime", + "pallet-origins/try-runtime", + "pallet-ddc-clusters-gov/try-runtime", + "pallet-ddc-verification/try-runtime", ] diff --git a/runtime/cere-dev/src/governance/mod.rs b/runtime/cere-dev/src/governance/mod.rs index 75b25fcc8..5b6d4e3d4 100644 --- a/runtime/cere-dev/src/governance/mod.rs +++ b/runtime/cere-dev/src/governance/mod.rs @@ -1,14 +1,22 @@ -use frame_support::parameter_types; +use frame_support::{ + pallet_prelude::EnsureOrigin, + parameter_types, + traits::{EitherOf, EnsureOriginWithArg, OriginTrait}, +}; use frame_system::EnsureRootWithSuccess; +use sp_std::marker::PhantomData; use super::*; -mod origins; -pub use origins::{ - pallet_custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, Spender, - StakingAdmin, Treasurer, WhitelistedCaller, -}; mod tracks; +use cere_runtime_common::constants::tracks::{ + CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID, CLUSTER_PROTOCOL_UPDATER_TRACK_ID, +}; +use ddc_primitives::traits::pallet::PalletsOriginOf; +pub use pallet_origins::pallet::{ + ClusterProtocolActivator, ClusterProtocolUpdater, GeneralAdmin, ReferendumCanceller, + ReferendumKiller, Spender, StakingAdmin, Treasurer, WhitelistedCaller, +}; pub use tracks::TracksInfo; parameter_types! { @@ -38,14 +46,17 @@ parameter_types! { pub type TreasurySpender = EitherOf, Spender>; -impl origins::pallet_custom_origins::Config for Runtime {} +impl pallet_origins::Config for Runtime {} impl pallet_whitelist::Config for Runtime { type WeightInfo = pallet_whitelist::weights::SubstrateWeight; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; - type WhitelistOrigin = EnsureRoot; - type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type WhitelistOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureMembers, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; type Preimages = Preimage; } @@ -55,7 +66,7 @@ impl pallet_referenda::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Scheduler = Scheduler; type Currency = Balances; - type SubmitOrigin = frame_system::EnsureSigned; + type SubmitOrigin = EnsureOfPermittedReferendaOrigin; type CancelOrigin = EitherOf, ReferendumCanceller>; type KillOrigin = EitherOf, ReferendumKiller>; type Slash = Treasury; @@ -68,3 +79,48 @@ impl pallet_referenda::Config for Runtime { type Tracks = TracksInfo; type Preimages = Preimage; } + +pub struct EnsureOfPermittedReferendaOrigin(PhantomData); +impl EnsureOriginWithArg> + for EnsureOfPermittedReferendaOrigin +where + ::RuntimeOrigin: OriginTrait, +{ + type Success = T::AccountId; + + fn try_origin( + o: T::RuntimeOrigin, + proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = as EnsureOrigin<_>>::try_origin(o.clone())?; + + let track_id = + match >::track_for( + proposal_origin, + ) { + Ok(track_id) => track_id, + Err(_) => return Err(o), + }; + + if track_id == CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID || + track_id == CLUSTER_PROTOCOL_UPDATER_TRACK_ID + { + let clusters_governance = >::get_account_id(); + if origin == clusters_governance { + Ok(origin) + } else { + Err(o) + } + } else { + Ok(origin) + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin( + _proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = frame_benchmarking::account::("successful_origin", 0, 0); + Ok(frame_system::RawOrigin::Signed(origin).into()) + } +} diff --git a/runtime/cere-dev/src/governance/tracks.rs b/runtime/cere-dev/src/governance/tracks.rs index d4c813a46..ae39005f3 100644 --- a/runtime/cere-dev/src/governance/tracks.rs +++ b/runtime/cere-dev/src/governance/tracks.rs @@ -1,51 +1,13 @@ //! Track configurations for governance. -use super::*; +use cere_runtime_common::constants::tracks::*; -const fn percent(x: i32) -> sp_arithmetic::FixedI64 { - sp_arithmetic::FixedI64::from_rational(x as u128, 100) -} -use pallet_referenda::Curve; -const APP_ROOT: Curve = Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_ROOT: Curve = Curve::make_linear(28, 28, percent(0), percent(50)); -const APP_STAKING_ADMIN: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_STAKING_ADMIN: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_TREASURER: Curve = Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_TREASURER: Curve = Curve::make_linear(28, 28, percent(0), percent(50)); -const APP_FELLOWSHIP_ADMIN: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_FELLOWSHIP_ADMIN: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_GENERAL_ADMIN: Curve = - Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_GENERAL_ADMIN: Curve = - Curve::make_reciprocal(7, 28, percent(10), percent(0), percent(50)); -const APP_REFERENDUM_CANCELLER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_REFERENDUM_CANCELLER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_REFERENDUM_KILLER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_REFERENDUM_KILLER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_SMALL_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); -const SUP_SMALL_TIPPER: Curve = Curve::make_reciprocal(1, 28, percent(4), percent(0), percent(50)); -const APP_BIG_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); -const SUP_BIG_TIPPER: Curve = Curve::make_reciprocal(8, 28, percent(1), percent(0), percent(50)); -const APP_SMALL_SPENDER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_SMALL_SPENDER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_MEDIUM_SPENDER: Curve = Curve::make_linear(23, 28, percent(50), percent(100)); -const SUP_MEDIUM_SPENDER: Curve = - Curve::make_reciprocal(16, 28, percent(1), percent(0), percent(50)); -const APP_BIG_SPENDER: Curve = Curve::make_linear(28, 28, percent(50), percent(100)); -const SUP_BIG_SPENDER: Curve = Curve::make_reciprocal(20, 28, percent(1), percent(0), percent(50)); -const APP_WHITELISTED_CALLER: Curve = - Curve::make_reciprocal(16, 28 * 24, percent(96), percent(50), percent(100)); -const SUP_WHITELISTED_CALLER: Curve = - Curve::make_reciprocal(1, 28, percent(20), percent(5), percent(50)); +use super::*; +use crate::{Balance, BlockNumber}; -const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13] = [ +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 14] = [ ( - 0, + ROOT_TRACK_ID, pallet_referenda::TrackInfo { name: "root", max_deciding: 1, @@ -59,7 +21,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 1, + WHITELISTED_CALLER_TRACK_ID, pallet_referenda::TrackInfo { name: "whitelisted_caller", max_deciding: 100, @@ -73,11 +35,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 10, + STAKING_ADMIN_TRACK_ID, pallet_referenda::TrackInfo { name: "staking_admin", max_deciding: 10, - decision_deposit: 5 * GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -87,11 +49,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 11, + TREASURER_TRACK_ID, pallet_referenda::TrackInfo { name: "treasurer", max_deciding: 10, - decision_deposit: GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -101,25 +63,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 13, - pallet_referenda::TrackInfo { - name: "fellowship_admin", - max_deciding: 10, - decision_deposit: 5 * GRAND, - prepare_period: 2 * HOURS, - decision_period: 28 * DAYS, - confirm_period: 3 * HOURS, - min_enactment_period: 10 * MINUTES, - min_approval: APP_FELLOWSHIP_ADMIN, - min_support: SUP_FELLOWSHIP_ADMIN, - }, - ), - ( - 14, + GENERAL_ADMIN_TRACK_ID, pallet_referenda::TrackInfo { name: "general_admin", max_deciding: 10, - decision_deposit: 5 * GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -129,7 +77,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 20, + REFERENDUM_CANCELER_TRACK_ID, pallet_referenda::TrackInfo { name: "referendum_canceller", max_deciding: 1_000, @@ -143,7 +91,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 21, + REFERENDUM_KILLER_TRACK_ID, pallet_referenda::TrackInfo { name: "referendum_killer", max_deciding: 1_000, @@ -157,11 +105,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 30, + SMALL_TIPPER_TRACK_ID, pallet_referenda::TrackInfo { name: "small_tipper", max_deciding: 200, - decision_deposit: DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: MINUTES, decision_period: 7 * DAYS, confirm_period: 10 * MINUTES, @@ -171,11 +119,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 31, + BIG_TIPPER_TRACK_ID, pallet_referenda::TrackInfo { name: "big_tipper", max_deciding: 100, - decision_deposit: 10 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 10 * MINUTES, decision_period: 7 * DAYS, confirm_period: HOURS, @@ -185,11 +133,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 32, + SMALL_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "small_spender", max_deciding: 50, - decision_deposit: 100 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 12 * HOURS, @@ -199,11 +147,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 33, + MEDIUM_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "medium_spender", max_deciding: 50, - decision_deposit: 200 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 24 * HOURS, @@ -213,11 +161,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 34, + BIG_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "big_spender", max_deciding: 50, - decision_deposit: 400 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 48 * HOURS, @@ -226,6 +174,34 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 min_support: SUP_BIG_SPENDER, }, ), + ( + CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_activator", + max_deciding: 50, + decision_deposit: DOLLARS, + prepare_period: 0, + decision_period: 2 * MINUTES, + confirm_period: MINUTES, + min_enactment_period: 0, + min_approval: APP_CLUSTER_PROTOCOL_ACTIVATOR, + min_support: SUP_CLUSTER_PROTOCOL_ACTIVATOR, + }, + ), + ( + CLUSTER_PROTOCOL_UPDATER_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_updater", + max_deciding: 50, + decision_deposit: DOLLARS, + prepare_period: 0, + decision_period: 2 * MINUTES, + confirm_period: MINUTES, + min_enactment_period: 0, + min_approval: APP_CLUSTER_PROTOCOL_UPDATER, + min_support: SUP_CLUSTER_PROTOCOL_UPDATER, + }, + ), ]; pub struct TracksInfo; @@ -238,30 +214,37 @@ impl pallet_referenda::TracksInfo for TracksInfo { fn track_for(id: &Self::RuntimeOrigin) -> Result { if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { match system_origin { - frame_system::RawOrigin::Root => Ok(0), + frame_system::RawOrigin::Root => Ok(ROOT_TRACK_ID), _ => Err(()), } - } else if let Ok(custom_origin) = origins::Origin::try_from(id.clone()) { + } else if let Ok(custom_origin) = pallet_origins::pallet::Origin::try_from(id.clone()) { match custom_origin { - origins::Origin::WhitelistedCaller => Ok(1), + pallet_origins::pallet::Origin::WhitelistedCaller => + Ok(WHITELISTED_CALLER_TRACK_ID), // General admin - origins::Origin::StakingAdmin => Ok(10), - origins::Origin::Treasurer => Ok(11), - origins::Origin::FellowshipAdmin => Ok(13), - origins::Origin::GeneralAdmin => Ok(14), + pallet_origins::pallet::Origin::StakingAdmin => Ok(STAKING_ADMIN_TRACK_ID), + pallet_origins::pallet::Origin::Treasurer => Ok(TREASURER_TRACK_ID), + pallet_origins::pallet::Origin::GeneralAdmin => Ok(GENERAL_ADMIN_TRACK_ID), // Referendum admins - origins::Origin::ReferendumCanceller => Ok(20), - origins::Origin::ReferendumKiller => Ok(21), + pallet_origins::pallet::Origin::ReferendumCanceller => + Ok(REFERENDUM_CANCELER_TRACK_ID), + pallet_origins::pallet::Origin::ReferendumKiller => Ok(REFERENDUM_KILLER_TRACK_ID), // Limited treasury spenders - origins::Origin::SmallTipper => Ok(30), - origins::Origin::BigTipper => Ok(31), - origins::Origin::SmallSpender => Ok(32), - origins::Origin::MediumSpender => Ok(33), - origins::Origin::BigSpender => Ok(34), + pallet_origins::pallet::Origin::SmallTipper => Ok(SMALL_TIPPER_TRACK_ID), + pallet_origins::pallet::Origin::BigTipper => Ok(BIG_TIPPER_TRACK_ID), + pallet_origins::pallet::Origin::SmallSpender => Ok(SMALL_SPENDER_TRACK_ID), + pallet_origins::pallet::Origin::MediumSpender => Ok(MEDIUM_SPENDER_TRACK_ID), + pallet_origins::pallet::Origin::BigSpender => Ok(BIG_SPENDER_TRACK_ID), + // DDC admins + pallet_origins::pallet::Origin::ClusterProtocolActivator => + Ok(CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID), + pallet_origins::pallet::Origin::ClusterProtocolUpdater => + Ok(CLUSTER_PROTOCOL_UPDATER_TRACK_ID), } } else { Err(()) } } } + pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); diff --git a/runtime/cere-dev/src/lib.rs b/runtime/cere-dev/src/lib.rs index 37843e8d1..06cd58d55 100644 --- a/runtime/cere-dev/src/lib.rs +++ b/runtime/cere-dev/src/lib.rs @@ -23,7 +23,10 @@ #![recursion_limit = "256"] use codec::{Decode, Encode, MaxEncodedLen}; -use ddc_primitives::traits::pallet::PalletVisitor; +use ddc_primitives::{ + traits::pallet::{GetDdcOrigin, PalletVisitor}, + MAX_PAYOUT_BATCH_COUNT, MAX_PAYOUT_BATCH_SIZE, +}; use frame_election_provider_support::{ bounds::ElectionBoundsBuilder, onchain, BalancingConfig, SequentialPhragmen, VoteWeight, }; @@ -33,9 +36,11 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ + fungible::HoldConsideration, + tokens::{PayFromAccount, UnityAssetBalanceConversion}, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOf, EitherOfDiverse, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, Nothing, - OnUnbalanced, WithdrawReasons, + EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, + LinearStoragePrice, Nothing, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ @@ -66,6 +71,7 @@ use pallet_election_provider_multi_phase::SolutionAccuracyOf; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; +use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_session::historical::{self as pallet_session_historical}; #[cfg(any(feature = "std", test))] @@ -76,7 +82,10 @@ pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdj use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{ + crypto::{AccountId32, KeyTypeId}, + OpaqueMetadata, H256, +}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_io::hashing::blake2_128; #[cfg(any(feature = "std", test))] @@ -86,14 +95,15 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, - Identity as IdentityConvert, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup, + self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, Convert, ConvertInto, + Identity as IdentityConvert, IdentityLookup, NumberFor, OpaqueKeys, SaturatedConversion, + StaticLookup, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perbill, Percent, Permill, Perquintill, RuntimeDebug, }; -use sp_std::prelude::*; +use sp_std::{marker::PhantomData, prelude::*}; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -111,7 +121,10 @@ use sp_runtime::generic::Era; // Governance configurations. pub mod governance; -use governance::{pallet_custom_origins, GeneralAdmin, StakingAdmin, Treasurer, TreasurySpender}; +use governance::{ + ClusterProtocolActivator, ClusterProtocolUpdater, GeneralAdmin, StakingAdmin, Treasurer, + TreasurySpender, +}; /// Generated voter bag information. mod voter_bags; @@ -140,10 +153,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 53003, + spec_version: 61000, impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 17, + transaction_version: 19, state_version: 0, }; @@ -357,6 +370,7 @@ parameter_types! { pub const PreimageMaxSize: u32 = 4096 * 1024; pub const PreimageBaseDeposit: Balance = deposit(2, 64); pub const PreimageByteDeposit: Balance = deposit(0, 1); + pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); } impl pallet_preimage::Config for Runtime { @@ -364,8 +378,12 @@ impl pallet_preimage::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureRoot; - type BaseDeposit = PreimageBaseDeposit; - type ByteDeposit = PreimageByteDeposit; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; } parameter_types! { @@ -379,7 +397,7 @@ impl pallet_scheduler::Config for Runtime { type PalletsOrigin = OriginCaller; type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; - type ScheduleOrigin = EnsureRoot; + type ScheduleOrigin = EitherOf, Treasurer>; type MaxScheduledPerBlock = ConstU32<512>; type WeightInfo = pallet_scheduler::weights::SubstrateWeight; type OriginPrivilegeCmp = EqualPrivilegeOnly; @@ -443,8 +461,9 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = pallet_balances::weights::SubstrateWeight; - type FreezeIdentifier = (); - type MaxFreezes = (); + type FreezeIdentifier = RuntimeFreezeReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; type MaxHolds = MaxHolds; } @@ -489,15 +508,41 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (Staking, ImOnline); } +impl_opaque_keys! { + pub struct OldSessionKeys { + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, + pub authority_discovery: AuthorityDiscovery, + } +} + impl_opaque_keys! { pub struct SessionKeys { pub grandpa: Grandpa, pub babe: Babe, pub im_online: ImOnline, pub authority_discovery: AuthorityDiscovery, + pub ddc_verification: DdcVerification, } } +fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys { + SessionKeys { + grandpa: old.grandpa, + babe: old.babe, + im_online: old.im_online, + authority_discovery: old.authority_discovery, + ddc_verification: { + let mut id: ddc_primitives::sr25519::AuthorityId = + sp_core::sr25519::Public::from_raw([0u8; 32]).into(); + let id_raw: &mut [u8] = id.as_mut(); + id_raw[0..32].copy_from_slice(v.as_ref()); + id_raw[0..4].copy_from_slice(b"cer!"); + id + }, + } +} impl pallet_session::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ValidatorId = ::AccountId; @@ -531,6 +576,7 @@ parameter_types! { pub const BondingDuration: sp_staking::EraIndex = 3; pub const SlashDeferDuration: sp_staking::EraIndex = 2; pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; + pub const MaxExposurePageSize: u32 = 512; pub const MaxNominatorRewardedPerValidator: u32 = 256; pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); pub OffchainRepeat: BlockNumber = 5; @@ -558,8 +604,8 @@ impl pallet_staking::Config for Runtime { type AdminOrigin = EitherOf, StakingAdmin>; type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; + type MaxExposurePageSize = MaxExposurePageSize; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = ElectionProviderMultiPhase; type GenesisElectionProvider = onchain::OnChainExecution; @@ -581,8 +627,6 @@ impl pallet_fast_unstake::Config for Runtime { type BatchSize = frame_support::traits::ConstU32<64>; type Staking = Staking; type MaxErasToCheckPerBlock = ConstU32<1>; - #[cfg(feature = "runtime-benchmarks")] - type MaxBackersPerValidator = MaxNominatorRewardedPerValidator; type WeightInfo = (); } @@ -593,7 +637,6 @@ parameter_types! { // signed config pub const SignedRewardBase: Balance = DOLLARS; - pub const SignedDepositBase: Balance = DOLLARS; pub const SignedDepositByte: Balance = CENTS; pub BetterUnsignedThreshold: Perbill = Perbill::from_rational(1u32, 10_000); @@ -709,6 +752,15 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime { } } +/// Returning a fixed value to respect the initial logic. +/// This could depend on the lenght of the solution. +pub struct FixedSignedDepositBase; +impl Convert for FixedSignedDepositBase { + fn convert(_: usize) -> u128 { + DOLLARS + } +} + impl pallet_election_provider_multi_phase::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -721,7 +773,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type MinerTxPriority = MultiPhaseUnsignedPriority; type SignedMaxSubmissions = ConstU32<10>; type SignedRewardBase = SignedRewardBase; - type SignedDepositBase = SignedDepositBase; + type SignedDepositBase = FixedSignedDepositBase; type SignedDepositByte = SignedDepositByte; type SignedMaxRefunds = ConstU32<3>; type SignedDepositWeight = (); @@ -768,6 +820,10 @@ parameter_types! { pub const MaxApprovals: u32 = 100; } +parameter_types! { + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + impl pallet_treasury::Config for Runtime { type PalletId = TreasuryPalletId; type Currency = Balances; @@ -785,6 +841,14 @@ impl pallet_treasury::Config for Runtime { type WeightInfo = pallet_treasury::weights::SubstrateWeight; type MaxApprovals = MaxApprovals; type SpendOrigin = TreasurySpender; + type AssetKind = (); + type Beneficiary = Self::AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<10>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -871,11 +935,8 @@ impl pallet_contracts::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); - type Migrations = ( - pallet_contracts::migration::v13::Migration, - pallet_contracts::migration::v14::Migration, - pallet_contracts::migration::v15::Migration, - ); + type Migrations = (); + type Xcm = (); } impl pallet_sudo::Config for Runtime { @@ -990,7 +1051,7 @@ impl pallet_grandpa::Config for Runtime { parameter_types! { pub const BasicDeposit: Balance = 10 * DOLLARS; // 258 bytes on-chain - pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain + pub const ByteDeposit: Balance = deposit(0, 1); pub const SubAccountDeposit: Balance = 2 * DOLLARS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -1001,10 +1062,10 @@ impl pallet_identity::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; + type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; type ForceOrigin = EitherOf, GeneralAdmin>; @@ -1098,6 +1159,7 @@ parameter_types! { impl pallet_nomination_pools::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type RuntimeFreezeReason = RuntimeFreezeReason; type RewardCounter = FixedU128; type BalanceToU256 = cere_runtime_common::BalanceToU256; type U256ToBalance = cere_runtime_common::U256ToBalance; @@ -1111,15 +1173,22 @@ impl pallet_nomination_pools::Config for Runtime { type WeightInfo = (); } +parameter_types! { + pub const ClusterBondingAmount: Balance = 100 * GRAND; + pub const ClusterUnboningDelay: BlockNumber = 28 * DAYS; +} + impl pallet_ddc_staking::Config for Runtime { type Currency = Balances; type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_ddc_staking::weights::SubstrateWeight; - type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; type ClusterCreator = pallet_ddc_clusters::Pallet; type ClusterManager = pallet_ddc_clusters::Pallet; type NodeVisitor = pallet_ddc_nodes::Pallet; type NodeCreator = pallet_ddc_nodes::Pallet; + type ClusterBondingAmount = ClusterBondingAmount; + type ClusterUnboningDelay = ClusterUnboningDelay; } parameter_types! { @@ -1132,7 +1201,7 @@ impl pallet_ddc_customers::Config for Runtime { type Currency = Balances; type PalletId = DdcCustomersPalletId; type RuntimeEvent = RuntimeEvent; - type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; type ClusterCreator = pallet_ddc_clusters::Pallet; type WeightInfo = pallet_ddc_customers::weights::SubstrateWeight; } @@ -1150,9 +1219,9 @@ impl pallet_ddc_clusters::Config for Runtime { type StakerCreator = pallet_ddc_staking::Pallet; type Currency = Balances; type WeightInfo = pallet_ddc_clusters::weights::SubstrateWeight; - type MinErasureCodingRequiredLimit = ConstU32<4>; - type MinErasureCodingTotalLimit = ConstU32<6>; - type MinReplicationTotalLimit = ConstU32<3>; + type MinErasureCodingRequiredLimit = ConstU32<0>; + type MinErasureCodingTotalLimit = ConstU32<0>; + type MinReplicationTotalLimit = ConstU32<0>; } parameter_types! { @@ -1171,13 +1240,115 @@ impl pallet_ddc_payouts::Config for Runtime { type PalletId = PayoutsPalletId; type Currency = Balances; type CustomerCharger = DdcCustomers; + type BucketVisitor = DdcCustomers; type CustomerDepositor = DdcCustomers; - type ClusterVisitor = DdcClusters; + type ClusterProtocol = DdcClusters; type TreasuryVisitor = TreasuryWrapper; type NominatorsAndValidatorsList = pallet_staking::UseNominatorsAndValidatorsMap; type ClusterCreator = DdcClusters; type WeightInfo = pallet_ddc_payouts::weights::SubstrateWeight; type VoteScoreToU64 = IdentityConvert; // used for UseNominatorsAndValidatorsMap + type ValidatorVisitor = pallet_ddc_verification::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type AccountIdConverter = AccountId32; +} + +parameter_types! { + pub const TechnicalMotionDuration: BlockNumber = 5 * DAYS; + pub const TechnicalMaxProposals: u32 = 100; + pub const TechnicalMaxMembers: u32 = 100; +} + +type TechCommCollective = pallet_collective::Instance3; +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalMotionDuration; + type MaxProposals = TechnicalMaxProposals; + type MaxMembers = TechnicalMaxMembers; + type SetMembersOrigin = EnsureRoot; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = pallet_collective::weights::SubstrateWeight; + type MaxProposalWeight = MaxCollectivesProposalWeight; +} + +parameter_types! { + pub const ClustersGovPalletId: PalletId = PalletId(*b"clustgov"); + pub const ClusterProposalDuration: BlockNumber = 7 * DAYS; + pub const MinValidatedNodesCount: u16 = 3; + pub ClusterProtocolActivatorTrackOrigin: RuntimeOrigin = pallet_origins::Origin::ClusterProtocolActivator.into(); + pub ClusterProtocolUpdaterTrackOrigin: RuntimeOrigin = pallet_origins::Origin::ClusterProtocolUpdater.into(); + pub const ReferendumEnactmentDuration: BlockNumber = 1; +} + +impl pallet_ddc_clusters_gov::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = ClustersGovPalletId; + type Currency = Balances; + type WeightInfo = pallet_ddc_clusters_gov::weights::SubstrateWeight; + type OpenGovActivatorTrackOrigin = DdcOriginAsNative; + type OpenGovActivatorOrigin = EitherOf, ClusterProtocolActivator>; + type OpenGovUpdaterTrackOrigin = DdcOriginAsNative; + type OpenGovUpdaterOrigin = EitherOf, ClusterProtocolUpdater>; + type ClusterProposalCall = RuntimeCall; + type ClusterProposalDuration = ClusterProposalDuration; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type SeatsConsensus = pallet_ddc_clusters_gov::Unanimous; + type DefaultVote = pallet_ddc_clusters_gov::NayAsDefaultVote; + type MinValidatedNodesCount = MinValidatedNodesCount; + type ReferendumEnactmentDuration = ReferendumEnactmentDuration; + #[cfg(feature = "runtime-benchmarks")] + type NodeCreator = pallet_ddc_nodes::Pallet; + #[cfg(feature = "runtime-benchmarks")] + type StakerCreator = pallet_ddc_staking::Pallet; +} + +pub struct ClustersGovWrapper; +impl PalletVisitor for ClustersGovWrapper { + fn get_account_id() -> T::AccountId { + ClustersGovPalletId::get().into_account_truncating() + } +} + +pub struct DdcOriginAsNative(PhantomData<(DdcOrigin, RuntimeOrigin)>); +impl, T: frame_system::Config> GetDdcOrigin + for DdcOriginAsNative +{ + fn get() -> T::RuntimeOrigin { + DdcOrigin::get() + } +} + +parameter_types! { + pub const VerificationPalletId: PalletId = PalletId(*b"verifypa"); + pub const MajorityOfAggregators: Percent = Percent::from_percent(67); +} +impl pallet_ddc_verification::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = VerificationPalletId; + type WeightInfo = pallet_ddc_verification::weights::SubstrateWeight; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterValidator = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type PayoutVisitor = pallet_ddc_payouts::Pallet; + type AuthorityId = ddc_primitives::sr25519::AuthorityId; + type OffchainIdentifierId = ddc_primitives::crypto::OffchainIdentifierId; + type ActivityHasher = BlakeTwo256; + const MAJORITY: u8 = 67; + const BLOCK_TO_START: u16 = 30; // every 100 blocks + const DAC_REDUNDANCY_FACTOR: u16 = 3; + type AggregatorsQuorum = MajorityOfAggregators; + const MAX_PAYOUT_BATCH_SIZE: u16 = MAX_PAYOUT_BATCH_SIZE; + const MAX_PAYOUT_BATCH_COUNT: u16 = MAX_PAYOUT_BATCH_COUNT; + type ActivityHash = H256; + type StakingVisitor = pallet_staking::Pallet; + type AccountIdConverter = AccountId32; + type CustomerVisitor = pallet_ddc_customers::Pallet; + const MAX_MERKLE_NODE_IDENTIFIER: u16 = 3; } construct_runtime!( @@ -1225,12 +1396,15 @@ construct_runtime!( DdcNodes: pallet_ddc_nodes, DdcClusters: pallet_ddc_clusters, DdcPayouts: pallet_ddc_payouts, + DdcVerification: pallet_ddc_verification, // Start OpenGov. ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event}, Referenda: pallet_referenda::{Pallet, Call, Storage, Event}, - Origins: pallet_custom_origins::{Origin}, + Origins: pallet_origins::{Origin}, Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event}, // End OpenGov. + TechComm: pallet_collective::, + DdcClustersGov: pallet_ddc_clusters_gov, } ); @@ -1269,85 +1443,12 @@ pub type SignedPayload = generic::SignedPayload; pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Runtime migrations -type Migrations = migrations::Unreleased; - -/// The runtime migrations per release. -#[allow(deprecated, missing_docs)] -pub mod migrations { - use frame_support::traits::LockIdentifier; - use frame_system::pallet_prelude::BlockNumberFor; - - use super::*; - - parameter_types! { - pub const DemocracyPalletName: &'static str = "Democracy"; - pub const CouncilPalletName: &'static str = "Council"; - pub const TechnicalCommitteePalletName: &'static str = "TechnicalCommittee"; - pub const ElectionPalletName: &'static str = "Elections"; - pub const TechnicalMembershipPalletName: &'static str = "TechnicalMembership"; - pub const TipsPalletName: &'static str = "Tips"; - pub const ElectionPalletId: LockIdentifier = *b"phrelect"; - } - - // Special Config for Gov V1 pallets, allowing us to run migrations for them without - // implementing their configs on [`Runtime`]. - pub struct UnlockConfig; - impl pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockConfig for UnlockConfig { - type Currency = Balances; - type MaxVotes = ConstU32<100>; - type MaxDeposits = ConstU32<100>; - type AccountId = AccountId; - type BlockNumber = BlockNumberFor; - type DbWeight = ::DbWeight; - type PalletName = DemocracyPalletName; - } - impl pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockConfig - for UnlockConfig - { - type Currency = Balances; - type MaxVotesPerVoter = ConstU32<16>; - type PalletId = ElectionPalletId; - type AccountId = AccountId; - type DbWeight = ::DbWeight; - type PalletName = ElectionPalletName; - } - impl pallet_tips::migrations::unreserve_deposits::UnlockConfig<()> for UnlockConfig { - type Currency = Balances; - type Hash = Hash; - type DataDepositPerByte = DataDepositPerByte; - type TipReportDepositBase = TipReportDepositBase; - type AccountId = AccountId; - type BlockNumber = BlockNumberFor; - type DbWeight = ::DbWeight; - type PalletName = TipsPalletName; - } - - /// Unreleased migrations. Add new ones here: - pub type Unreleased = ( - pallet_ddc_clusters::migration::MigrateToV1, - pallet_contracts::migration::Migration, - pallet_referenda::migration::v1::MigrateV0ToV1, - // Gov v1 storage migrations - // https://github.com/paritytech/polkadot/issues/6749 - pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, - - // Delete all Gov v1 pallet storage key/values. - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - ); -} +type Migrations = ( + pallet_nomination_pools::migration::versioned_migrations::V5toV6, + pallet_nomination_pools::migration::versioned_migrations::V6ToV7, + pallet_staking::migrations::v14::MigrateToV14, + pallet_grandpa::migrations::MigrateV4ToV5, +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -1359,6 +1460,22 @@ pub type Executive = frame_executive::Executive< Migrations, >; +pub mod migrations { + use super::*; + + /// When this is removed, should also remove `OldSessionKeys`. + pub struct UpgradeSessionKeys; + impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys { + fn on_runtime_upgrade() -> Weight { + Session::upgrade_keys::(transform_session_keys); + Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block + } + } + + /// Unreleased migrations. Add new ones here: + pub type Unreleased = (UpgradeSessionKeys,); +} + type EventRecord = frame_system::EventRecord< ::RuntimeEvent, ::Hash, @@ -1386,9 +1503,12 @@ mod benches { [pallet_im_online, ImOnline] [pallet_indices, Indices] [pallet_multisig, Multisig] + [pallet_nomination_pools, NominationPoolsBench::] + [pallet_offences, OffencesBench::] [pallet_proxy, Proxy] [pallet_preimage, Preimage] [pallet_scheduler, Scheduler] + [pallet_session, SessionBench::] [pallet_staking, Staking] [pallet_ddc_customers, DdcCustomers] [pallet_ddc_clusters, DdcClusters] @@ -1403,6 +1523,8 @@ mod benches { [pallet_conviction_voting, ConvictionVoting] [pallet_referenda, Referenda] [pallet_whitelist, Whitelist] + [pallet_collective, TechComm] + [pallet_ddc_clusters_gov, DdcClustersGov] ); } @@ -1730,7 +1852,10 @@ impl_runtime_apis! { // Trying to add benchmarks directly to the Session Pallet caused cyclic dependency // issues. To get around that, we separated the Session benchmarks into its own crate, // which is why we need these two lines below. + use pallet_session_benchmarking::Pallet as SessionBench; + use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; @@ -1751,13 +1876,19 @@ impl_runtime_apis! { // Trying to add benchmarks directly to the Session Pallet caused cyclic dependency // issues. To get around that, we separated the Session benchmarks into its own crate, // which is why we need these two lines below. + use pallet_session_benchmarking::Pallet as SessionBench; + use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; + impl pallet_session_benchmarking::Config for Runtime {} + impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} impl baseline::Config for Runtime {} + impl pallet_nomination_pools_benchmarking::Config for Runtime {} let whitelist: Vec = vec![ // Block Number diff --git a/runtime/cere/Cargo.toml b/runtime/cere/Cargo.toml index e04dfbb5c..cd536a347 100644 --- a/runtime/cere/Cargo.toml +++ b/runtime/cere/Cargo.toml @@ -52,8 +52,10 @@ pallet-insecure-randomness-collective-flip = { workspace = true } pallet-membership = { workspace = true } pallet-multisig = { workspace = true } pallet-nomination-pools = { workspace = true } +pallet-nomination-pools-benchmarking = { workspace = true } pallet-nomination-pools-runtime-api = { workspace = true } pallet-offences = { workspace = true } +pallet-offences-benchmarking = { workspace = true, optional = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-recovery = { workspace = true } @@ -93,12 +95,15 @@ ddc-primitives = { workspace = true } pallet-chainbridge = { workspace = true } pallet-conviction-voting = { workspace = true } pallet-ddc-clusters = { workspace = true } +pallet-ddc-clusters-gov = { workspace = true } pallet-ddc-customers = { workspace = true } pallet-ddc-nodes = { workspace = true } pallet-ddc-payouts = { workspace = true } pallet-ddc-staking = { workspace = true } +pallet-ddc-verification = { workspace = true } pallet-erc20 = { workspace = true } pallet-erc721 = { workspace = true } +pallet-origins = { workspace = true } pallet-referenda = { workspace = true } pallet-whitelist = { workspace = true } @@ -180,11 +185,14 @@ std = [ "pallet-ddc-nodes/std", "pallet-ddc-payouts/std", "pallet-ddc-staking/std", + "pallet-ddc-verification/std", "cere-runtime-common/std", "sp-arithmetic/std", "pallet-conviction-voting/std", "pallet-referenda/std", "pallet-whitelist/std", + "pallet-preimage/std", + "pallet-origins/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -215,6 +223,8 @@ runtime-benchmarks = [ "pallet-membership/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", + "pallet-nomination-pools-benchmarking/runtime-benchmarks", + "pallet-offences-benchmarking/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", @@ -232,6 +242,10 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-referenda/runtime-benchmarks", "pallet-whitelist/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-ddc-clusters-gov/runtime-benchmarks", + "pallet-origins/runtime-benchmarks", + "pallet-ddc-verification/runtime-benchmarks", ] try-runtime = [ "frame-executive/try-runtime", @@ -283,4 +297,8 @@ try-runtime = [ "pallet-conviction-voting/try-runtime", "pallet-referenda/try-runtime", "pallet-whitelist/try-runtime", + "pallet-preimage/try-runtime", + "pallet-origins/try-runtime", + "pallet-ddc-clusters-gov/try-runtime", + "pallet-ddc-verification/try-runtime", ] diff --git a/runtime/cere/src/governance/mod.rs b/runtime/cere/src/governance/mod.rs index 1e2c835e6..47f2a1b7c 100644 --- a/runtime/cere/src/governance/mod.rs +++ b/runtime/cere/src/governance/mod.rs @@ -1,15 +1,22 @@ -use frame_support::parameter_types; +use frame_support::{ + pallet_prelude::EnsureOrigin, + parameter_types, + traits::{EitherOf, EnsureOriginWithArg, OriginTrait}, +}; use frame_system::EnsureRootWithSuccess; +use sp_std::marker::PhantomData; use super::*; -mod origins; -pub use origins::{ - pallet_custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, Spender, - StakingAdmin, Treasurer, WhitelistedCaller, -}; - mod tracks; +use cere_runtime_common::constants::tracks::{ + CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID, CLUSTER_PROTOCOL_UPDATER_TRACK_ID, +}; +use ddc_primitives::traits::pallet::PalletsOriginOf; +pub use pallet_origins::pallet::{ + ClusterProtocolActivator, ClusterProtocolUpdater, GeneralAdmin, ReferendumCanceller, + ReferendumKiller, Spender, StakingAdmin, Treasurer, WhitelistedCaller, +}; pub use tracks::TracksInfo; parameter_types! { @@ -38,14 +45,17 @@ parameter_types! { } pub type TreasurySpender = EitherOf, Spender>; -impl origins::pallet_custom_origins::Config for Runtime {} +impl pallet_origins::Config for Runtime {} impl pallet_whitelist::Config for Runtime { type WeightInfo = pallet_whitelist::weights::SubstrateWeight; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; - type WhitelistOrigin = EnsureRoot; - type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type WhitelistOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureMembers, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; type Preimages = Preimage; } @@ -55,7 +65,7 @@ impl pallet_referenda::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Scheduler = Scheduler; type Currency = Balances; - type SubmitOrigin = frame_system::EnsureSigned; + type SubmitOrigin = EnsureOfPermittedReferendaOrigin; type CancelOrigin = EitherOf, ReferendumCanceller>; type KillOrigin = EitherOf, ReferendumKiller>; type Slash = Treasury; @@ -68,3 +78,48 @@ impl pallet_referenda::Config for Runtime { type Tracks = TracksInfo; type Preimages = Preimage; } + +pub struct EnsureOfPermittedReferendaOrigin(PhantomData); +impl EnsureOriginWithArg> + for EnsureOfPermittedReferendaOrigin +where + ::RuntimeOrigin: OriginTrait, +{ + type Success = T::AccountId; + + fn try_origin( + o: T::RuntimeOrigin, + proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = as EnsureOrigin<_>>::try_origin(o.clone())?; + + let track_id = + match >::track_for( + proposal_origin, + ) { + Ok(track_id) => track_id, + Err(_) => return Err(o), + }; + + if track_id == CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID || + track_id == CLUSTER_PROTOCOL_UPDATER_TRACK_ID + { + let clusters_governance = >::get_account_id(); + if origin == clusters_governance { + Ok(origin) + } else { + Err(o) + } + } else { + Ok(origin) + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin( + _proposal_origin: &PalletsOriginOf, + ) -> Result { + let origin = frame_benchmarking::account::("successful_origin", 0, 0); + Ok(frame_system::RawOrigin::Signed(origin).into()) + } +} diff --git a/runtime/cere/src/governance/origins.rs b/runtime/cere/src/governance/origins.rs deleted file mode 100644 index dbbfebe20..000000000 --- a/runtime/cere/src/governance/origins.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Custom origins for governance interventions. -pub use pallet_custom_origins::*; - -#[frame_support::pallet] -pub mod pallet_custom_origins { - use frame_support::pallet_prelude::*; - - use crate::{Balance, DOLLARS, GRAND}; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug)] - #[pallet::origin] - pub enum Origin { - /// Origin able to cancel slashes and manage minimum commission. - StakingAdmin, - /// Origin for spending up to $10,000,000 DOT from the treasury as well as generally - /// administering it. - Treasurer, - /// Origin for managing the composition of the fellowship. - FellowshipAdmin, - /// Origin for managing the registrar. - GeneralAdmin, - /// Origin able to cancel referenda. - ReferendumCanceller, - /// Origin able to kill referenda. - ReferendumKiller, - /// Origin able to spend around $250 from the treasury at once. - SmallTipper, - /// Origin able to spend around $1,000 from the treasury at once. - BigTipper, - /// Origin able to spend around $10,000 from the treasury at once. - SmallSpender, - /// Origin able to spend around $100,000 from the treasury at once. - MediumSpender, - /// Origin able to spend up to $1,000,000 DOT from the treasury at once. - BigSpender, - /// Origin able to dispatch a whitelisted call. - WhitelistedCaller, - } - - macro_rules! decl_unit_ensures { - ( $name:ident: $success_type:ty = $success:expr ) => { - pub struct $name; - impl> + From> - EnsureOrigin for $name - { - type Success = $success_type; - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - Origin::$name => Ok($success), - r => Err(O::from(r)), - }) - } - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(O::from(Origin::$name)) - } - } - }; - ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; - ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { - decl_unit_ensures! { $name: $success_type = $success } - decl_unit_ensures! { $( $rest )* } - }; - ( $name:ident, $( $rest:tt )* ) => { - decl_unit_ensures! { $name } - decl_unit_ensures! { $( $rest )* } - }; - () => {} - } - decl_unit_ensures!( - StakingAdmin, - Treasurer, - FellowshipAdmin, - GeneralAdmin, - ReferendumCanceller, - ReferendumKiller, - WhitelistedCaller, - ); - - macro_rules! decl_ensure { - ( - $vis:vis type $name:ident: EnsureOrigin { - $( $item:ident = $success:expr, )* - } - ) => { - $vis struct $name; - impl> + From> - EnsureOrigin for $name - { - type Success = $success_type; - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - $( - Origin::$item => Ok($success), - )* - r => Err(O::from(r)), - }) - } - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - // By convention the more privileged origins go later, so for greatest chance - // of success, we want the last one. - let _result: Result = Err(()); - $( - let _result: Result = Ok(O::from(Origin::$item)); - )* - _result - } - } - } - } - - decl_ensure! { - pub type Spender: EnsureOrigin { - SmallTipper = 250 * DOLLARS, - BigTipper = GRAND, - SmallSpender = 10 * GRAND, - MediumSpender = 100 * GRAND, - BigSpender = 1_000 * GRAND, - Treasurer = 10_000 * GRAND, - } - } -} diff --git a/runtime/cere/src/governance/tracks.rs b/runtime/cere/src/governance/tracks.rs index da3b0dce1..8e0a3a10c 100644 --- a/runtime/cere/src/governance/tracks.rs +++ b/runtime/cere/src/governance/tracks.rs @@ -1,67 +1,13 @@ -// Copyright 2023 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - //! Track configurations for governance. -use super::*; +use cere_runtime_common::constants::tracks::*; -const fn percent(x: i32) -> sp_arithmetic::FixedI64 { - sp_arithmetic::FixedI64::from_rational(x as u128, 100) -} -use pallet_referenda::Curve; -const APP_ROOT: Curve = Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_ROOT: Curve = Curve::make_linear(28, 28, percent(0), percent(50)); -const APP_STAKING_ADMIN: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_STAKING_ADMIN: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_TREASURER: Curve = Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_TREASURER: Curve = Curve::make_linear(28, 28, percent(0), percent(50)); -const APP_FELLOWSHIP_ADMIN: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_FELLOWSHIP_ADMIN: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_GENERAL_ADMIN: Curve = - Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); -const SUP_GENERAL_ADMIN: Curve = - Curve::make_reciprocal(7, 28, percent(10), percent(0), percent(50)); -const APP_REFERENDUM_CANCELLER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_REFERENDUM_CANCELLER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_REFERENDUM_KILLER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_REFERENDUM_KILLER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_SMALL_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); -const SUP_SMALL_TIPPER: Curve = Curve::make_reciprocal(1, 28, percent(4), percent(0), percent(50)); -const APP_BIG_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); -const SUP_BIG_TIPPER: Curve = Curve::make_reciprocal(8, 28, percent(1), percent(0), percent(50)); -const APP_SMALL_SPENDER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); -const SUP_SMALL_SPENDER: Curve = - Curve::make_reciprocal(12, 28, percent(1), percent(0), percent(50)); -const APP_MEDIUM_SPENDER: Curve = Curve::make_linear(23, 28, percent(50), percent(100)); -const SUP_MEDIUM_SPENDER: Curve = - Curve::make_reciprocal(16, 28, percent(1), percent(0), percent(50)); -const APP_BIG_SPENDER: Curve = Curve::make_linear(28, 28, percent(50), percent(100)); -const SUP_BIG_SPENDER: Curve = Curve::make_reciprocal(20, 28, percent(1), percent(0), percent(50)); -const APP_WHITELISTED_CALLER: Curve = - Curve::make_reciprocal(16, 28 * 24, percent(96), percent(50), percent(100)); -const SUP_WHITELISTED_CALLER: Curve = - Curve::make_reciprocal(1, 28, percent(20), percent(5), percent(50)); +use super::*; +use crate::{Balance, BlockNumber}; -const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13] = [ +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 14] = [ ( - 0, + ROOT_TRACK_ID, pallet_referenda::TrackInfo { name: "root", max_deciding: 1, @@ -75,7 +21,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 1, + WHITELISTED_CALLER_TRACK_ID, pallet_referenda::TrackInfo { name: "whitelisted_caller", max_deciding: 100, @@ -89,11 +35,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 10, + STAKING_ADMIN_TRACK_ID, pallet_referenda::TrackInfo { name: "staking_admin", max_deciding: 10, - decision_deposit: 5 * GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -103,11 +49,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 11, + TREASURER_TRACK_ID, pallet_referenda::TrackInfo { name: "treasurer", max_deciding: 10, - decision_deposit: GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -117,25 +63,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 13, - pallet_referenda::TrackInfo { - name: "fellowship_admin", - max_deciding: 10, - decision_deposit: 5 * GRAND, - prepare_period: 2 * HOURS, - decision_period: 28 * DAYS, - confirm_period: 3 * HOURS, - min_enactment_period: 10 * MINUTES, - min_approval: APP_FELLOWSHIP_ADMIN, - min_support: SUP_FELLOWSHIP_ADMIN, - }, - ), - ( - 14, + GENERAL_ADMIN_TRACK_ID, pallet_referenda::TrackInfo { name: "general_admin", max_deciding: 10, - decision_deposit: 5 * GRAND, + decision_deposit: 10 * GRAND, prepare_period: 2 * HOURS, decision_period: 28 * DAYS, confirm_period: 3 * HOURS, @@ -145,7 +77,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 20, + REFERENDUM_CANCELER_TRACK_ID, pallet_referenda::TrackInfo { name: "referendum_canceller", max_deciding: 1_000, @@ -159,7 +91,7 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 21, + REFERENDUM_KILLER_TRACK_ID, pallet_referenda::TrackInfo { name: "referendum_killer", max_deciding: 1_000, @@ -173,11 +105,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 30, + SMALL_TIPPER_TRACK_ID, pallet_referenda::TrackInfo { name: "small_tipper", max_deciding: 200, - decision_deposit: DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: MINUTES, decision_period: 7 * DAYS, confirm_period: 10 * MINUTES, @@ -187,11 +119,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 31, + BIG_TIPPER_TRACK_ID, pallet_referenda::TrackInfo { name: "big_tipper", max_deciding: 100, - decision_deposit: 10 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 10 * MINUTES, decision_period: 7 * DAYS, confirm_period: HOURS, @@ -201,11 +133,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 32, + SMALL_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "small_spender", max_deciding: 50, - decision_deposit: 100 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 12 * HOURS, @@ -215,11 +147,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 33, + MEDIUM_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "medium_spender", max_deciding: 50, - decision_deposit: 200 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 24 * HOURS, @@ -229,11 +161,11 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 }, ), ( - 34, + BIG_SPENDER_TRACK_ID, pallet_referenda::TrackInfo { name: "big_spender", max_deciding: 50, - decision_deposit: 400 * DOLLARS, + decision_deposit: 10 * GRAND, prepare_period: 4 * HOURS, decision_period: 28 * DAYS, confirm_period: 48 * HOURS, @@ -242,6 +174,34 @@ const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 13 min_support: SUP_BIG_SPENDER, }, ), + ( + CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_activator", + max_deciding: 50, + decision_deposit: 10 * GRAND, + prepare_period: 30 * MINUTES, + decision_period: 28 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 10 * MINUTES, + min_approval: APP_CLUSTER_PROTOCOL_ACTIVATOR, + min_support: SUP_CLUSTER_PROTOCOL_ACTIVATOR, + }, + ), + ( + CLUSTER_PROTOCOL_UPDATER_TRACK_ID, + pallet_referenda::TrackInfo { + name: "cluster_protocol_updater", + max_deciding: 50, + decision_deposit: 10 * GRAND, + prepare_period: 30 * MINUTES, + decision_period: 28 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 10 * MINUTES, + min_approval: APP_CLUSTER_PROTOCOL_UPDATER, + min_support: SUP_CLUSTER_PROTOCOL_UPDATER, + }, + ), ]; pub struct TracksInfo; @@ -254,30 +214,37 @@ impl pallet_referenda::TracksInfo for TracksInfo { fn track_for(id: &Self::RuntimeOrigin) -> Result { if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { match system_origin { - frame_system::RawOrigin::Root => Ok(0), + frame_system::RawOrigin::Root => Ok(ROOT_TRACK_ID), _ => Err(()), } - } else if let Ok(custom_origin) = origins::Origin::try_from(id.clone()) { + } else if let Ok(custom_origin) = pallet_origins::pallet::Origin::try_from(id.clone()) { match custom_origin { - origins::Origin::WhitelistedCaller => Ok(1), + pallet_origins::pallet::Origin::WhitelistedCaller => + Ok(WHITELISTED_CALLER_TRACK_ID), // General admin - origins::Origin::StakingAdmin => Ok(10), - origins::Origin::Treasurer => Ok(11), - origins::Origin::FellowshipAdmin => Ok(13), - origins::Origin::GeneralAdmin => Ok(14), + pallet_origins::pallet::Origin::StakingAdmin => Ok(STAKING_ADMIN_TRACK_ID), + pallet_origins::pallet::Origin::Treasurer => Ok(TREASURER_TRACK_ID), + pallet_origins::pallet::Origin::GeneralAdmin => Ok(GENERAL_ADMIN_TRACK_ID), // Referendum admins - origins::Origin::ReferendumCanceller => Ok(20), - origins::Origin::ReferendumKiller => Ok(21), + pallet_origins::pallet::Origin::ReferendumCanceller => + Ok(REFERENDUM_CANCELER_TRACK_ID), + pallet_origins::pallet::Origin::ReferendumKiller => Ok(REFERENDUM_KILLER_TRACK_ID), // Limited treasury spenders - origins::Origin::SmallTipper => Ok(30), - origins::Origin::BigTipper => Ok(31), - origins::Origin::SmallSpender => Ok(32), - origins::Origin::MediumSpender => Ok(33), - origins::Origin::BigSpender => Ok(34), + pallet_origins::pallet::Origin::SmallTipper => Ok(SMALL_TIPPER_TRACK_ID), + pallet_origins::pallet::Origin::BigTipper => Ok(BIG_TIPPER_TRACK_ID), + pallet_origins::pallet::Origin::SmallSpender => Ok(SMALL_SPENDER_TRACK_ID), + pallet_origins::pallet::Origin::MediumSpender => Ok(MEDIUM_SPENDER_TRACK_ID), + pallet_origins::pallet::Origin::BigSpender => Ok(BIG_SPENDER_TRACK_ID), + // DDC admins + pallet_origins::pallet::Origin::ClusterProtocolActivator => + Ok(CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID), + pallet_origins::pallet::Origin::ClusterProtocolUpdater => + Ok(CLUSTER_PROTOCOL_UPDATER_TRACK_ID), } } else { Err(()) } } } + pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); diff --git a/runtime/cere/src/lib.rs b/runtime/cere/src/lib.rs index d4e6450d6..11b92e51c 100644 --- a/runtime/cere/src/lib.rs +++ b/runtime/cere/src/lib.rs @@ -22,7 +22,10 @@ // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit = "256"] use codec::{Decode, Encode, MaxEncodedLen}; -use ddc_primitives::traits::pallet::PalletVisitor; +use ddc_primitives::{ + traits::pallet::{GetDdcOrigin, PalletVisitor}, + MAX_PAYOUT_BATCH_COUNT, MAX_PAYOUT_BATCH_SIZE, +}; use frame_election_provider_support::{ bounds::ElectionBoundsBuilder, onchain, BalancingConfig, SequentialPhragmen, VoteWeight, }; @@ -32,9 +35,11 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ + fungible::HoldConsideration, + tokens::{PayFromAccount, UnityAssetBalanceConversion}, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOf, EitherOfDiverse, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, Nothing, - OnUnbalanced, WithdrawReasons, + EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, + LinearStoragePrice, Nothing, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ @@ -70,7 +75,10 @@ pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdj use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{ + crypto::{AccountId32, KeyTypeId}, + OpaqueMetadata, H256, +}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_io::hashing::blake2_128; #[cfg(any(feature = "std", test))] @@ -80,8 +88,9 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, - Identity as IdentityConvert, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup, + self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, Convert, ConvertInto, + Identity as IdentityConvert, IdentityLookup, NumberFor, OpaqueKeys, SaturatedConversion, + StaticLookup, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perbill, Percent, Permill, Perquintill, @@ -101,12 +110,16 @@ use cere_runtime_common::{ CurrencyToVote, }; use impls::Author; +use pallet_identity::legacy::IdentityInfo; use sp_runtime::generic::Era; +use sp_std::marker::PhantomData; // Governance configurations. pub mod governance; -use governance::{pallet_custom_origins, GeneralAdmin, StakingAdmin, Treasurer, TreasurySpender}; - +use governance::{ + ClusterProtocolActivator, ClusterProtocolUpdater, GeneralAdmin, StakingAdmin, Treasurer, + TreasurySpender, +}; /// Generated voter bag information. mod voter_bags; @@ -134,10 +147,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 53003, + spec_version: 61000, impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 17, + transaction_version: 19, state_version: 0, }; @@ -351,6 +364,7 @@ parameter_types! { pub const PreimageMaxSize: u32 = 4096 * 1024; pub const PreimageBaseDeposit: Balance = deposit(2, 64); pub const PreimageByteDeposit: Balance = deposit(0, 1); + pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); } impl pallet_preimage::Config for Runtime { @@ -358,8 +372,12 @@ impl pallet_preimage::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureRoot; - type BaseDeposit = PreimageBaseDeposit; - type ByteDeposit = PreimageByteDeposit; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; } parameter_types! { @@ -373,7 +391,7 @@ impl pallet_scheduler::Config for Runtime { type PalletsOrigin = OriginCaller; type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; - type ScheduleOrigin = EnsureRoot; + type ScheduleOrigin = EitherOf, Treasurer>; type MaxScheduledPerBlock = ConstU32<512>; type WeightInfo = pallet_scheduler::weights::SubstrateWeight; type OriginPrivilegeCmp = EqualPrivilegeOnly; @@ -437,8 +455,9 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = pallet_balances::weights::SubstrateWeight; - type FreezeIdentifier = (); - type MaxFreezes = (); + type FreezeIdentifier = RuntimeFreezeReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; type MaxHolds = MaxHolds; } @@ -483,12 +502,39 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (Staking, ImOnline); } +impl_opaque_keys! { + pub struct OldSessionKeys { + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, + pub authority_discovery: AuthorityDiscovery, + } +} + impl_opaque_keys! { pub struct SessionKeys { pub grandpa: Grandpa, pub babe: Babe, pub im_online: ImOnline, pub authority_discovery: AuthorityDiscovery, + pub ddc_verification: DdcVerification, + } +} + +fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys { + SessionKeys { + grandpa: old.grandpa, + babe: old.babe, + im_online: old.im_online, + authority_discovery: old.authority_discovery, + ddc_verification: { + let mut id: ddc_primitives::sr25519::AuthorityId = + sp_core::sr25519::Public::from_raw([0u8; 32]).into(); + let id_raw: &mut [u8] = id.as_mut(); + id_raw[0..32].copy_from_slice(v.as_ref()); + id_raw[0..4].copy_from_slice(b"cer!"); + id + }, } } @@ -525,6 +571,7 @@ parameter_types! { pub const BondingDuration: sp_staking::EraIndex = 3; pub const SlashDeferDuration: sp_staking::EraIndex = 2; pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; + pub const MaxExposurePageSize: u32 = 512; pub const MaxNominatorRewardedPerValidator: u32 = 512; pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); pub OffchainRepeat: BlockNumber = 5; @@ -554,8 +601,8 @@ impl pallet_staking::Config for Runtime { type AdminOrigin = EitherOf, StakingAdmin>; type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; + type MaxExposurePageSize = MaxExposurePageSize; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = ElectionProviderMultiPhase; type GenesisElectionProvider = onchain::OnChainExecution; @@ -577,8 +624,6 @@ impl pallet_fast_unstake::Config for Runtime { type BatchSize = frame_support::traits::ConstU32<64>; type Staking = Staking; type MaxErasToCheckPerBlock = ConstU32<1>; - #[cfg(feature = "runtime-benchmarks")] - type MaxBackersPerValidator = MaxNominatorRewardedPerValidator; type WeightInfo = (); } @@ -589,7 +634,6 @@ parameter_types! { // signed config pub const SignedRewardBase: Balance = DOLLARS; - pub const SignedDepositBase: Balance = DOLLARS; pub const SignedDepositByte: Balance = CENTS; pub BetterUnsignedThreshold: Perbill = Perbill::from_rational(1u32, 10_000); @@ -705,6 +749,15 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime { } } +/// Returning a fixed value to respect the initial logic. +/// This could depend on the length of the solution. +pub struct FixedSignedDepositBase; +impl Convert for FixedSignedDepositBase { + fn convert(_: usize) -> u128 { + DOLLARS + } +} + impl pallet_election_provider_multi_phase::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -720,7 +773,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type MinerConfig = Self; type SignedMaxSubmissions = ConstU32<10>; type SignedRewardBase = SignedRewardBase; - type SignedDepositBase = SignedDepositBase; + type SignedDepositBase = FixedSignedDepositBase; type SignedDepositByte = SignedDepositByte; type SignedMaxRefunds = ConstU32<3>; type SignedDepositWeight = (); @@ -769,6 +822,10 @@ parameter_types! { pub const MaxApprovals: u32 = 100; } +parameter_types! { + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + impl pallet_treasury::Config for Runtime { type PalletId = TreasuryPalletId; type Currency = Balances; @@ -786,6 +843,14 @@ impl pallet_treasury::Config for Runtime { type WeightInfo = pallet_treasury::weights::SubstrateWeight; type MaxApprovals = MaxApprovals; type SpendOrigin = TreasurySpender; + type AssetKind = (); + type Beneficiary = Self::AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<10>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { @@ -873,11 +938,8 @@ impl pallet_contracts::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); - type Migrations = ( - pallet_contracts::migration::v13::Migration, - pallet_contracts::migration::v14::Migration, - pallet_contracts::migration::v15::Migration, - ); + type Migrations = (); + type Xcm = (); } impl pallet_sudo::Config for Runtime { @@ -992,7 +1054,7 @@ impl pallet_grandpa::Config for Runtime { parameter_types! { pub const BasicDeposit: Balance = 10 * DOLLARS; // 258 bytes on-chain - pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain + pub const ByteDeposit: Balance = deposit(0, 1); pub const SubAccountDeposit: Balance = 2 * DOLLARS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -1003,10 +1065,10 @@ impl pallet_identity::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; + type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; type ForceOrigin = EitherOf, GeneralAdmin>; @@ -1100,6 +1162,7 @@ parameter_types! { impl pallet_nomination_pools::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type RuntimeFreezeReason = RuntimeFreezeReason; type RewardCounter = FixedU128; type BalanceToU256 = cere_runtime_common::BalanceToU256; type U256ToBalance = cere_runtime_common::U256ToBalance; @@ -1123,7 +1186,7 @@ impl pallet_ddc_customers::Config for Runtime { type Currency = Balances; type PalletId = DdcCustomersPalletId; type RuntimeEvent = RuntimeEvent; - type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; type ClusterCreator = pallet_ddc_clusters::Pallet; type WeightInfo = pallet_ddc_customers::weights::SubstrateWeight; } @@ -1162,24 +1225,134 @@ impl pallet_ddc_payouts::Config for Runtime { type PalletId = PayoutsPalletId; type Currency = Balances; type CustomerCharger = DdcCustomers; + type BucketVisitor = DdcCustomers; type CustomerDepositor = DdcCustomers; - type ClusterVisitor = DdcClusters; + type ClusterProtocol = DdcClusters; type TreasuryVisitor = TreasuryWrapper; type NominatorsAndValidatorsList = pallet_staking::UseNominatorsAndValidatorsMap; type ClusterCreator = DdcClusters; type WeightInfo = pallet_ddc_payouts::weights::SubstrateWeight; - type VoteScoreToU64 = IdentityConvert; // used for UseNominatorsAndValidatorsMap + type VoteScoreToU64 = IdentityConvert; + type ValidatorVisitor = pallet_ddc_verification::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type AccountIdConverter = AccountId32; +} + +parameter_types! { + pub const ClusterBondingAmount: Balance = 100 * GRAND; + pub const ClusterUnboningDelay: BlockNumber = 28 * DAYS; } impl pallet_ddc_staking::Config for Runtime { type Currency = Balances; type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_ddc_staking::weights::SubstrateWeight; - type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; type ClusterCreator = pallet_ddc_clusters::Pallet; type ClusterManager = pallet_ddc_clusters::Pallet; type NodeVisitor = pallet_ddc_nodes::Pallet; type NodeCreator = pallet_ddc_nodes::Pallet; + type ClusterBondingAmount = ClusterBondingAmount; + type ClusterUnboningDelay = ClusterUnboningDelay; +} + +parameter_types! { + pub const TechnicalMotionDuration: BlockNumber = 5 * DAYS; + pub const TechnicalMaxProposals: u32 = 100; + pub const TechnicalMaxMembers: u32 = 100; +} + +type TechCommCollective = pallet_collective::Instance3; +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalMotionDuration; + type MaxProposals = TechnicalMaxProposals; + type MaxMembers = TechnicalMaxMembers; + type SetMembersOrigin = EnsureRoot; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = pallet_collective::weights::SubstrateWeight; + type MaxProposalWeight = MaxCollectivesProposalWeight; +} + +parameter_types! { + pub const ClustersGovPalletId: PalletId = PalletId(*b"clustgov"); + pub const ClusterProposalDuration: BlockNumber = 7 * DAYS; + pub const MinValidatedNodesCount: u16 = 3; + pub ClusterProtocolActivatorTrackOrigin: RuntimeOrigin = pallet_origins::Origin::ClusterProtocolActivator.into(); + pub ClusterProtocolUpdaterTrackOrigin: RuntimeOrigin = pallet_origins::Origin::ClusterProtocolUpdater.into(); + pub const ReferendumEnactmentDuration: BlockNumber = 1; +} + +impl pallet_ddc_clusters_gov::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = ClustersGovPalletId; + type Currency = Balances; + type WeightInfo = pallet_ddc_clusters_gov::weights::SubstrateWeight; + type OpenGovActivatorTrackOrigin = DdcOriginAsNative; + type OpenGovActivatorOrigin = EitherOf, ClusterProtocolActivator>; + type OpenGovUpdaterTrackOrigin = DdcOriginAsNative; + type OpenGovUpdaterOrigin = EitherOf, ClusterProtocolUpdater>; + type ClusterProposalCall = RuntimeCall; + type ClusterProposalDuration = ClusterProposalDuration; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type ClusterProtocol = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type SeatsConsensus = pallet_ddc_clusters_gov::Unanimous; + type DefaultVote = pallet_ddc_clusters_gov::NayAsDefaultVote; + type MinValidatedNodesCount = MinValidatedNodesCount; + type ReferendumEnactmentDuration = ReferendumEnactmentDuration; + #[cfg(feature = "runtime-benchmarks")] + type NodeCreator = pallet_ddc_nodes::Pallet; + #[cfg(feature = "runtime-benchmarks")] + type StakerCreator = pallet_ddc_staking::Pallet; +} + +pub struct ClustersGovWrapper; +impl PalletVisitor for ClustersGovWrapper { + fn get_account_id() -> T::AccountId { + ClustersGovPalletId::get().into_account_truncating() + } +} + +pub struct DdcOriginAsNative(PhantomData<(DdcOrigin, RuntimeOrigin)>); +impl, T: frame_system::Config> GetDdcOrigin + for DdcOriginAsNative +{ + fn get() -> T::RuntimeOrigin { + DdcOrigin::get() + } +} + +parameter_types! { + pub const VerificationPalletId: PalletId = PalletId(*b"verifypa"); + pub const MajorityOfAggregators: Percent = Percent::from_percent(67); +} + +impl pallet_ddc_verification::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = VerificationPalletId; + type WeightInfo = pallet_ddc_verification::weights::SubstrateWeight; + type ClusterManager = pallet_ddc_clusters::Pallet; + type ClusterValidator = pallet_ddc_clusters::Pallet; + type NodeVisitor = pallet_ddc_nodes::Pallet; + type PayoutVisitor = pallet_ddc_payouts::Pallet; + type AuthorityId = ddc_primitives::sr25519::AuthorityId; + type OffchainIdentifierId = ddc_primitives::crypto::OffchainIdentifierId; + type ActivityHasher = BlakeTwo256; + const MAJORITY: u8 = 67; + const BLOCK_TO_START: u16 = 100; // every 100 blocks + const DAC_REDUNDANCY_FACTOR: u16 = 3; + type AggregatorsQuorum = MajorityOfAggregators; + const MAX_PAYOUT_BATCH_SIZE: u16 = MAX_PAYOUT_BATCH_SIZE; + const MAX_PAYOUT_BATCH_COUNT: u16 = MAX_PAYOUT_BATCH_COUNT; + type ActivityHash = H256; + type StakingVisitor = pallet_staking::Pallet; + type AccountIdConverter = AccountId32; + type CustomerVisitor = pallet_ddc_customers::Pallet; + const MAX_MERKLE_NODE_IDENTIFIER: u16 = 3; } construct_runtime!( @@ -1230,9 +1403,12 @@ construct_runtime!( // Start OpenGov. ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event}, Referenda: pallet_referenda::{Pallet, Call, Storage, Event}, - Origins: pallet_custom_origins::{Origin}, + Origins: pallet_origins::{Origin}, Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event}, // End OpenGov. + TechComm: pallet_collective::, + DdcClustersGov: pallet_ddc_clusters_gov, + DdcVerification: pallet_ddc_verification, } ); @@ -1271,86 +1447,34 @@ pub type SignedPayload = generic::SignedPayload; pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Runtime migrations -type Migrations = migrations::Unreleased; +type Migrations = ( + pallet_nomination_pools::migration::versioned_migrations::V5toV6, + pallet_nomination_pools::migration::versioned_migrations::V6ToV7, + pallet_staking::migrations::v14::MigrateToV14, + pallet_grandpa::migrations::MigrateV4ToV5, + migrations::Unreleased, +); -/// The runtime migrations per release. -#[allow(deprecated, missing_docs)] pub mod migrations { - use frame_support::traits::LockIdentifier; - use frame_system::pallet_prelude::BlockNumberFor; - use super::*; - parameter_types! { - pub const DemocracyPalletName: &'static str = "Democracy"; - pub const CouncilPalletName: &'static str = "Council"; - pub const TechnicalCommitteePalletName: &'static str = "TechnicalCommittee"; - pub const ElectionPalletName: &'static str = "Elections"; - pub const TechnicalMembershipPalletName: &'static str = "TechnicalMembership"; - pub const TipsPalletName: &'static str = "Tips"; - pub const ElectionPalletId: LockIdentifier = *b"phrelect"; - } - - // Special Config for Gov V1 pallets, allowing us to run migrations for them without - // implementing their configs on [`Runtime`]. - pub struct UnlockConfig; - impl pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockConfig for UnlockConfig { - type Currency = Balances; - type MaxVotes = ConstU32<100>; - type MaxDeposits = ConstU32<100>; - type AccountId = AccountId; - type BlockNumber = BlockNumberFor; - type DbWeight = ::DbWeight; - type PalletName = DemocracyPalletName; - } - impl pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockConfig - for UnlockConfig - { - type Currency = Balances; - type MaxVotesPerVoter = ConstU32<16>; - type PalletId = ElectionPalletId; - type AccountId = AccountId; - type DbWeight = ::DbWeight; - type PalletName = ElectionPalletName; - } - impl pallet_tips::migrations::unreserve_deposits::UnlockConfig<()> for UnlockConfig { - type Currency = Balances; - type Hash = Hash; - type DataDepositPerByte = DataDepositPerByte; - type TipReportDepositBase = TipReportDepositBase; - type AccountId = AccountId; - type BlockNumber = BlockNumberFor; - type DbWeight = ::DbWeight; - type PalletName = TipsPalletName; + /// When this is removed, should also remove `OldSessionKeys`. + pub struct UpgradeSessionKeys; + impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys { + fn on_runtime_upgrade() -> Weight { + Session::upgrade_keys::(transform_session_keys); + Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block + } } /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - pallet_ddc_clusters::migration::MigrateToV1, - pallet_contracts::migration::Migration, - pallet_referenda::migration::v1::MigrateV0ToV1, - // Gov v1 storage migrations - // https://github.com/paritytech/polkadot/issues/6749 - pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, - - // Delete all Gov v1 pallet storage key/values. - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, + pallet_ddc_customers::migration::v2::MigrateToV2, + pallet_ddc_clusters::migrations::v3::MigrateToV3, + pallet_ddc_nodes::migrations::v1::MigrateToV1, + UpgradeSessionKeys, ); } - /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -1393,9 +1517,12 @@ mod benches { [pallet_im_online, ImOnline] [pallet_indices, Indices] [pallet_multisig, Multisig] + [pallet_nomination_pools, NominationPoolsBench::] + [pallet_offences, OffencesBench::] [pallet_proxy, Proxy] [pallet_preimage, Preimage] [pallet_scheduler, Scheduler] + [pallet_session, SessionBench::] [pallet_staking, Staking] [frame_system, SystemBench::] [pallet_timestamp, Timestamp] @@ -1405,6 +1532,8 @@ mod benches { [pallet_conviction_voting, ConvictionVoting] [pallet_referenda, Referenda] [pallet_whitelist, Whitelist] + [pallet_collective, TechComm] + [pallet_ddc_clusters_gov, DdcClustersGov] ); } @@ -1731,7 +1860,10 @@ impl_runtime_apis! { // Trying to add benchmarks directly to the Session Pallet caused cyclic dependency // issues. To get around that, we separated the Session benchmarks into its own crate, // which is why we need these two lines below. + use pallet_session_benchmarking::Pallet as SessionBench; + use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; @@ -1752,13 +1884,19 @@ impl_runtime_apis! { // Trying to add benchmarks directly to the Session Pallet caused cyclic dependency // issues. To get around that, we separated the Session benchmarks into its own crate, // which is why we need these two lines below. + use pallet_session_benchmarking::Pallet as SessionBench; + use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; + use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; + impl pallet_session_benchmarking::Config for Runtime {} + impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} impl baseline::Config for Runtime {} + impl pallet_nomination_pools_benchmarking::Config for Runtime {} let whitelist: Vec = vec![ // Block Number diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index 9297f7109..560f80150 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -16,8 +16,10 @@ frame-support = { workspace = true } frame-system = { workspace = true } node-primitives = { workspace = true } pallet-contracts = { workspace = true } +pallet-referenda = { workspace = true } pallet-session = { workspace = true } sp-api = { workspace = true } +sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } diff --git a/runtime/common/src/constants.rs b/runtime/common/src/constants.rs index 78831f532..b9792a82a 100644 --- a/runtime/common/src/constants.rs +++ b/runtime/common/src/constants.rs @@ -19,7 +19,7 @@ /// Money matters. pub mod currency { - use node_primitives::Balance; + pub use node_primitives::Balance; pub const MILLICENTS: Balance = 100_000; pub const CENTS: Balance = 1_000 * MILLICENTS; // assume this is worth about a cent. @@ -33,7 +33,7 @@ pub mod currency { /// Time. pub mod time { - use node_primitives::{BlockNumber, Moment}; + pub use node_primitives::{BlockNumber, Moment}; /// Since BABE is probabilistic this is the average expected block time that /// we are targeting. Blocks will be produced at a minimum duration defined @@ -74,3 +74,98 @@ pub mod time { pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; } + +pub mod tracks { + pub const fn percent(x: i32) -> sp_arithmetic::FixedI64 { + sp_arithmetic::FixedI64::from_rational(x as u128, 100) + } + pub const fn percent_perbill(x: Perbill) -> sp_arithmetic::FixedI64 { + sp_arithmetic::FixedI64::from_perbill(x) + } + pub use pallet_referenda::Curve; + use sp_runtime::Perbill; + + pub const APP_ROOT: Curve = + Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); + pub const SUP_ROOT: Curve = Curve::make_linear(28, 28, percent(20), percent(50)); + pub const APP_STAKING_ADMIN: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); + pub const SUP_STAKING_ADMIN: Curve = + Curve::make_reciprocal(12, 28, percent(11), percent(10), percent(50)); + pub const APP_TREASURER: Curve = + Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); + pub const SUP_TREASURER: Curve = Curve::make_linear(28, 28, percent(10), percent(50)); + pub const APP_GENERAL_ADMIN: Curve = + Curve::make_reciprocal(4, 28, percent(80), percent(50), percent(100)); + pub const SUP_GENERAL_ADMIN: Curve = + Curve::make_reciprocal(7, 28, percent(20), percent(10), percent(50)); + pub const APP_REFERENDUM_CANCELLER: Curve = + Curve::make_linear(17, 28, percent(50), percent(100)); + pub const SUP_REFERENDUM_CANCELLER: Curve = + Curve::make_reciprocal(12, 28, percent(11), percent(10), percent(50)); + pub const APP_REFERENDUM_KILLER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); + pub const SUP_REFERENDUM_KILLER: Curve = + Curve::make_reciprocal(12, 28, percent(11), percent(10), percent(50)); + pub const APP_SMALL_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); + pub const SUP_SMALL_TIPPER: Curve = + Curve::make_reciprocal(1, 28, percent(14), percent(10), percent(50)); + pub const APP_BIG_TIPPER: Curve = Curve::make_linear(10, 28, percent(50), percent(100)); + pub const SUP_BIG_TIPPER: Curve = + Curve::make_reciprocal(8, 28, percent(11), percent(10), percent(50)); + pub const APP_SMALL_SPENDER: Curve = Curve::make_linear(17, 28, percent(50), percent(100)); + pub const SUP_SMALL_SPENDER: Curve = + Curve::make_reciprocal(12, 28, percent(11), percent(10), percent(50)); + pub const APP_MEDIUM_SPENDER: Curve = Curve::make_linear(23, 28, percent(50), percent(100)); + pub const SUP_MEDIUM_SPENDER: Curve = + Curve::make_reciprocal(16, 28, percent(11), percent(10), percent(50)); + pub const APP_BIG_SPENDER: Curve = Curve::make_linear(28, 28, percent(50), percent(100)); + pub const SUP_BIG_SPENDER: Curve = + Curve::make_reciprocal(20, 28, percent(11), percent(10), percent(50)); + pub const APP_WHITELISTED_CALLER: Curve = + Curve::make_reciprocal(16, 28 * 24, percent(96), percent(50), percent(100)); + pub const SUP_WHITELISTED_CALLER: Curve = Curve::make_reciprocal( + 1, + 28, + percent_perbill(Perbill::from_parts(1_000_000)), // 0.1 % + percent_perbill(Perbill::from_parts(250_000)), // 0.025 % + percent(50), + ); + pub const APP_CLUSTER_PROTOCOL_ACTIVATOR: Curve = + Curve::make_reciprocal(16, 28 * 24, percent(96), percent(50), percent(100)); + pub const SUP_CLUSTER_PROTOCOL_ACTIVATOR: Curve = Curve::make_reciprocal( + 1, + 28, + percent_perbill(Perbill::from_parts(100_000)), // 0.01 % + percent_perbill(Perbill::from_parts(25_000)), // 0.0025 % + percent(50), + ); + pub const APP_CLUSTER_PROTOCOL_UPDATER: Curve = + Curve::make_reciprocal(16, 28 * 24, percent(96), percent(50), percent(100)); + pub const SUP_CLUSTER_PROTOCOL_UPDATER: Curve = Curve::make_reciprocal( + 1, + 28, + percent_perbill(Perbill::from_parts(100_000)), // 0.01 % + percent_perbill(Perbill::from_parts(25_000)), // 0.0025 % + percent(50), + ); + + // Root track + pub const ROOT_TRACK_ID: u16 = 0; + // Whitelister track + pub const WHITELISTED_CALLER_TRACK_ID: u16 = 1; + // General admin tracks + pub const STAKING_ADMIN_TRACK_ID: u16 = 10; + pub const TREASURER_TRACK_ID: u16 = 11; + pub const GENERAL_ADMIN_TRACK_ID: u16 = 14; + // Referendum admins tracks + pub const REFERENDUM_CANCELER_TRACK_ID: u16 = 20; + pub const REFERENDUM_KILLER_TRACK_ID: u16 = 21; + // Limited treasury spenders tracks + pub const SMALL_TIPPER_TRACK_ID: u16 = 30; + pub const BIG_TIPPER_TRACK_ID: u16 = 31; + pub const SMALL_SPENDER_TRACK_ID: u16 = 32; + pub const MEDIUM_SPENDER_TRACK_ID: u16 = 33; + pub const BIG_SPENDER_TRACK_ID: u16 = 34; + // DDC admins tracks + pub const CLUSTER_PROTOCOL_ACTIVATOR_TRACK_ID: u16 = 100; + pub const CLUSTER_PROTOCOL_UPDATER_TRACK_ID: u16 = 101; +} diff --git a/scripts/start_validator1.sh b/scripts/start_validator1.sh new file mode 100644 index 000000000..601a97554 --- /dev/null +++ b/scripts/start_validator1.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +../target/release/cere \ +--base-path /tmp/alice \ +--chain local \ +--alice \ +--port 30333 \ +--rpc-port 9945 \ +--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \ +--validator diff --git a/scripts/start_validator2.sh b/scripts/start_validator2.sh new file mode 100644 index 000000000..439b3f86d --- /dev/null +++ b/scripts/start_validator2.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +../target/release/cere \ +--base-path /tmp/bob \ +--chain local \ +--bob \ +--port 30334 \ +--rpc-port 9946 \ +--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \ +--validator diff --git a/scripts/start_validator3.sh b/scripts/start_validator3.sh new file mode 100644 index 000000000..0c5acd01a --- /dev/null +++ b/scripts/start_validator3.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +../target/release/cere \ +--base-path /tmp/charlie \ +--chain local \ +--charlie \ +--port 30334 \ +--rpc-port 9947 \ +--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \ +--validator