From 98dec509a4d58033a0594a3307230098f1544d8d Mon Sep 17 00:00:00 2001 From: Andrea Giacobino Date: Fri, 12 Apr 2024 19:26:11 +0200 Subject: [PATCH] tracing runtime 2900 (#97) * Add tracing sources for runtime 2900 * disable wasm src, fix build with srtool * Add tracing wasm for runtime 2900 --- scripts/build-tracing-runtime.sh | 1 + ...nbase-runtime-2900-substitute-tracing.json | 1 + ...nbeam-runtime-2900-substitute-tracing.json | 1 + ...river-runtime-2900-substitute-tracing.json | 1 + tracing/2900/Cargo.lock | 8484 +++++++++++++++++ tracing/2900/Cargo.toml | 444 + tracing/2900/runtime/common/Cargo.toml | 146 + tracing/2900/runtime/common/src/apis.rs | 939 ++ .../2900/runtime/common/src/benchmarking.rs | 30 + .../common/src/impl_moonbeam_xcm_call.rs | 51 + .../src/impl_moonbeam_xcm_call_tracing.rs | 118 + .../src/impl_on_charge_evm_transaction.rs | 62 + .../common/src/impl_self_contained_call.rs | 78 + .../runtime/common/src/impl_xcm_evm_runner.rs | 188 + tracing/2900/runtime/common/src/lib.rs | 29 + tracing/2900/runtime/common/src/migrations.rs | 369 + .../src/weights/cumulus_pallet_xcmp_queue.rs | 154 + .../2900/runtime/common/src/weights/mod.rs | 48 + .../src/weights/pallet_asset_manager.rs | 144 + .../common/src/weights/pallet_assets.rs | 485 + .../src/weights/pallet_author_inherent.rs | 70 + .../src/weights/pallet_author_mapping.rs | 122 + .../src/weights/pallet_author_slot_filter.rs | 59 + .../common/src/weights/pallet_balances.rs | 151 + .../common/src/weights/pallet_collective.rs | 308 + .../src/weights/pallet_conviction_voting.rs | 184 + .../src/weights/pallet_crowdloan_rewards.rs | 163 + .../runtime/common/src/weights/pallet_evm.rs | 78 + .../common/src/weights/pallet_identity.rs | 411 + .../pallet_moonbeam_lazy_migrations.rs | 77 + .../src/weights/pallet_moonbeam_orbiters.rs | 203 + .../common/src/weights/pallet_multisig.rs | 160 + .../src/weights/pallet_parachain_staking.rs | 864 ++ .../weights/pallet_precompile_benchmarks.rs | 69 + .../common/src/weights/pallet_preimage.rs | 251 + .../common/src/weights/pallet_proxy.rs | 217 + .../common/src/weights/pallet_randomness.rs | 162 + .../common/src/weights/pallet_referenda.rs | 469 + .../src/weights/pallet_relay_storage_roots.rs | 64 + .../common/src/weights/pallet_scheduler.rs | 196 + .../runtime/common/src/weights/pallet_sudo.rs | 91 + .../common/src/weights/pallet_timestamp.rs | 67 + .../common/src/weights/pallet_treasury.rs | 187 + .../common/src/weights/pallet_utility.rs | 105 + .../common/src/weights/pallet_whitelist.rs | 118 + .../runtime/common/src/weights/pallet_xcm.rs | 318 + .../src/weights/pallet_xcm_transactor.rs | 192 + tracing/2900/runtime/moonbase/Cargo.toml | 420 + tracing/2900/runtime/moonbase/build.rs | 25 + .../2900/runtime/moonbase/src/asset_config.rs | 221 + .../moonbase/src/governance/councils.rs | 62 + .../runtime/moonbase/src/governance/mod.rs | 29 + .../moonbase/src/governance/origins.rs | 83 + .../moonbase/src/governance/referenda.rs | 101 + .../runtime/moonbase/src/governance/tracks.rs | 193 + tracing/2900/runtime/moonbase/src/lib.rs | 1803 ++++ .../2900/runtime/moonbase/src/migrations.rs | 43 + .../2900/runtime/moonbase/src/precompiles.rs | 312 + .../2900/runtime/moonbase/src/timestamp.rs | 87 + .../2900/runtime/moonbase/src/xcm_config.rs | 732 ++ .../2900/runtime/moonbase/tests/common/mod.rs | 394 + .../runtime/moonbase/tests/evm_tracing.rs | 110 + .../moonbase/tests/integration_test.rs | 3004 ++++++ .../runtime/moonbase/tests/runtime_apis.rs | 392 + .../runtime/moonbase/tests/xcm_mock/mod.rs | 272 + .../moonbase/tests/xcm_mock/parachain.rs | 1129 +++ .../moonbase/tests/xcm_mock/relay_chain.rs | 412 + .../moonbase/tests/xcm_mock/statemint_like.rs | 575 ++ .../2900/runtime/moonbase/tests/xcm_tests.rs | 3970 ++++++++ tracing/2900/runtime/moonbeam/Cargo.toml | 401 + tracing/2900/runtime/moonbeam/build.rs | 25 + .../2900/runtime/moonbeam/src/asset_config.rs | 219 + .../moonbeam/src/governance/councils.rs | 61 + .../runtime/moonbeam/src/governance/mod.rs | 29 + .../moonbeam/src/governance/origins.rs | 85 + .../moonbeam/src/governance/referenda.rs | 100 + .../runtime/moonbeam/src/governance/tracks.rs | 193 + tracing/2900/runtime/moonbeam/src/lib.rs | 1788 ++++ .../2900/runtime/moonbeam/src/precompiles.rs | 285 + .../2900/runtime/moonbeam/src/xcm_config.rs | 709 ++ .../2900/runtime/moonbeam/tests/common/mod.rs | 373 + .../runtime/moonbeam/tests/evm_tracing.rs | 110 + .../moonbeam/tests/integration_test.rs | 2762 ++++++ .../runtime/moonbeam/tests/runtime_apis.rs | 393 + .../runtime/moonbeam/tests/xcm_mock/mod.rs | 271 + .../moonbeam/tests/xcm_mock/parachain.rs | 1088 +++ .../moonbeam/tests/xcm_mock/relay_chain.rs | 412 + .../moonbeam/tests/xcm_mock/statemint_like.rs | 575 ++ .../2900/runtime/moonbeam/tests/xcm_tests.rs | 3689 +++++++ tracing/2900/runtime/moonriver/Cargo.toml | 406 + tracing/2900/runtime/moonriver/build.rs | 25 + .../runtime/moonriver/src/asset_config.rs | 219 + .../moonriver/src/governance/councils.rs | 61 + .../runtime/moonriver/src/governance/mod.rs | 29 + .../moonriver/src/governance/origins.rs | 84 + .../moonriver/src/governance/referenda.rs | 100 + .../moonriver/src/governance/tracks.rs | 193 + tracing/2900/runtime/moonriver/src/lib.rs | 1790 ++++ .../2900/runtime/moonriver/src/precompiles.rs | 259 + .../2900/runtime/moonriver/src/xcm_config.rs | 722 ++ .../runtime/moonriver/tests/common/mod.rs | 380 + .../runtime/moonriver/tests/evm_tracing.rs | 110 + .../moonriver/tests/integration_test.rs | 2664 ++++++ .../runtime/moonriver/tests/runtime_apis.rs | 396 + .../runtime/moonriver/tests/xcm_mock/mod.rs | 273 + .../moonriver/tests/xcm_mock/parachain.rs | 1094 +++ .../moonriver/tests/xcm_mock/relay_chain.rs | 412 + .../tests/xcm_mock/statemine_like.rs | 575 ++ .../2900/runtime/moonriver/tests/xcm_tests.rs | 3998 ++++++++ tracing/2900/rust-toolchain | 5 + tracing/2900/shared/primitives/ext/Cargo.toml | 33 + tracing/2900/shared/primitives/ext/src/lib.rs | 82 + .../shared/primitives/rpc/debug/Cargo.toml | 44 + .../shared/primitives/rpc/debug/src/lib.rs | 97 + .../rpc/evm-tracing-events/Cargo.toml | 37 + .../rpc/evm-tracing-events/src/evm.rs | 258 + .../rpc/evm-tracing-events/src/gasometer.rs | 119 + .../rpc/evm-tracing-events/src/lib.rs | 284 + .../rpc/evm-tracing-events/src/runtime.rs | 156 + .../2900/shared/runtime/evm_tracer/Cargo.toml | 51 + .../2900/shared/runtime/evm_tracer/src/lib.rs | 117 + ...nbase-runtime-2900-substitute-tracing.wasm | Bin 0 -> 2111185 bytes ...nbeam-runtime-2900-substitute-tracing.wasm | Bin 0 -> 2084380 bytes ...river-runtime-2900-substitute-tracing.wasm | Bin 0 -> 2089715 bytes 124 files changed, 60139 insertions(+) create mode 100644 srtool-digest/moonbase-runtime-2900-substitute-tracing.json create mode 100644 srtool-digest/moonbeam-runtime-2900-substitute-tracing.json create mode 100644 srtool-digest/moonriver-runtime-2900-substitute-tracing.json create mode 100644 tracing/2900/Cargo.lock create mode 100644 tracing/2900/Cargo.toml create mode 100644 tracing/2900/runtime/common/Cargo.toml create mode 100644 tracing/2900/runtime/common/src/apis.rs create mode 100644 tracing/2900/runtime/common/src/benchmarking.rs create mode 100644 tracing/2900/runtime/common/src/impl_moonbeam_xcm_call.rs create mode 100644 tracing/2900/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs create mode 100644 tracing/2900/runtime/common/src/impl_on_charge_evm_transaction.rs create mode 100644 tracing/2900/runtime/common/src/impl_self_contained_call.rs create mode 100644 tracing/2900/runtime/common/src/impl_xcm_evm_runner.rs create mode 100644 tracing/2900/runtime/common/src/lib.rs create mode 100644 tracing/2900/runtime/common/src/migrations.rs create mode 100644 tracing/2900/runtime/common/src/weights/cumulus_pallet_xcmp_queue.rs create mode 100644 tracing/2900/runtime/common/src/weights/mod.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_asset_manager.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_assets.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_author_inherent.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_author_mapping.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_author_slot_filter.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_balances.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_collective.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_conviction_voting.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_crowdloan_rewards.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_evm.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_identity.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_moonbeam_lazy_migrations.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_moonbeam_orbiters.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_multisig.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_parachain_staking.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_precompile_benchmarks.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_preimage.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_proxy.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_randomness.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_referenda.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_relay_storage_roots.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_scheduler.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_sudo.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_timestamp.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_treasury.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_utility.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_whitelist.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_xcm.rs create mode 100644 tracing/2900/runtime/common/src/weights/pallet_xcm_transactor.rs create mode 100644 tracing/2900/runtime/moonbase/Cargo.toml create mode 100644 tracing/2900/runtime/moonbase/build.rs create mode 100644 tracing/2900/runtime/moonbase/src/asset_config.rs create mode 100644 tracing/2900/runtime/moonbase/src/governance/councils.rs create mode 100644 tracing/2900/runtime/moonbase/src/governance/mod.rs create mode 100644 tracing/2900/runtime/moonbase/src/governance/origins.rs create mode 100644 tracing/2900/runtime/moonbase/src/governance/referenda.rs create mode 100644 tracing/2900/runtime/moonbase/src/governance/tracks.rs create mode 100644 tracing/2900/runtime/moonbase/src/lib.rs create mode 100644 tracing/2900/runtime/moonbase/src/migrations.rs create mode 100644 tracing/2900/runtime/moonbase/src/precompiles.rs create mode 100644 tracing/2900/runtime/moonbase/src/timestamp.rs create mode 100644 tracing/2900/runtime/moonbase/src/xcm_config.rs create mode 100644 tracing/2900/runtime/moonbase/tests/common/mod.rs create mode 100644 tracing/2900/runtime/moonbase/tests/evm_tracing.rs create mode 100644 tracing/2900/runtime/moonbase/tests/integration_test.rs create mode 100644 tracing/2900/runtime/moonbase/tests/runtime_apis.rs create mode 100644 tracing/2900/runtime/moonbase/tests/xcm_mock/mod.rs create mode 100644 tracing/2900/runtime/moonbase/tests/xcm_mock/parachain.rs create mode 100644 tracing/2900/runtime/moonbase/tests/xcm_mock/relay_chain.rs create mode 100644 tracing/2900/runtime/moonbase/tests/xcm_mock/statemint_like.rs create mode 100644 tracing/2900/runtime/moonbase/tests/xcm_tests.rs create mode 100644 tracing/2900/runtime/moonbeam/Cargo.toml create mode 100644 tracing/2900/runtime/moonbeam/build.rs create mode 100644 tracing/2900/runtime/moonbeam/src/asset_config.rs create mode 100644 tracing/2900/runtime/moonbeam/src/governance/councils.rs create mode 100644 tracing/2900/runtime/moonbeam/src/governance/mod.rs create mode 100644 tracing/2900/runtime/moonbeam/src/governance/origins.rs create mode 100644 tracing/2900/runtime/moonbeam/src/governance/referenda.rs create mode 100644 tracing/2900/runtime/moonbeam/src/governance/tracks.rs create mode 100644 tracing/2900/runtime/moonbeam/src/lib.rs create mode 100644 tracing/2900/runtime/moonbeam/src/precompiles.rs create mode 100644 tracing/2900/runtime/moonbeam/src/xcm_config.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/common/mod.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/evm_tracing.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/integration_test.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/runtime_apis.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/xcm_mock/mod.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/xcm_mock/parachain.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/xcm_mock/relay_chain.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/xcm_mock/statemint_like.rs create mode 100644 tracing/2900/runtime/moonbeam/tests/xcm_tests.rs create mode 100644 tracing/2900/runtime/moonriver/Cargo.toml create mode 100644 tracing/2900/runtime/moonriver/build.rs create mode 100644 tracing/2900/runtime/moonriver/src/asset_config.rs create mode 100644 tracing/2900/runtime/moonriver/src/governance/councils.rs create mode 100644 tracing/2900/runtime/moonriver/src/governance/mod.rs create mode 100644 tracing/2900/runtime/moonriver/src/governance/origins.rs create mode 100644 tracing/2900/runtime/moonriver/src/governance/referenda.rs create mode 100644 tracing/2900/runtime/moonriver/src/governance/tracks.rs create mode 100644 tracing/2900/runtime/moonriver/src/lib.rs create mode 100644 tracing/2900/runtime/moonriver/src/precompiles.rs create mode 100644 tracing/2900/runtime/moonriver/src/xcm_config.rs create mode 100644 tracing/2900/runtime/moonriver/tests/common/mod.rs create mode 100644 tracing/2900/runtime/moonriver/tests/evm_tracing.rs create mode 100644 tracing/2900/runtime/moonriver/tests/integration_test.rs create mode 100644 tracing/2900/runtime/moonriver/tests/runtime_apis.rs create mode 100644 tracing/2900/runtime/moonriver/tests/xcm_mock/mod.rs create mode 100644 tracing/2900/runtime/moonriver/tests/xcm_mock/parachain.rs create mode 100644 tracing/2900/runtime/moonriver/tests/xcm_mock/relay_chain.rs create mode 100644 tracing/2900/runtime/moonriver/tests/xcm_mock/statemine_like.rs create mode 100644 tracing/2900/runtime/moonriver/tests/xcm_tests.rs create mode 100644 tracing/2900/rust-toolchain create mode 100644 tracing/2900/shared/primitives/ext/Cargo.toml create mode 100644 tracing/2900/shared/primitives/ext/src/lib.rs create mode 100644 tracing/2900/shared/primitives/rpc/debug/Cargo.toml create mode 100644 tracing/2900/shared/primitives/rpc/debug/src/lib.rs create mode 100644 tracing/2900/shared/primitives/rpc/evm-tracing-events/Cargo.toml create mode 100644 tracing/2900/shared/primitives/rpc/evm-tracing-events/src/evm.rs create mode 100644 tracing/2900/shared/primitives/rpc/evm-tracing-events/src/gasometer.rs create mode 100644 tracing/2900/shared/primitives/rpc/evm-tracing-events/src/lib.rs create mode 100644 tracing/2900/shared/primitives/rpc/evm-tracing-events/src/runtime.rs create mode 100644 tracing/2900/shared/runtime/evm_tracer/Cargo.toml create mode 100644 tracing/2900/shared/runtime/evm_tracer/src/lib.rs create mode 100644 wasm/moonbase-runtime-2900-substitute-tracing.wasm create mode 100644 wasm/moonbeam-runtime-2900-substitute-tracing.wasm create mode 100644 wasm/moonriver-runtime-2900-substitute-tracing.wasm diff --git a/scripts/build-tracing-runtime.sh b/scripts/build-tracing-runtime.sh index 2ffb4b9c..eded7785 100755 --- a/scripts/build-tracing-runtime.sh +++ b/scripts/build-tracing-runtime.sh @@ -38,6 +38,7 @@ for RUNTIME_NAME in ${ALL_RUNTIMES_NAMES[@]}; do --rm \ -e PACKAGE=$RUNTIME_NAME-runtime \ -e RUNTIME_DIR=$RUNTIME_DIR \ + -e WASM_BUILD_STD=0 \ -e CARGO_BUILD_JOBS=$CARGO_BUILD_JOBS \ -v $PWD:/build \ $SRTOOL_IMAGE build --app --json -cM" diff --git a/srtool-digest/moonbase-runtime-2900-substitute-tracing.json b/srtool-digest/moonbase-runtime-2900-substitute-tracing.json new file mode 100644 index 00000000..3325243a --- /dev/null +++ b/srtool-digest/moonbase-runtime-2900-substitute-tracing.json @@ -0,0 +1 @@ +{"gen":"srtool v0.13.0","src":"zip","version":"0.8.4","commit":"","tag":"","branch":"","rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonbase-runtime","tmsp":"2024-04-12T16:35:27Z","size":"2111185","prop":"0x8af27cdd73a751aee45b7ff44c96f6429a08d1d5d5929c0496d6c9b1c3af7ea3","authorize_upgrade_prop":"0xa7907d0f9f7b670614ecc8df9086cc2c1827ef1856c0136f88132c4a7fa213ca","ipfs":"QmeFArP7s6d8qGidDMCqVDkbQdwdQd9h4JsVBt2hpMHs7x","sha256":"0x239e4c5a3ddd7949a3fc5d54067d8b2ce2517b68e825249972c6609c0c7eaa8e","wasm":"runtime/moonbase/target/srtool/release/wbuild/moonbase-runtime/moonbase_runtime.compact.compressed.wasm","info":{"generator":{"name":"srtool","version":"0.13.0"},"src":"zip","version":"0.8.4","git":{"commit":"","tag":"","branch":""},"rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonbase-runtime","profile":"release"},"context":{"package":"moonbase-runtime","runtime_dir":"runtime/moonbase","docker":{"image":"paritytech/srtool","tag":"1.74.0"},"profile":"release"},"runtimes":{"compact":{"tmsp":"2024-04-12T16:35:09Z","size":"10257519","prop":"0xbad46acb92a869f1e0b1cd4c7cdb90d5985f7ecc77c1eb31d5a689974bfa38dd","authorize_upgrade_prop":"0xc75fe35fbd2a92384cd2e76acf9f76f3ffe96d8553a1024c237c225705f3df14","blake2_256":"0x4788fecfcdb2dc95f9b4b208a25df596cca25e855fa7391b2a0bb16aae7ea3a6","ipfs":"Qmcn58mbwSaDUiu8LVXQmpmoZKt6GoX5LKfvntLnjyjMYw","sha256":"0x00f68e4f093456f2a867343d8c6bc311ed2950d3f0072820adde6e4094b14ac6","wasm":"runtime/moonbase/target/srtool/release/wbuild/moonbase-runtime/moonbase_runtime.compact.wasm","subwasm":{"size":10257519,"compression":{"size_compressed":10257519,"size_decompressed":10257519,"compressed":false},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonbase","implName":"moonbase","authoringVersion":4,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0xbad46acb92a869f1e0b1cd4c7cdb90d5985f7ecc77c1eb31d5a689974bfa38dd","parachain_authorize_upgrade_hash":"0xc75fe35fbd2a92384cd2e76acf9f76f3ffe96d8553a1024c237c225705f3df14","ipfs_hash":"Qmcn58mbwSaDUiu8LVXQmpmoZKt6GoX5LKfvntLnjyjMYw","blake2_256":"0x4788fecfcdb2dc95f9b4b208a25df596cca25e855fa7391b2a0bb16aae7ea3a6"}},"compressed":{"tmsp":"2024-04-12T16:31:56Z","size":"2111185","prop":"0x8af27cdd73a751aee45b7ff44c96f6429a08d1d5d5929c0496d6c9b1c3af7ea3","authorize_upgrade_prop":"0xa7907d0f9f7b670614ecc8df9086cc2c1827ef1856c0136f88132c4a7fa213ca","blake2_256":"0xac417e9f24cca7362c7baf2ef105ffc1227080b981cabdad4226dee0cf82cb23","ipfs":"QmeFArP7s6d8qGidDMCqVDkbQdwdQd9h4JsVBt2hpMHs7x","sha256":"0x239e4c5a3ddd7949a3fc5d54067d8b2ce2517b68e825249972c6609c0c7eaa8e","wasm":"runtime/moonbase/target/srtool/release/wbuild/moonbase-runtime/moonbase_runtime.compact.compressed.wasm","subwasm":{"size":2111185,"compression":{"size_compressed":2111185,"size_decompressed":10257519,"compressed":true},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonbase","implName":"moonbase","authoringVersion":4,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0x8af27cdd73a751aee45b7ff44c96f6429a08d1d5d5929c0496d6c9b1c3af7ea3","parachain_authorize_upgrade_hash":"0xa7907d0f9f7b670614ecc8df9086cc2c1827ef1856c0136f88132c4a7fa213ca","ipfs_hash":"QmeFArP7s6d8qGidDMCqVDkbQdwdQd9h4JsVBt2hpMHs7x","blake2_256":"0xac417e9f24cca7362c7baf2ef105ffc1227080b981cabdad4226dee0cf82cb23"}}}} diff --git a/srtool-digest/moonbeam-runtime-2900-substitute-tracing.json b/srtool-digest/moonbeam-runtime-2900-substitute-tracing.json new file mode 100644 index 00000000..0b3a2fb3 --- /dev/null +++ b/srtool-digest/moonbeam-runtime-2900-substitute-tracing.json @@ -0,0 +1 @@ +{"gen":"srtool v0.13.0","src":"zip","version":"0.8.4","commit":"","tag":"","branch":"","rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonbeam-runtime","tmsp":"2024-04-12T16:46:39Z","size":"2084380","prop":"0x4b770eb378369a1bc54e992db257d925cdb288a0886ea3702963246933917538","authorize_upgrade_prop":"0xbcb9701565ff5d468ff1ea4bf0c6adadd6757b14dec48aeae7396dd947af9d5a","ipfs":"QmVM4AxLnagYnLVNmsc58g6nWtpDaNVAsCaGDTD5yh8dad","sha256":"0xa464a54c946bb0b0fe3211c5d216fe596314dbca4dfabd02b7247f46ae4e5179","wasm":"runtime/moonbeam/target/srtool/release/wbuild/moonbeam-runtime/moonbeam_runtime.compact.compressed.wasm","info":{"generator":{"name":"srtool","version":"0.13.0"},"src":"zip","version":"0.8.4","git":{"commit":"","tag":"","branch":""},"rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonbeam-runtime","profile":"release"},"context":{"package":"moonbeam-runtime","runtime_dir":"runtime/moonbeam","docker":{"image":"paritytech/srtool","tag":"1.74.0"},"profile":"release"},"runtimes":{"compact":{"tmsp":"2024-04-12T16:46:21Z","size":"10162255","prop":"0xd1ff8dcbfef648a376cc35e6975a83b73cbe10e0fe7aab06a31252499fcaaff3","authorize_upgrade_prop":"0x58035ec12d443514421b45d374f30c5ecd9863a3702268f6260151605e057888","blake2_256":"0x6e1bdf9acf986e88a40789abdaad72e6946cee2a238181f9187d9dc38fd17ed6","ipfs":"QmcVzWrhGLQdpdYWaEjwi9AvwAdJC4X4tp8i2XMnSLeEt8","sha256":"0xc5315ff25daee0ab6eae9db2cdbf39a253e84ab814af06fbe4f2e43d6ed0764c","wasm":"runtime/moonbeam/target/srtool/release/wbuild/moonbeam-runtime/moonbeam_runtime.compact.wasm","subwasm":{"size":10162255,"compression":{"size_compressed":10162255,"size_decompressed":10162255,"compressed":false},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonbeam","implName":"moonbeam","authoringVersion":3,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0xd1ff8dcbfef648a376cc35e6975a83b73cbe10e0fe7aab06a31252499fcaaff3","parachain_authorize_upgrade_hash":"0x58035ec12d443514421b45d374f30c5ecd9863a3702268f6260151605e057888","ipfs_hash":"QmcVzWrhGLQdpdYWaEjwi9AvwAdJC4X4tp8i2XMnSLeEt8","blake2_256":"0x6e1bdf9acf986e88a40789abdaad72e6946cee2a238181f9187d9dc38fd17ed6"}},"compressed":{"tmsp":"2024-04-12T16:43:00Z","size":"2084380","prop":"0x4b770eb378369a1bc54e992db257d925cdb288a0886ea3702963246933917538","authorize_upgrade_prop":"0xbcb9701565ff5d468ff1ea4bf0c6adadd6757b14dec48aeae7396dd947af9d5a","blake2_256":"0xd54a76c0894eedac1487a4122e7707140c4f9d34c71262579730bf2298e66264","ipfs":"QmVM4AxLnagYnLVNmsc58g6nWtpDaNVAsCaGDTD5yh8dad","sha256":"0xa464a54c946bb0b0fe3211c5d216fe596314dbca4dfabd02b7247f46ae4e5179","wasm":"runtime/moonbeam/target/srtool/release/wbuild/moonbeam-runtime/moonbeam_runtime.compact.compressed.wasm","subwasm":{"size":2084380,"compression":{"size_compressed":2084380,"size_decompressed":10162255,"compressed":true},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonbeam","implName":"moonbeam","authoringVersion":3,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0x4b770eb378369a1bc54e992db257d925cdb288a0886ea3702963246933917538","parachain_authorize_upgrade_hash":"0xbcb9701565ff5d468ff1ea4bf0c6adadd6757b14dec48aeae7396dd947af9d5a","ipfs_hash":"QmVM4AxLnagYnLVNmsc58g6nWtpDaNVAsCaGDTD5yh8dad","blake2_256":"0xd54a76c0894eedac1487a4122e7707140c4f9d34c71262579730bf2298e66264"}}}} diff --git a/srtool-digest/moonriver-runtime-2900-substitute-tracing.json b/srtool-digest/moonriver-runtime-2900-substitute-tracing.json new file mode 100644 index 00000000..07d84aa7 --- /dev/null +++ b/srtool-digest/moonriver-runtime-2900-substitute-tracing.json @@ -0,0 +1 @@ +{"gen":"srtool v0.13.0","src":"zip","version":"0.8.4","commit":"","tag":"","branch":"","rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonriver-runtime","tmsp":"2024-04-12T16:40:20Z","size":"2089715","prop":"0x230ee3747c2c2260096122de1a91683edf953c7ee345f005a6c7ea806003074b","authorize_upgrade_prop":"0x52fc158187c59296ee1f6915ed9d624818df88639fa353a19df2890b539d91c2","ipfs":"QmQpUZmLneRFVb5sziCbXfVGyMB3P247qggKi9gP4cchAm","sha256":"0x9b0464b81e866cbc2eada59d390a6075f42c2ccb54cd8221ff2698133e09b9c6","wasm":"runtime/moonriver/target/srtool/release/wbuild/moonriver-runtime/moonriver_runtime.compact.compressed.wasm","info":{"generator":{"name":"srtool","version":"0.13.0"},"src":"zip","version":"0.8.4","git":{"commit":"","tag":"","branch":""},"rustc":"rustc 1.74.0 (79e9716c9 2023-11-13)","pkg":"moonriver-runtime","profile":"release"},"context":{"package":"moonriver-runtime","runtime_dir":"runtime/moonriver","docker":{"image":"paritytech/srtool","tag":"1.74.0"},"profile":"release"},"runtimes":{"compact":{"tmsp":"2024-04-12T16:40:03Z","size":"10153039","prop":"0xc569a60c1f0192bea2a9dc3b8ce46d34e718eea5bedcfe90cba92f25998cdf5b","authorize_upgrade_prop":"0x5f719977995166a7dda0149a4f879b095dcbd5237ea8cb4932a56dc4bda8ade1","blake2_256":"0x963ac3730788c6c732628b529a78e342b2d80b8efc993f0675e6799abc074f89","ipfs":"QmWqexT7FtYRwQJB3JeQ8VmnJtqNSF8gFpicMd1mz9TyXr","sha256":"0xa708d1ed791441001cf84676eeffc43294b006f78717717e2f164863abc89868","wasm":"runtime/moonriver/target/srtool/release/wbuild/moonriver-runtime/moonriver_runtime.compact.wasm","subwasm":{"size":10153039,"compression":{"size_compressed":10153039,"size_decompressed":10153039,"compressed":false},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonriver","implName":"moonriver","authoringVersion":3,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0xc569a60c1f0192bea2a9dc3b8ce46d34e718eea5bedcfe90cba92f25998cdf5b","parachain_authorize_upgrade_hash":"0x5f719977995166a7dda0149a4f879b095dcbd5237ea8cb4932a56dc4bda8ade1","ipfs_hash":"QmWqexT7FtYRwQJB3JeQ8VmnJtqNSF8gFpicMd1mz9TyXr","blake2_256":"0x963ac3730788c6c732628b529a78e342b2d80b8efc993f0675e6799abc074f89"}},"compressed":{"tmsp":"2024-04-12T16:36:52Z","size":"2089715","prop":"0x230ee3747c2c2260096122de1a91683edf953c7ee345f005a6c7ea806003074b","authorize_upgrade_prop":"0x52fc158187c59296ee1f6915ed9d624818df88639fa353a19df2890b539d91c2","blake2_256":"0x2d3dcc755ad01956300dffd27d219496a96c57aade1d738d93b5258e4880163a","ipfs":"QmQpUZmLneRFVb5sziCbXfVGyMB3P247qggKi9gP4cchAm","sha256":"0x9b0464b81e866cbc2eada59d390a6075f42c2ccb54cd8221ff2698133e09b9c6","wasm":"runtime/moonriver/target/srtool/release/wbuild/moonriver-runtime/moonriver_runtime.compact.compressed.wasm","subwasm":{"size":2089715,"compression":{"size_compressed":2089715,"size_decompressed":10153039,"compressed":true},"reserved_meta":[109,101,116,97],"reserved_meta_valid":true,"metadata_version":14,"core_version":{"specName":"moonriver","implName":"moonriver","authoringVersion":3,"specVersion":2900,"implVersion":0,"apis":[["0xd2bc9897eed08f15",3],["0xd0399cd053adda2b",1],["0xdf6acb689907609b",4],["0x37e397fc7c91f5e4",2],["0x40fe3ad401f8959a",6],["0xf78b278be53f454c",2],["0xab3c0572291feb8b",1],["0xfbc577b9d747efd6",1],["0xbc9d89904f5b923f",1],["0xbd78255d4feeea1f",5],["0xa33d43f58731ad84",2],["0x582211f65bb14b89",5],["0xe65b00e46cedd0aa",2],["0x37c8bb1350a9a2a8",4],["0x2aa62120049dd2d2",1],["0xea93e3f16f3d6962",2],["0xba8173bf23b2e6f8",1]],"transactionVersion":2,"stateVersion":0},"proposal_hash":"0x230ee3747c2c2260096122de1a91683edf953c7ee345f005a6c7ea806003074b","parachain_authorize_upgrade_hash":"0x52fc158187c59296ee1f6915ed9d624818df88639fa353a19df2890b539d91c2","ipfs_hash":"QmQpUZmLneRFVb5sziCbXfVGyMB3P247qggKi9gP4cchAm","blake2_256":"0x2d3dcc755ad01956300dffd27d219496a96c57aade1d738d93b5258e4880163a"}}}} diff --git a/tracing/2900/Cargo.lock b/tracing/2900/Cargo.lock new file mode 100644 index 00000000..cbd703e6 --- /dev/null +++ b/tracing/2900/Cargo.lock @@ -0,0 +1,8484 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "account" +version = "0.1.1" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "blake2-rfc", + "impl-serde 0.3.2", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sha3", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.1", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "affix" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e7ea84d3fa2009f355f8429a0b418a96849135a4188fadf384f59127d5d4bc" +dependencies = [ + "convert_case 0.5.0", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.14", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom 0.2.14", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "aquamarine" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +dependencies = [ + "include_dir", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cde0f2aa063a2a5c28d39b47761aa102bda7c13c84fc118a61b87c7b2f785c" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-scale" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f69c00b3b529be29528a6f2fd5fa7b1790f8bed81b9cdca17e326538545a179" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ark-secret-scalar" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "ark-transcript", + "digest 0.10.7", + "getrandom_or_panic", + "zeroize", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "digest 0.10.7", + "rand_core 0.6.4", + "sha3", +] + +[[package]] +name = "array-bytes" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f840fb7195bcfc5e17ea40c26e5ce6d5b9ce5d584466e17703209657e459ae0" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-backing-primitives" +version = "0.9.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "sp-api", + "sp-consensus-slots", +] + +[[package]] +name = "async-trait" +version = "0.1.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line 0.21.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.32.2", + "rustc-demangle", +] + +[[package]] +name = "bandersnatch_vrfs" +version = "0.0.4" +source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ff", + "ark-serialize", + "ark-std", + "dleq_vrf", + "fflonk", + "merlin", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "ring", + "sha2 0.10.8", + "sp-ark-bls12-381", + "sp-ark-ed-on-bls12-381-bandersnatch", + "zeroize", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bounded-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32385ecb91a31bddaf908e8dcf4a15aef1bcd3913cc03ebfad02ff6d568abc1" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bp-xcm-bridge-hub-router" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "build-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" +dependencies = [ + "semver 0.6.0", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.22", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + +[[package]] +name = "cc" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.4", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "fflonk", + "getrandom_or_panic", + "merlin", + "rand_chacha 0.3.1", +] + +[[package]] +name = "common-path" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.14", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "constcat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "cumulus-pallet-dmp-queue" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bytes", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-primitives-proof-size-hostfunction", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-message-queue", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-runtime-parachains", + "scale-info", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "staging-xcm", + "trie-db", +] + +[[package]] +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "cumulus-pallet-xcm" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "cumulus-pallet-xcmp-queue" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bounded-collections", + "bp-xcm-bridge-hub-router", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-message-queue", + "parity-scale-codec", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "cumulus-primitives-core" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std", + "sp-trie", + "staging-xcm", +] + +[[package]] +name = "cumulus-primitives-parachain-inherent" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-std", + "sp-trie", +] + +[[package]] +name = "cumulus-primitives-proof-size-hostfunction" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "sp-externalities", + "sp-runtime-interface", + "sp-trie", +] + +[[package]] +name = "cumulus-primitives-timestamp" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "sp-inherents", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "cumulus-primitives-utility" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "log", + "pallet-asset-conversion", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "cxx" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db378d04296a84d8b7d047c36bb3954f0b46529db725d7e62fb02f9ba53ccc" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5262a7fa3f0bae2a55b767c223ba98032d7c328f5c13fa5cdc980b77fc0658" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.58", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8dcadd2e2fb4a501e1d9e93d6e88e6ea494306d8272069c92d5a9edf8855c0" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad08a837629ad949b73d032c637653d069e909cffe4ee7870b02301939ce39cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "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.58", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dleq_vrf" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-scale", + "ark-secret-scalar", + "ark-serialize", + "ark-std", + "ark-transcript", + "arrayvec 0.7.4", + "zeroize", +] + +[[package]] +name = "docify" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" +dependencies = [ + "docify_macros", +] + +[[package]] +name = "docify_macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" +dependencies = [ + "common-path", + "derive-syn-parse 0.2.0", + "once_cell", + "proc-macro2", + "quote", + "regex", + "syn 2.0.58", + "termcolor", + "toml", + "walkdir", +] + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", + "rand 0.7.3", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.2", + "ed25519 2.2.3", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "enumflags2" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enumn" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" +dependencies = [ + "bytes", + "ethereum-types", + "hash-db", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "sha3", + "trie-root", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "evm" +version = "0.41.1" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-v1.7.2#d8991ec727ad0fb64fe9957a3cd307387a6701e4" +dependencies = [ + "auto_impl", + "environmental", + "ethereum", + "evm-core", + "evm-gasometer", + "evm-runtime", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde", + "sha3", +] + +[[package]] +name = "evm-core" +version = "0.41.0" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-v1.7.2#d8991ec727ad0fb64fe9957a3cd307387a6701e4" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", +] + +[[package]] +name = "evm-gasometer" +version = "0.41.0" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-v1.7.2#d8991ec727ad0fb64fe9957a3cd307387a6701e4" +dependencies = [ + "environmental", + "evm-core", + "evm-runtime", + "primitive-types", +] + +[[package]] +name = "evm-runtime" +version = "0.41.0" +source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-v1.7.2#d8991ec727ad0fb64fe9957a3cd307387a6701e4" +dependencies = [ + "auto_impl", + "environmental", + "evm-core", + "primitive-types", + "sha3", +] + +[[package]] +name = "evm-tracing-events" +version = "0.1.0" +dependencies = [ + "environmental", + "ethereum", + "ethereum-types", + "evm", + "evm-gasometer", + "evm-runtime", + "parity-scale-codec", + "sp-runtime-interface", +] + +[[package]] +name = "expander" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fflonk" +version = "0.1.0" +source = "git+https://github.com/w3f/fflonk#1e854f35e9a65d08b11a86291405cdc95baa0a35" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "merlin", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "hex", + "impl-serde 0.4.0", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "fp-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "ethereum", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-ethereum" +version = "1.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "frame-support", + "parity-scale-codec", + "sp-std", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "evm", + "frame-support", + "num_enum 0.7.2", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-rpc" +version = "3.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", +] + +[[package]] +name = "fp-self-contained" +version = "1.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", +] + +[[package]] +name = "fp-storage" +version = "2.0.0" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "parity-scale-codec", + "serde", +] + +[[package]] +name = "frame-benchmarking" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "static_assertions", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "13.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "frame-election-provider-support" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-executive" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "frame-try-runtime", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "aquamarine", + "array-bytes", + "bitflags 1.3.2", + "docify", + "environmental", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "macro_magic", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "serde_json", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-crypto-hashing-proc-macro", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-metadata-ir", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "static_assertions", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "23.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse 0.1.5", + "expander", + "frame-support-procedural-tools", + "itertools", + "macro_magic", + "proc-macro-warning", + "proc-macro2", + "quote", + "sp-crypto-hashing", + "syn 2.0.58", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "10.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "11.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "frame-system" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cfg-if", + "docify", + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "frame-system-benchmarking" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "frame-try-runtime" +version = "0.34.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom_or_panic" +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", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[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 = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linregress" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" +dependencies = [ + "nalgebra", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "macro_magic" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03844fc635e92f3a0067e25fa4bf3e3dbf3f2927bf3aa01bb7bc8f1c428949d" +dependencies = [ + "macro_magic_core", + "macro_magic_macros", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "macro_magic_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468155613a44cfd825f1fb0ffa532b018253920d404e6fca1e8d43155198a46d" +dependencies = [ + "const-random", + "derive-syn-parse 0.1.5", + "macro_magic_core_macros", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "macro_magic_core_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "macro_magic_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" +dependencies = [ + "macro_magic_core", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.32", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "moonbase-runtime" +version = "0.8.4" +dependencies = [ + "account", + "async-backing-primitives", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "evm-tracing-events", + "fp-evm", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "moonbeam-core-primitives", + "moonbeam-evm-tracer", + "moonbeam-relay-encoder", + "moonbeam-rpc-primitives-debug", + "moonbeam-rpc-primitives-txpool", + "moonbeam-runtime-common 0.8.0-dev", + "moonbeam-xcm-benchmarks", + "nimbus-primitives", + "num_enum 0.5.11", + "orml-traits", + "orml-xcm-support", + "orml-xtokens", + "pallet-asset-manager", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-author-mapping", + "pallet-author-slot-filter", + "pallet-balances", + "pallet-collective", + "pallet-conviction-voting", + "pallet-crowdloan-rewards", + "pallet-emergency-para-xcm", + "pallet-erc20-xcm-bridge", + "pallet-ethereum", + "pallet-ethereum-xcm", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-evm-precompile-author-mapping", + "pallet-evm-precompile-balances-erc20", + "pallet-evm-precompile-batch", + "pallet-evm-precompile-blake2", + "pallet-evm-precompile-bn128", + "pallet-evm-precompile-call-permit", + "pallet-evm-precompile-collective", + "pallet-evm-precompile-conviction-voting", + "pallet-evm-precompile-crowdloan-rewards", + "pallet-evm-precompile-dispatch", + "pallet-evm-precompile-gmp", + "pallet-evm-precompile-identity", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-preimage", + "pallet-evm-precompile-proxy", + "pallet-evm-precompile-randomness", + "pallet-evm-precompile-referenda", + "pallet-evm-precompile-registry", + "pallet-evm-precompile-relay-encoder", + "pallet-evm-precompile-relay-verifier", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", + "pallet-evm-precompile-storage-cleaner", + "pallet-evm-precompile-xcm-transactor", + "pallet-evm-precompile-xcm-utils", + "pallet-evm-precompile-xtokens", + "pallet-evm-precompileset-assets-erc20", + "pallet-identity", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-moonbeam-lazy-migrations", + "pallet-moonbeam-orbiters", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-precompile-benchmarks", + "pallet-preimage", + "pallet-proxy", + "pallet-proxy-genesis-companion", + "pallet-randomness", + "pallet-referenda", + "pallet-relay-storage-roots", + "pallet-root-testing", + "pallet-scheduler", + "pallet-society", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-transactor", + "parachains-common", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "precompile-utils 0.1.1", + "rlp", + "scale-info", + "serde", + "session-keys-primitives", + "sha3", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "sp-weights", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "strum", + "strum_macros", + "substrate-wasm-builder", + "xcm-primitives 0.1.0", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonbeam-core-primitives" +version = "0.1.1" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "account", + "fp-self-contained", + "hex-literal 0.3.4", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "moonbeam-evm-tracer" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "evm", + "evm-gasometer", + "evm-runtime", + "evm-tracing-events", + "fp-evm", + "moonbeam-primitives-ext", + "pallet-evm", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "moonbeam-primitives-ext" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "evm-tracing-events", + "parity-scale-codec", + "sp-externalities", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "moonbeam-relay-encoder" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "frame-system", + "pallet-evm-precompile-relay-encoder", + "pallet-staking", + "pallet-xcm-transactor", + "parity-scale-codec", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonbeam-rpc-primitives-debug" +version = "0.1.0" +dependencies = [ + "environmental", + "ethereum", + "ethereum-types", + "hex", + "parity-scale-codec", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "moonbeam-rpc-primitives-txpool" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "ethereum", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "moonbeam-runtime" +version = "0.8.4" +dependencies = [ + "account", + "async-backing-primitives", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "evm-tracing-events", + "fp-evm", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "moonbeam-core-primitives", + "moonbeam-evm-tracer", + "moonbeam-relay-encoder", + "moonbeam-rpc-primitives-debug", + "moonbeam-rpc-primitives-txpool", + "moonbeam-runtime-common 0.8.0-dev", + "moonbeam-xcm-benchmarks", + "nimbus-primitives", + "num_enum 0.5.11", + "orml-traits", + "orml-xcm-support", + "orml-xtokens", + "pallet-asset-manager", + "pallet-assets", + "pallet-author-inherent", + "pallet-author-mapping", + "pallet-author-slot-filter", + "pallet-balances", + "pallet-collective", + "pallet-conviction-voting", + "pallet-crowdloan-rewards", + "pallet-erc20-xcm-bridge", + "pallet-ethereum", + "pallet-ethereum-xcm", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-evm-precompile-author-mapping", + "pallet-evm-precompile-balances-erc20", + "pallet-evm-precompile-batch", + "pallet-evm-precompile-blake2", + "pallet-evm-precompile-bn128", + "pallet-evm-precompile-call-permit", + "pallet-evm-precompile-collective", + "pallet-evm-precompile-conviction-voting", + "pallet-evm-precompile-crowdloan-rewards", + "pallet-evm-precompile-dispatch", + "pallet-evm-precompile-gmp", + "pallet-evm-precompile-identity", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-preimage", + "pallet-evm-precompile-proxy", + "pallet-evm-precompile-randomness", + "pallet-evm-precompile-referenda", + "pallet-evm-precompile-registry", + "pallet-evm-precompile-relay-encoder", + "pallet-evm-precompile-relay-verifier", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", + "pallet-evm-precompile-xcm-transactor", + "pallet-evm-precompile-xcm-utils", + "pallet-evm-precompile-xtokens", + "pallet-evm-precompileset-assets-erc20", + "pallet-identity", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-moonbeam-lazy-migrations", + "pallet-moonbeam-orbiters", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-precompile-benchmarks", + "pallet-preimage", + "pallet-proxy", + "pallet-proxy-genesis-companion", + "pallet-randomness", + "pallet-referenda", + "pallet-relay-storage-roots", + "pallet-root-testing", + "pallet-scheduler", + "pallet-society", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-transactor", + "parachains-common", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "precompile-utils 0.1.1", + "rlp", + "scale-info", + "serde", + "session-keys-primitives", + "sha3", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-slots", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "sp-weights", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "strum", + "strum_macros", + "substrate-wasm-builder", + "xcm-primitives 0.1.0", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonbeam-runtime-common" +version = "0.8.0-dev" +dependencies = [ + "account", + "cumulus-pallet-xcmp-queue", + "fp-ethereum", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "frame-try-runtime", + "hex-literal 0.3.4", + "impl-trait-for-tuples", + "log", + "moonbeam-xcm-benchmarks", + "nimbus-primitives", + "pallet-asset-manager", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-author-mapping", + "pallet-author-slot-filter", + "pallet-balances", + "pallet-collective", + "pallet-conviction-voting", + "pallet-crowdloan-rewards", + "pallet-ethereum-xcm", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-identity", + "pallet-migrations", + "pallet-moonbeam-lazy-migrations", + "pallet-moonbeam-orbiters", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-precompile-benchmarks", + "pallet-preimage", + "pallet-proxy", + "pallet-randomness", + "pallet-referenda", + "pallet-relay-storage-roots", + "pallet-scheduler", + "pallet-sudo", + "pallet-timestamp", + "pallet-treasury", + "pallet-utility", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-transactor", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-api", + "sp-consensus-slots", + "sp-core", + "sp-genesis-builder", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonbeam-runtime-common" +version = "0.8.0-dev" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "account", + "cumulus-pallet-xcmp-queue", + "fp-ethereum", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "frame-try-runtime", + "hex-literal 0.3.4", + "impl-trait-for-tuples", + "log", + "moonbeam-xcm-benchmarks", + "nimbus-primitives", + "pallet-asset-manager", + "pallet-assets", + "pallet-async-backing", + "pallet-author-inherent", + "pallet-author-mapping", + "pallet-author-slot-filter", + "pallet-balances", + "pallet-collective", + "pallet-conviction-voting", + "pallet-crowdloan-rewards", + "pallet-ethereum-xcm", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-identity", + "pallet-migrations", + "pallet-moonbeam-lazy-migrations", + "pallet-moonbeam-orbiters", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-precompile-benchmarks", + "pallet-preimage", + "pallet-proxy", + "pallet-randomness", + "pallet-referenda", + "pallet-relay-storage-roots", + "pallet-scheduler", + "pallet-sudo", + "pallet-timestamp", + "pallet-treasury", + "pallet-utility", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-transactor", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-api", + "sp-consensus-slots", + "sp-core", + "sp-genesis-builder", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonbeam-xcm-benchmarks" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-erc20-xcm-bridge", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "staging-xcm", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "moonriver-runtime" +version = "0.8.4" +dependencies = [ + "account", + "async-backing-primitives", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "evm-tracing-events", + "fp-evm", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "moonbeam-core-primitives", + "moonbeam-evm-tracer", + "moonbeam-relay-encoder", + "moonbeam-rpc-primitives-debug", + "moonbeam-rpc-primitives-txpool", + "moonbeam-runtime-common 0.8.0-dev", + "moonbeam-xcm-benchmarks", + "nimbus-primitives", + "num_enum 0.5.11", + "orml-traits", + "orml-xcm-support", + "orml-xtokens", + "pallet-asset-manager", + "pallet-assets", + "pallet-author-inherent", + "pallet-author-mapping", + "pallet-author-slot-filter", + "pallet-balances", + "pallet-collective", + "pallet-conviction-voting", + "pallet-crowdloan-rewards", + "pallet-erc20-xcm-bridge", + "pallet-ethereum", + "pallet-ethereum-xcm", + "pallet-evm", + "pallet-evm-chain-id", + "pallet-evm-precompile-author-mapping", + "pallet-evm-precompile-balances-erc20", + "pallet-evm-precompile-batch", + "pallet-evm-precompile-blake2", + "pallet-evm-precompile-bn128", + "pallet-evm-precompile-call-permit", + "pallet-evm-precompile-collective", + "pallet-evm-precompile-conviction-voting", + "pallet-evm-precompile-crowdloan-rewards", + "pallet-evm-precompile-dispatch", + "pallet-evm-precompile-gmp", + "pallet-evm-precompile-identity", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-preimage", + "pallet-evm-precompile-proxy", + "pallet-evm-precompile-randomness", + "pallet-evm-precompile-referenda", + "pallet-evm-precompile-registry", + "pallet-evm-precompile-relay-encoder", + "pallet-evm-precompile-relay-verifier", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", + "pallet-evm-precompile-xcm-transactor", + "pallet-evm-precompile-xcm-utils", + "pallet-evm-precompile-xtokens", + "pallet-evm-precompileset-assets-erc20", + "pallet-identity", + "pallet-maintenance-mode", + "pallet-message-queue", + "pallet-migrations", + "pallet-moonbeam-lazy-migrations", + "pallet-moonbeam-orbiters", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-precompile-benchmarks", + "pallet-preimage", + "pallet-proxy", + "pallet-proxy-genesis-companion", + "pallet-randomness", + "pallet-referenda", + "pallet-relay-storage-roots", + "pallet-root-testing", + "pallet-scheduler", + "pallet-society", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-transactor", + "parachains-common", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "precompile-utils 0.1.1", + "rlp", + "scale-info", + "serde", + "session-keys-primitives", + "sha3", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-slots", + "sp-core", + "sp-debug-derive", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "sp-weights", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "strum", + "strum_macros", + "substrate-wasm-builder", + "xcm-primitives 0.1.0", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "nalgebra" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "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 = "nimbus-primitives" +version = "0.9.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "async-trait", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.4", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive 0.7.2", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "orml-traits" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.7.2#f90f4de88986571e24ea3c027b9c09a4b732ee1f" +dependencies = [ + "frame-support", + "impl-trait-for-tuples", + "num-traits", + "orml-utilities", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "orml-utilities" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.7.2#f90f4de88986571e24ea3c027b9c09a4b732ee1f" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "orml-xcm-support" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.7.2#f90f4de88986571e24ea3c027b9c09a4b732ee1f" +dependencies = [ + "frame-support", + "orml-traits", + "parity-scale-codec", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "orml-xtokens" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.7.2#f90f4de88986571e24ea3c027b9c09a4b732ee1f" +dependencies = [ + "frame-support", + "frame-system", + "log", + "orml-traits", + "orml-xcm-support", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "pallet-asset-conversion" +version = "10.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-manager" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-asset-rate" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-tx-payment" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-assets" +version = "29.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-async-backing" +version = "0.9.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-consensus-slots", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-author-inherent" +version = "0.9.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-author-mapping" +version = "2.0.5" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "serde", + "session-keys-primitives", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-author-slot-filter" +version = "0.9.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authority-discovery" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authorship" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-babe" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-balances" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-broker" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-collator-selection" +version = "9.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-collective" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-conviction-voting" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-crowdloan-rewards" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/crowdloan-rewards?branch=moonbeam-polkadot-v1.7.2#0803fcfe6abdb966b989fd9ef8c22f007e24a232" +dependencies = [ + "ed25519-dalek 1.0.1", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", + "strum", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-emergency-para-xcm" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "pallet-message-queue", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.0", +] + +[[package]] +name = "pallet-erc20-xcm-bridge" +version = "1.0.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "environmental", + "ethereum-types", + "fp-evm", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "pallet-ethereum" +version = "4.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "environmental", + "ethereum", + "ethereum-types", + "evm", + "fp-consensus", + "fp-ethereum", + "fp-evm", + "fp-rpc", + "fp-storage", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-ethereum-xcm" +version = "1.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-ethereum", + "fp-evm", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "environmental", + "evm", + "fp-account", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "hash-db", + "hex", + "hex-literal 0.4.1", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-chain-id" +version = "1.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "pallet-evm-precompile-author-mapping" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num_enum 0.5.11", + "pallet-author-mapping", + "pallet-evm", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-balances-erc20" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-batch" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "slices", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-blake2" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", +] + +[[package]] +name = "pallet-evm-precompile-bn128" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "sp-core", + "substrate-bn", +] + +[[package]] +name = "pallet-evm-precompile-call-permit" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-collective" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-collective", + "pallet-evm", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompile-conviction-voting" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-conviction-voting", + "pallet-evm", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-crowdloan-rewards" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-crowdloan-rewards", + "pallet-evm", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-dispatch" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "frame-support", + "pallet-evm", + "parity-scale-codec", + "sp-runtime", +] + +[[package]] +name = "pallet-evm-precompile-gmp" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "orml-traits", + "orml-xtokens", + "pallet-evm", + "pallet-xcm", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompile-identity" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "enumflags2", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "pallet-evm", + "pallet-identity", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-modexp" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "num", +] + +[[package]] +name = "pallet-evm-precompile-parachain-staking" +version = "1.0.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-parachain-staking", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-consensus-slots", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-preimage" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-preimage", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-proxy" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-balances", + "pallet-evm", + "pallet-proxy", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-randomness" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "num_enum 0.5.11", + "pallet-evm", + "pallet-randomness", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-referenda" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-referenda", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-registry" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "pallet-evm", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-relay-encoder" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-staking", + "pallet-xcm-transactor", + "parity-scale-codec", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompile-relay-verifier" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "fp-evm", + "frame-support", + "frame-system", + "moonbeam-runtime-common 0.8.0-dev (git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900)", + "pallet-evm", + "pallet-precompile-benchmarks", + "pallet-relay-storage-roots", + "parity-scale-codec", + "precompile-utils 0.1.1", + "scale-info", + "sp-core", + "sp-std", + "storage-proof-primitives", +] + +[[package]] +name = "pallet-evm-precompile-sha3fips" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "tiny-keccak", +] + +[[package]] +name = "pallet-evm-precompile-simple" +version = "2.0.0-dev" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "ripemd", + "sp-io", +] + +[[package]] +name = "pallet-evm-precompile-storage-cleaner" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "pallet-evm", + "parity-scale-codec", + "precompile-utils 0.1.0", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-xcm-transactor" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-evm", + "pallet-xcm-transactor", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompile-xcm-utils" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "num_enum 0.5.11", + "pallet-evm", + "pallet-xcm", + "parity-scale-codec", + "precompile-utils 0.1.1", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompile-xtokens" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "orml-xtokens", + "pallet-evm", + "precompile-utils 0.1.1", + "rustc-hex", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "pallet-evm-precompileset-assets-erc20" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "log", + "num_enum 0.5.11", + "pallet-assets", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils 0.1.1", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-fast-unstake" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-identity" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-maintenance-mode" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.0", +] + +[[package]] +name = "pallet-message-queue" +version = "31.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", +] + +[[package]] +name = "pallet-migrations" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm-primitives 0.1.0", +] + +[[package]] +name = "pallet-moonbeam-lazy-migrations" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "pallet-evm", + "pallet-scheduler", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-moonbeam-orbiters" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-multisig" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-parachain-staking" +version = "3.0.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-slots", + "sp-runtime", + "sp-std", + "substrate-fixed", +] + +[[package]] +name = "pallet-precompile-benchmarks" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-relay-storage-roots", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", + "storage-proof-primitives", +] + +[[package]] +name = "pallet-preimage" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-proxy" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-proxy-genesis-companion" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "frame-support", + "frame-system", + "pallet-proxy", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-randomness" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "schnorrkel", + "serde", + "session-keys-primitives", + "sp-consensus-babe", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-referenda" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-relay-storage-roots" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "log", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-root-testing" +version = "4.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-scheduler" +version = "29.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", +] + +[[package]] +name = "pallet-session" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-society" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-staking" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-staking-reward-fn" +version = "19.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "log", + "sp-arithmetic", +] + +[[package]] +name = "pallet-sudo" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-timestamp" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-storage", + "sp-timestamp", +] + +[[package]] +name = "pallet-transaction-payment" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-transaction-payment-rpc-runtime-api" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "pallet-transaction-payment", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "pallet-treasury" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-utility" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-vesting" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-whitelist" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-xcm" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bounded-collections", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "pallet-xcm-benchmarks" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "pallet-xcm-transactor" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "orml-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-primitives 0.1.1", +] + +[[package]] +name = "parachains-common" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "cumulus-primitives-utility", + "frame-support", + "frame-system", + "log", + "pallet-asset-tx-payment", + "pallet-assets", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-message-queue", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "scale-info", + "sp-consensus-aura", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-executor", + "substrate-wasm-builder", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +dependencies = [ + "proc-macro-crate 2.0.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.0", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + +[[package]] +name = "polkadot-core-primitives" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "polkadot-parachain-primitives" +version = "6.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bounded-collections", + "derive_more", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", + "sp-weights", +] + +[[package]] +name = "polkadot-primitives" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bitvec", + "hex-literal 0.4.1", + "log", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "polkadot-runtime-common" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "libsecp256k1", + "log", + "pallet-asset-rate", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-broker", + "pallet-election-provider-multi-phase", + "pallet-fast-unstake", + "pallet-identity", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-fn", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "slot-range-helper", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "static_assertions", +] + +[[package]] +name = "polkadot-runtime-metrics" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bs58", + "frame-benchmarking", + "parity-scale-codec", + "polkadot-primitives", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "polkadot-runtime-parachains" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bitflags 1.3.2", + "bitvec", + "derive_more", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-broker", + "pallet-message-queue", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-vesting", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-metrics", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rustc-hex", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "staging-xcm", + "staging-xcm-executor", + "static_assertions", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precompile-utils" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "num_enum 0.7.2", + "pallet-evm", + "parity-scale-codec", + "precompile-utils-macro 0.1.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", +] + +[[package]] +name = "precompile-utils" +version = "0.1.1" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "affix", + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "num_enum 0.5.11", + "pallet-evm", + "parity-scale-codec", + "paste", + "precompile-utils-macro 0.1.1", + "sha3", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", + "staging-xcm", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/frontier?branch=moonbeam-polkadot-v1.7.2#5d6bf0c78d1d325d4eea6f9593e9a11da18a1ea8" +dependencies = [ + "case", + "num_enum 0.7.2", + "prettyplease 0.2.17", + "proc-macro2", + "quote", + "sp-core-hashing", + "syn 1.0.109", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.1.1" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "case", + "num_enum 0.5.11", + "prettyplease 0.1.25", + "proc-macro2", + "quote", + "sha3", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn 2.0.58", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-warning" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.14", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "ref-cast" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "ring" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "blake2", + "common", + "fflonk", + "merlin", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.22", +] + +[[package]] +name = "rustix" +version = "0.36.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.11", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" +dependencies = [ + "aead", + "arrayref", + "arrayvec 0.7.4", + "curve25519-dalek 4.1.2", + "getrandom_or_panic", + "merlin", + "rand_core 0.6.4", + "serde_bytes", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "session-keys-primitives" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "async-trait", + "frame-support", + "nimbus-primitives", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simple-mermaid" +version = "0.1.0" +source = "git+https://github.com/kianenigma/simple-mermaid.git?rev=e48b187bcfd5cc75111acd9d241f1bd36604344b#e48b187bcfd5cc75111acd9d241f1bd36604344b" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slices" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2086e458a369cdca838e9f6ed04b4cc2e3ce636d99abb80c9e2eada107749cf" +dependencies = [ + "faster-hex", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "slot-range-helper" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "enumn", + "parity-scale-codec", + "paste", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "sp-api" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "scale-info", + "sp-api-proc-macro", + "sp-core", + "sp-externalities", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "15.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "Inflector", + "blake2", + "expander", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sp-application-crypto" +version = "30.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "23.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "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 = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-block-builder" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-consensus-aura" +version = "0.32.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-babe" +version = "0.32.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.32.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-core" +version = "28.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "array-bytes", + "bandersnatch_vrfs", + "bip39", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde 0.4.0", + "itertools", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot", + "paste", + "primitive-types", + "rand 0.8.5", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-crypto-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tracing", + "w3f-bls", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "15.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "sp-crypto-hashing", +] + +[[package]] +name = "sp-crypto-ec-utils" +version = "0.10.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +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", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "sp-crypto-hashing" +version = "0.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.8", + "sha3", + "twox-hash", +] + +[[package]] +name = "sp-crypto-hashing-proc-macro" +version = "0.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "quote", + "sp-crypto-hashing", + "syn 2.0.58", +] + +[[package]] +name = "sp-debug-derive" +version = "14.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sp-externalities" +version = "0.25.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-genesis-builder" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "serde_json", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-inherents" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "30.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bytes", + "ed25519-dalek 2.1.1", + "libsecp256k1", + "log", + "parity-scale-codec", + "rustversion", + "secp256k1", + "sp-core", + "sp-crypto-hashing", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.34.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "parking_lot", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "11.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "thiserror", + "zstd", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.6.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "sp-npos-elections" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-offchain" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "sp-api", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "sp-panic-handler" +version = "13.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "31.0.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "docify", + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "simple-mermaid", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "24.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +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", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "17.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "Inflector", + "expander", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sp-session" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "sp-staking" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.35.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", + "trie-db", +] + +[[package]] +name = "sp-std" +version = "14.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" + +[[package]] +name = "sp-storage" +version = "19.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "impl-serde 0.4.0", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-timestamp" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "async-trait", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-tracing" +version = "16.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-transaction-pool" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-trie" +version = "29.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "ahash 0.8.11", + "hash-db", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "scale-info", + "schnellru", + "sp-core", + "sp-externalities", + "sp-std", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "29.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "impl-serde 0.4.0", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-crypto-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "13.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sp-wasm-interface" +version = "20.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "bounded-collections", + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ss58-registry" +version = "1.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "staging-parachain-info" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "staging-xcm" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "array-bytes", + "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 = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +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", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", +] + +[[package]] +name = "staging-xcm-executor" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +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", + "sp-weights", + "staging-xcm", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "storage-proof-primitives" +version = "0.1.0" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a7590dc041b9bc2825e52ce5af8416c73dbe9d0654402bfd4b4941938b94d8f" +dependencies = [ + "hmac 0.11.0", + "pbkdf2", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + +[[package]] +name = "substrate-fixed" +version = "0.5.9" +source = "git+https://github.com/encointer/substrate-fixed#879c58bcc6fd676a74315dcd38b598f28708b0b5" +dependencies = [ + "parity-scale-codec", + "scale-info", + "substrate-typenum", +] + +[[package]] +name = "substrate-typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f0091e93c2c75b233ae39424c52cb8a662c0811fb68add149e20e5d7e8a788" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "substrate-wasm-builder" +version = "17.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "build-helper", + "cargo_metadata", + "console", + "filetime", + "parity-wasm", + "sp-maybe-compressed-blob", + "strum", + "tempfile", + "toml", + "walkdir", + "wasm-opt", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix 0.38.32", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.9", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.5", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff28e0f815c2fea41ebddf148e008b077d2faddb026c9555b29696114d602642" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db", +] + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.7", + "rand 0.7.3", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "w3f-bls" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-opt" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" +dependencies = [ + "anyhow", + "libc", + "strum", + "strum_macros", + "tempfile", + "thiserror", + "wasm-opt-cxx-sys", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-cxx-sys" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" +dependencies = [ + "anyhow", + "cxx", + "cxx-build", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-sys" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" +dependencies = [ + "anyhow", + "cc", + "cxx", + "cxx-build", +] + +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.30.4", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object 0.30.4", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line 0.19.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "log", + "object 0.30.4", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "once_cell", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand 0.8.5", + "rustix 0.36.17", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "wide" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xcm-primitives" +version = "0.1.0" +source = "git+https://github.com/Moonsong-Labs/moonkit?branch=moonbeam-polkadot-v1.7.2#bca9e28b36008609860bf7970d5d7b8d361588b3" +dependencies = [ + "sp-runtime", +] + +[[package]] +name = "xcm-primitives" +version = "0.1.1" +source = "git+https://github.com/moonbeam-foundation/moonbeam?rev=runtime-2900#47e691fff3527e7000aabbcdfb8199d1336dbfdc" +dependencies = [ + "cumulus-primitives-core", + "ethereum", + "ethereum-types", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "orml-traits", + "pallet-staking", + "parity-scale-codec", + "polkadot-runtime-common", + "scale-info", + "serde", + "sha3", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "xcm-procedural" +version = "7.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-v1.7.2#2073906e7994ca069f7f68120b05474f7565b8d2" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/tracing/2900/Cargo.toml b/tracing/2900/Cargo.toml new file mode 100644 index 00000000..8926bfb7 --- /dev/null +++ b/tracing/2900/Cargo.toml @@ -0,0 +1,444 @@ +[workspace] +members = [ + "runtime/moonbase", + "runtime/moonbeam", + "runtime/moonriver", +] +resolver = "2" + +[workspace.package] +authors = ["PureStake"] +repository = "https://github.com/PureStake/moonbeam" + +[workspace.dependencies] +# Dependencies are split into 2 groups: wasm and client. +# - "wasm" dependencies requires to be no_std compatible, which often requires +# `default-features = false`. When used in a client-side crate the "std" feature should be enabled +# there if it exists. +# - "client" dependencies are only used in the client, and thus don't need to be no_std compatible. + +# Moonbeam (wasm) +account = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +evm-tracing-events = { path = "shared/primitives/rpc/evm-tracing-events", default-features = false , features = ["runtime-1600"] } +moonbeam-core-primitives = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-primitives-ext = { path = "shared/primitives/ext", default-features = false } +moonbeam-rpc-primitives-debug = { path = "shared/primitives/rpc/debug", default-features = false, features = [ + "runtime-2900", +] } +moonbeam-rpc-primitives-txpool = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +storage-proof-primitives = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } + +moonbeam-evm-tracer = { path = "shared/runtime/evm_tracer", default-features = false } +moonbeam-relay-encoder = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-runtime-common = { path = "runtime/common", default-features = false } + +moonbeam-xcm-benchmarks = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-asset-manager = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-erc20-xcm-bridge = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-ethereum-xcm = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } + +pallet-evm-precompile-author-mapping = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-balances-erc20 = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-batch = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-call-permit = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-collective = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-conviction-voting = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-crowdloan-rewards = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-gmp = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-identity = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-parachain-staking = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-preimage = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-proxy = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-randomness = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-referenda = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-registry = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-relay-encoder = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-relay-verifier = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-xcm-transactor = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-xcm-utils = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompile-xtokens = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-evm-precompileset-assets-erc20 = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-moonbeam-orbiters = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-parachain-staking = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-precompile-benchmarks = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-proxy-genesis-companion = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-xcm-transactor = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +pallet-moonbeam-lazy-migrations = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +precompile-utils = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +xcm-primitives = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } + +pallet-crowdloan-rewards = { git = "https://github.com/moonbeam-foundation/crowdloan-rewards", branch = "moonbeam-polkadot-v1.7.2", default-features = false } + +# Moonbeam (client) +moonbeam-cli = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-cli-opt = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-service = { default-features = false , git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } + +manual-xcm-rpc = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-client-evm-tracing = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-finality-rpc = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-core-debug = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-core-trace = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-core-txpool = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-core-types = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-debug = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-trace = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-rpc-txpool = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } +moonbeam-vrf = { git = "https://github.com/moonbeam-foundation/moonbeam", rev = "runtime-2900" } + +moonbase-runtime = { path = "runtime/moonbase" } +moonbeam-runtime = { path = "runtime/moonbeam" } +moonriver-runtime = { path = "runtime/moonriver" } + +frame-benchmarking = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-executive = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-support = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-system = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-system-benchmarking = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +frame-try-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-assets = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-balances = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-collective = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-conviction-voting = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-identity = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-message-queue = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-multisig = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-preimage = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-proxy = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-referenda = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-root-testing = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-scheduler = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-society = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-staking = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-sudo = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-timestamp = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-transaction-payment = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-treasury = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-utility = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-whitelist = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +parity-scale-codec = { version = "3.2.2", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.0", default-features = false, features = [ + "derive", +] } +sp-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-application-crypto = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-block-builder = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-consensus-babe = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-consensus-slots = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-core = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-debug-derive = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-externalities = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-inherents = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-io = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-keystore = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-offchain = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-runtime-interface = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-session = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-std = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-state-machine = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-transaction-pool = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-trie = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-version = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-weights = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +sp-genesis-builder = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +substrate-fixed = { git = "https://github.com/encointer/substrate-fixed", default-features = false } + +# Substrate (client) +frame-benchmarking-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +pallet-transaction-payment-rpc = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-basic-authorship = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-block-builder = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-chain-spec = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-client-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-client-db = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-consensus = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-consensus-grandpa = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-consensus-manual-seal = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-executor = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-informant = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-network = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-network-common = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-network-sync = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-offchain = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-rpc = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-rpc-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-service = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-sysinfo = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-telemetry = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-tracing = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-transaction-pool = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-transaction-pool-api = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sc-utils = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sp-blockchain = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sp-consensus = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sp-storage = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sp-timestamp = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +sp-wasm-interface = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-build-script-utils = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-frame-rpc-system = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-prometheus-endpoint = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-test-client = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-test-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-test-runtime-client = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +substrate-wasm-builder = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +try-runtime-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } + +# Frontier (wasm) +ethereum = { version = "0.15.0", default-features = false, features = [ + "with-codec", +] } +ethereum-types = { version = "0.14", default-features = false } +evm = { git = "https://github.com/moonbeam-foundation/evm", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +evm-gasometer = { git = "https://github.com/moonbeam-foundation/evm", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +evm-runtime = { git = "https://github.com/moonbeam-foundation/evm", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +fp-ethereum = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +fp-evm = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +fp-rpc = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +fp-self-contained = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-ethereum = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false, features = [ + "forbid-evm-reentrancy", +] } +pallet-evm = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false, features = [ + "forbid-evm-reentrancy", +] } +pallet-evm-chain-id = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-blake2 = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-bn128 = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-dispatch = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-modexp = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-sha3fips = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-simple = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-evm-precompile-storage-cleaner = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", default-features = false } + +# Frontier (client) +fc-consensus = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fc-db = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fc-api = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fc-mapping-sync = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fc-rpc = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2", features = [ + "rpc-binary-search-estimate", +] } +fc-rpc-core = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fc-storage = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fp-consensus = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } +fp-storage = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.7.2" } + +# Cumulus (wasm) +cumulus-pallet-dmp-queue = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false, features = [ + "parameterized-consensus-hook", +] } +cumulus-pallet-xcm = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-primitives-core = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-primitives-parachain-inherent = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-primitives-timestamp = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-primitives-utility = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +cumulus-test-relay-sproof-builder = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +parachain-info = { package = "staging-parachain-info", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +parachains-common = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } + +# Cumulus (client) +cumulus-client-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-collator = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-consensus-common = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-consensus-proposer = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-consensus-relay-chain = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-network = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-service = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-client-parachain-inherent = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-relay-chain-inprocess-interface = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-relay-chain-interface = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-relay-chain-minimal-node = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +cumulus-relay-chain-rpc-interface = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } + +# Polkadot / XCM (wasm) +orml-traits = { git = "https://github.com/moonbeam-foundation/open-runtime-module-library", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +orml-xcm-support = { git = "https://github.com/moonbeam-foundation/open-runtime-module-library", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +orml-xtokens = { git = "https://github.com/moonbeam-foundation/open-runtime-module-library", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-xcm = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-xcm-benchmarks = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +polkadot-core-primitives = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +polkadot-parachain = { package = "polkadot-parachain-primitives", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +polkadot-runtime-common = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +polkadot-runtime-parachains = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +xcm = { package = "staging-xcm", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2", default-features = false } + +# Polkadot / XCM (client) +#kusama-runtime = { package = "staging-kusama-runtime", git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +polkadot-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +polkadot-primitives = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +#polkadot-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +polkadot-service = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +rococo-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +westend-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } +xcm-simulator = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } + +# Moonkit (wasm) +async-backing-primitives = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +moonkit-xcm-primitives = { package = "xcm-primitives", git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +nimbus-primitives = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-async-backing = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-author-inherent = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-author-mapping = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-author-slot-filter = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-emergency-para-xcm = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-maintenance-mode = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-migrations = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-randomness = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +pallet-relay-storage-roots = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } +session-keys-primitives = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2", default-features = false } + +# Moonkit (client) +nimbus-consensus = { git = "https://github.com/Moonsong-Labs/moonkit", branch = "moonbeam-polkadot-v1.7.2" } + +# Other (wasm) +affix = "0.1.2" +async-trait = { version = "0.1.42" } +blake2-rfc = { version = "0.2.18", default-features = false } +derive_more = "0.99" +environmental = { version = "1.1.2", default-features = false } +frame-metadata = { version = "16.0.0", default-features = false, features = [ + "current", +] } +hex = { version = "0.4.3", default-features = false } +hex-literal = { version = "0.3.4" } +impl-serde = { version = "0.3.1", default-features = false } +impl-trait-for-tuples = "0.2.1" +libsecp256k1 = { version = "0.7", default-features = false } +log = { version = "0.4", default-features = false } +num_enum = { version = "0.5.3", default-features = false } +paste = "1.0.6" +rlp = { version = "0.5", default-features = false } +rustc-hex = { version = "2.0.1", default-features = false } +serde = { version = "1.0.101", default-features = false } +sha3 = { version = "0.10", default-features = false } +slices = "0.2.0" +smallvec = "1.8.0" +strum = { version = "0.24", default-features = false, features = ["derive"] } +strum_macros = "0.24" + +# Other (client) + +ansi_term = "0.12.1" +assert_cmd = "2.0.10" +async-io = "1.3" +bip32 = { version = "0.5.1", default-features = false, features = ["bip39"] } +clap = { version = "4.0.9", features = ["derive"] } +clap-num = "=1.1.1" +exit-future = "0.2" +flume = "0.10.9" +futures = { version = "0.3.21" } +jsonrpsee = { version = "0.20.3", default-features = false } +maplit = "1.0.2" +nix = "0.23" +parking_lot = "0.12.0" +primitive-types = "0.12.0" +prometheus = { version = "0.13.0", default-features = false } +rand = "0.7.3" +serde_json = { version = "1.0" } +similar-asserts = "1.1.0" +tempfile = "3.2.0" +tiny-bip39 = { version = "0.8", default-features = false } +schnorrkel = { version = "0.11.4", default-features = false, features = [ + "preaudit_deprecated", +] } +tokio = { version = "1.36" } +tracing = "0.1.34" +tracing-core = "0.1.29" +trie-root = "0.15.2" +url = "2.2.2" + +# The list of dependencies below (which can be both direct and indirect dependencies) are crates +# that are suspected to be CPU-intensive, and that are unlikely to require debugging (as some of +# their debug info might be missing) or to require to be frequently recompiled. We compile these +# dependencies with `opt-level=3` even in "dev" mode in order to make "dev" mode more usable. +# The majority of these crates are cryptographic libraries. +# +# Note that this does **not** affect crates that depend on Moonbeam. In other words, if you add +# a dependency on Moonbeam, you have to copy-paste this list in your own `Cargo.toml` (assuming +# that you want the same list). This list is only relevant when running `cargo build` from within +# the Moonbeam workspace. +# +# If you see an error mentioning "profile package spec ... did not match any packages", it +# probably concerns this list. +# +# This list is ordered alphabetically. +[profile.dev.package] +blake2 = { opt-level = 3 } +blake2b_simd = { opt-level = 3 } +chacha20poly1305 = { opt-level = 3 } +cranelift-codegen = { opt-level = 3 } +cranelift-wasm = { opt-level = 3 } +crc32fast = { opt-level = 3 } +crossbeam-deque = { opt-level = 3 } +crypto-mac = { opt-level = 3 } +curve25519-dalek = { opt-level = 3 } +ed25519-zebra = { opt-level = 3 } +flate2 = { opt-level = 3 } +futures-channel = { opt-level = 3 } +hash-db = { opt-level = 3 } +hashbrown = { opt-level = 3 } +hmac = { opt-level = 3 } +httparse = { opt-level = 3 } +integer-sqrt = { opt-level = 3 } +k256 = { opt-level = 3 } +keccak = { opt-level = 3 } +libm = { opt-level = 3 } +librocksdb-sys = { opt-level = 3 } +libsecp256k1 = { opt-level = 3 } +libz-sys = { opt-level = 3 } +mio = { opt-level = 3 } +nalgebra = { opt-level = 3 } +num-bigint = { opt-level = 3 } +parking_lot = { opt-level = 3 } +parking_lot_core = { opt-level = 3 } +percent-encoding = { opt-level = 3 } +primitive-types = { opt-level = 3 } +ring = { opt-level = 3 } +rustls = { opt-level = 3 } +secp256k1 = { opt-level = 3 } +sha2 = { opt-level = 3 } +sha3 = { opt-level = 3 } +smallvec = { opt-level = 3 } +snow = { opt-level = 3 } +twox-hash = { opt-level = 3 } +uint = { opt-level = 3 } +wasmi = { opt-level = 3 } +x25519-dalek = { opt-level = 3 } +yamux = { opt-level = 3 } +zeroize = { opt-level = 3 } + +# make sure dev builds with backtrace do +# not slow us down +[profile.dev.package.backtrace] +inherits = "release" + +[profile.production] +codegen-units = 1 +incremental = false +inherits = "release" +lto = true + +[profile.release] +debug-assertions = true # Enable debug-assert! for non-production profiles +opt-level = 3 +# Moonbeam runtime requires unwinding. +panic = "unwind" + +[profile.testnet] +debug = 1 # debug symbols are useful for profilers +debug-assertions = true # Enable debug-assert! for non-production profiles +inherits = "release" +overflow-checks = true + +[patch."https://github.com/paritytech/polkadot-sdk"] +sp-crypto-ec-utils = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-v1.7.2" } diff --git a/tracing/2900/runtime/common/Cargo.toml b/tracing/2900/runtime/common/Cargo.toml new file mode 100644 index 00000000..309b6700 --- /dev/null +++ b/tracing/2900/runtime/common/Cargo.toml @@ -0,0 +1,146 @@ +[package] +authors = { workspace = true } +description = "Common code shared between runtimes" +edition = "2021" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +name = "moonbeam-runtime-common" +version = "0.8.0-dev" + +[dependencies] +hex-literal = "0.3.4" +impl-trait-for-tuples = "0.2.1" +log = "0.4" + +# Moonbeam +moonbeam-xcm-benchmarks = { workspace = true } +pallet-asset-manager = { workspace = true } +pallet-author-mapping = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-ethereum-xcm = { workspace = true } +pallet-migrations = { workspace = true } +pallet-moonbeam-lazy-migrations = { workspace = true } +pallet-parachain-staking = { workspace = true } +pallet-precompile-benchmarks = { workspace = true } +pallet-randomness = { workspace = true } +pallet-relay-storage-roots = { workspace = true } +pallet-xcm-transactor = { workspace = true } +precompile-utils = { workspace = true } +xcm-primitives = { workspace = true } + +# Substrate +cumulus-pallet-xcmp-queue = { workspace = true } +frame-benchmarking = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true } +pallet-collective = { workspace = true } +pallet-crowdloan-rewards = { workspace = true } +pallet-identity = { workspace = true } +pallet-moonbeam-orbiters = { workspace = true } +pallet-multisig = { workspace = true } +pallet-preimage = { workspace = true } +pallet-proxy = { workspace = true } +pallet-referenda = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-treasury = { workspace = true } +pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } +pallet-xcm = { workspace = true } +sp-api = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +sp-genesis-builder = { workspace = true } +frame-try-runtime = { workspace = true } + +# Frontier +fp-ethereum = { workspace = true } +fp-evm = { workspace = true } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm-chain-id = { workspace = true } + +# Moonkit +nimbus-primitives = { workspace = true } +pallet-async-backing = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-author-slot-filter = { workspace = true } + +# Polkadot +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +# Parity +parity-scale-codec = { workspace = true } + +account = { workspace = true } + +[features] +std = [ + "fp-ethereum/std", + "fp-evm/std", + "frame-support/std", + "pallet-asset-manager/std", + "pallet-async-backing/std", + "pallet-author-inherent/std", + "pallet-author-mapping/std", + "pallet-ethereum-xcm/std", + "pallet-evm/std", + "pallet-migrations/std", + "pallet-parachain-staking/std", + "pallet-randomness/std", + "pallet-referenda/std", + "pallet-scheduler/std", + "pallet-xcm-transactor/std", + "pallet-moonbeam-lazy-migrations/std", + "pallet-identity/std", + "parity-scale-codec/std", + "precompile-utils/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "sp-genesis-builder/std", + "xcm-executor/std", + "xcm/std", + "account/std", +] +runtime-benchmarks = [ + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "pallet-asset-manager/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-author-mapping/runtime-benchmarks", + "pallet-author-slot-filter/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-crowdloan-rewards/runtime-benchmarks", + "pallet-ethereum-xcm/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-identity/runtime-benchmarks", + "pallet-moonbeam-orbiters/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-parachain-staking/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-randomness/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-treasury/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", + "pallet-xcm-transactor/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "pallet-moonbeam-lazy-migrations/runtime-benchmarks", + "moonbeam-xcm-benchmarks/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime", "pallet-migrations/try-runtime"] diff --git a/tracing/2900/runtime/common/src/apis.rs b/tracing/2900/runtime/common/src/apis.rs new file mode 100644 index 00000000..eef538c2 --- /dev/null +++ b/tracing/2900/runtime/common/src/apis.rs @@ -0,0 +1,939 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_runtime_apis_plus_common { + {$($custom:tt)*} => { + + #[cfg(feature = "evm-tracing")] + // Helper function to replay the "on_idle" hook for all pallets, we need this for + // evm-tracing because some ethereum-xcm transactions might be executed at on_idle. + // + // We need to make sure that we replay on_idle exactly the same way as the + // original block execution, but unfortunatly frame executive diosn't provide a function + // to replay only on_idle, so we need to copy here some code inside frame executive. + fn replay_on_idle() { + use frame_system::pallet_prelude::BlockNumberFor; + use frame_support::traits::OnIdle; + + let weight = >::block_weight(); + let max_weight = < + ::BlockWeights as + frame_support::traits::Get<_> + >::get().max_block; + let remaining_weight = max_weight.saturating_sub(weight.total()); + if remaining_weight.all_gt(Weight::zero()) { + let _ = >>::on_idle( + >::block_number(), + remaining_weight, + ); + } + } + + impl_runtime_apis! { + $($custom)* + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics( + data: sp_inherents::InherentData, + ) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + opaque::SessionKeys::decode_into_raw_public_keys(&encoded) + } + + fn generate_session_keys(seed: Option>) -> Vec { + opaque::SessionKeys::generate(seed) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + frame_support::genesis_builder_helper::create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + frame_support::genesis_builder_helper::build_config::(config) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl moonbeam_rpc_primitives_debug::DebugRuntimeApi for Runtime { + fn trace_transaction( + extrinsics: Vec<::Extrinsic>, + traced_transaction: &EthereumTransaction, + header: &::Header, + ) -> Result< + (), + sp_runtime::DispatchError, + > { + #[cfg(feature = "evm-tracing")] + { + use moonbeam_evm_tracer::tracer::EvmTracer; + use xcm_primitives::{ + ETHEREUM_XCM_TRACING_STORAGE_KEY, + EthereumXcmTracingStatus + }; + use frame_support::storage::unhashed; + use frame_system::pallet_prelude::BlockNumberFor; + + // Tell the CallDispatcher we are tracing a specific Transaction. + unhashed::put::( + ETHEREUM_XCM_TRACING_STORAGE_KEY, + &EthereumXcmTracingStatus::Transaction(traced_transaction.hash()), + ); + + // Initialize block: calls the "on_initialize" hook on every pallet + // in AllPalletsWithSystem. + // After pallet message queue was introduced, this must be done only after + // enabling XCM tracing by setting ETHEREUM_XCM_TRACING_STORAGE_KEY + // in the storage + Executive::initialize_block(header); + + // Apply the a subset of extrinsics: all the substrate-specific or ethereum + // transactions that preceded the requested transaction. + for ext in extrinsics.into_iter() { + let _ = match &ext.0.function { + RuntimeCall::Ethereum(transact { transaction }) => { + if transaction == traced_transaction { + EvmTracer::new().trace(|| Executive::apply_extrinsic(ext)); + return Ok(()); + } else { + Executive::apply_extrinsic(ext) + } + } + _ => Executive::apply_extrinsic(ext), + }; + if let Some(EthereumXcmTracingStatus::TransactionExited) = unhashed::get( + ETHEREUM_XCM_TRACING_STORAGE_KEY + ) { + return Ok(()); + } + } + + if let Some(EthereumXcmTracingStatus::Transaction(_)) = unhashed::get( + ETHEREUM_XCM_TRACING_STORAGE_KEY + ) { + // If the transaction was not found, it might be + // an eth-xcm transaction that was executed at on_idle + replay_on_idle(); + } + + if let Some(EthereumXcmTracingStatus::TransactionExited) = unhashed::get( + ETHEREUM_XCM_TRACING_STORAGE_KEY + ) { + // The transaction was found + Ok(()) + } else { + // The transaction was not-found + Err(sp_runtime::DispatchError::Other( + "Failed to find Ethereum transaction among the extrinsics.", + )) + } + } + #[cfg(not(feature = "evm-tracing"))] + Err(sp_runtime::DispatchError::Other( + "Missing `evm-tracing` compile time feature flag.", + )) + } + + fn trace_block( + extrinsics: Vec<::Extrinsic>, + known_transactions: Vec, + header: &::Header, + ) -> Result< + (), + sp_runtime::DispatchError, + > { + #[cfg(feature = "evm-tracing")] + { + use moonbeam_evm_tracer::tracer::EvmTracer; + use frame_system::pallet_prelude::BlockNumberFor; + use xcm_primitives::EthereumXcmTracingStatus; + + // Tell the CallDispatcher we are tracing a full Block. + frame_support::storage::unhashed::put::( + xcm_primitives::ETHEREUM_XCM_TRACING_STORAGE_KEY, + &EthereumXcmTracingStatus::Block, + ); + + let mut config = ::config().clone(); + config.estimate = true; + + // Initialize block: calls the "on_initialize" hook on every pallet + // in AllPalletsWithSystem. + // After pallet message queue was introduced, this must be done only after + // enabling XCM tracing by setting ETHEREUM_XCM_TRACING_STORAGE_KEY + // in the storage + Executive::initialize_block(header); + + // Apply all extrinsics. Ethereum extrinsics are traced. + for ext in extrinsics.into_iter() { + match &ext.0.function { + RuntimeCall::Ethereum(transact { transaction }) => { + if known_transactions.contains(&transaction.hash()) { + // Each known extrinsic is a new call stack. + EvmTracer::emit_new(); + EvmTracer::new().trace(|| Executive::apply_extrinsic(ext)); + } else { + let _ = Executive::apply_extrinsic(ext); + } + } + _ => { + let _ = Executive::apply_extrinsic(ext); + } + }; + } + + // Replay on_idle + // Some XCM messages with eth-xcm transaction might be executed at on_idle + replay_on_idle(); + + Ok(()) + } + #[cfg(not(feature = "evm-tracing"))] + Err(sp_runtime::DispatchError::Other( + "Missing `evm-tracing` compile time feature flag.", + )) + } + } + + impl moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi for Runtime { + fn extrinsic_filter( + xts_ready: Vec<::Extrinsic>, + xts_future: Vec<::Extrinsic>, + ) -> TxPoolResponse { + TxPoolResponse { + ready: xts_ready + .into_iter() + .filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None, + }) + .collect(), + future: xts_future + .into_iter() + .filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None, + }) + .collect(), + } + } + } + + impl fp_rpc::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + ::ChainId::get() + } + + fn account_basic(address: H160) -> EVMAccount { + let (account, _) = EVM::account_basic(&address); + account + } + + fn gas_price() -> U256 { + let (gas_price, _) = ::FeeCalculator::min_gas_price(); + gas_price + } + + fn account_code_at(address: H160) -> Vec { + pallet_evm::AccountCodes::::get(address) + } + + fn author() -> H160 { + >::find_author() + } + + fn storage_at(address: H160, index: U256) -> H256 { + let mut tmp = [0u8; 32]; + index.to_big_endian(&mut tmp); + pallet_evm::AccountStorages::::get(address, H256::from_slice(&tmp[..])) + } + + fn call( + from: H160, + to: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + access_list: Option)>>, + ) -> Result { + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + let is_transactional = false; + let validate = true; + + // Estimated encoded transaction size must be based on the heaviest transaction + // type (EIP1559Transaction) to be compatible with all transaction types. + let mut estimated_transaction_len = data.len() + + // pallet ethereum index: 1 + // transact call index: 1 + // Transaction enum variant: 1 + // chain_id 8 bytes + // nonce: 32 + // max_priority_fee_per_gas: 32 + // max_fee_per_gas: 32 + // gas_limit: 32 + // action: 21 (enum varianrt + call address) + // value: 32 + // access_list: 1 (empty vec size) + // 65 bytes signature + 258; + + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + let gas_limit = gas_limit.min(u64::MAX.into()).low_u64(); + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; + + ::Runner::call( + from, + to, + data, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + is_transactional, + validate, + weight_limit, + proof_size_base_cost, + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.error.into()) + } + + fn create( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + access_list: Option)>>, + ) -> Result { + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + let is_transactional = false; + let validate = true; + + let mut estimated_transaction_len = data.len() + + // from: 20 + // value: 32 + // gas_limit: 32 + // nonce: 32 + // 1 byte transaction action variant + // chain id 8 bytes + // 65 bytes signature + 190; + + if max_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if max_priority_fee_per_gas.is_some() { + estimated_transaction_len += 32; + } + if access_list.is_some() { + estimated_transaction_len += access_list.encoded_size(); + } + + let gas_limit = if gas_limit > U256::from(u64::MAX) { + u64::MAX + } else { + gas_limit.low_u64() + }; + let without_base_extrinsic_weight = true; + + let (weight_limit, proof_size_base_cost) = + match ::GasWeightMapping::gas_to_weight( + gas_limit, + without_base_extrinsic_weight + ) { + weight_limit if weight_limit.proof_size() > 0 => { + (Some(weight_limit), Some(estimated_transaction_len as u64)) + } + _ => (None, None), + }; + + #[allow(clippy::or_fun_call)] // suggestion not helpful here + ::Runner::create( + from, + data, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list.unwrap_or_default(), + is_transactional, + validate, + weight_limit, + proof_size_base_cost, + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.error.into()) + } + + fn current_transaction_statuses() -> Option> { + pallet_ethereum::CurrentTransactionStatuses::::get() + } + + fn current_block() -> Option { + pallet_ethereum::CurrentBlock::::get() + } + + fn current_receipts() -> Option> { + pallet_ethereum::CurrentReceipts::::get() + } + + fn current_all() -> ( + Option, + Option>, + Option>, + ) { + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentReceipts::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get(), + ) + } + + fn extrinsic_filter( + xts: Vec<::Extrinsic>, + ) -> Vec { + xts.into_iter().filter_map(|xt| match xt.0.function { + RuntimeCall::Ethereum(transact { transaction }) => Some(transaction), + _ => None + }).collect::>() + } + + fn elasticity() -> Option { + None + } + + fn gas_limit_multiplier_support() {} + + fn pending_block( + xts: Vec<::Extrinsic> + ) -> ( + Option, Option> + ) { + for ext in xts.into_iter() { + let _ = Executive::apply_extrinsic(ext); + } + + Ethereum::on_finalize(System::block_number() + 1); + + ( + pallet_ethereum::CurrentBlock::::get(), + pallet_ethereum::CurrentTransactionStatuses::::get() + ) + } + + fn initialize_pending_block(header: &::Header) { + pallet_randomness::vrf::using_fake_vrf(|| Executive::initialize_block(header)) + } + } + + impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { + fn convert_transaction( + transaction: pallet_ethereum::Transaction + ) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl nimbus_primitives::NimbusApi for Runtime { + fn can_author( + author: nimbus_primitives::NimbusId, + slot: u32, + parent_header: &::Header + ) -> bool { + use pallet_parachain_staking::Config as PalletParachainStakingConfig; + + let block_number = parent_header.number + 1; + + // The Moonbeam runtimes use an entropy source that needs to do some accounting + // work during block initialization. Therefore we initialize it here to match + // the state it will be in when the next block is being executed. + use frame_support::traits::OnInitialize; + System::initialize( + &block_number, + &parent_header.hash(), + &parent_header.digest, + ); + + // Because the staking solution calculates the next staking set at the beginning + // of the first block in the new round, the only way to accurately predict the + // authors is to compute the selection during prediction. + if pallet_parachain_staking::Pallet::::round() + .should_update(block_number) { + // get author account id + use nimbus_primitives::AccountLookup; + let author_account_id = if let Some(account) = + pallet_author_mapping::Pallet::::lookup_account(&author) { + account + } else { + // return false if author mapping not registered like in can_author impl + return false + }; + let candidates = pallet_parachain_staking::Pallet::::compute_top_candidates(); + if candidates.is_empty() { + // If there are zero selected candidates, we use the same eligibility + // as the previous round + return AuthorInherent::can_author(&author, &slot); + } + + // predict eligibility post-selection by computing selection results now + let (eligible, _) = + pallet_author_slot_filter::compute_pseudo_random_subset::( + candidates, + &slot + ); + eligible.contains(&author_account_id) + } else { + AuthorInherent::can_author(&author, &slot) + } + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info( + header: &::Header + ) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl session_keys_primitives::VrfApi for Runtime { + fn get_last_vrf_output() -> Option<::Hash> { + // TODO: remove in future runtime upgrade along with storage item + if pallet_randomness::Pallet::::not_first_block().is_none() { + return None; + } + pallet_randomness::Pallet::::local_vrf_output() + } + fn vrf_key_lookup( + nimbus_id: nimbus_primitives::NimbusId + ) -> Option { + use session_keys_primitives::KeysLookup; + AuthorMapping::lookup_keys(&nimbus_id) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList}; + use moonbeam_xcm_benchmarks::generic::benchmarking as MoonbeamXcmBenchmarks; + use frame_support::traits::StorageInfoTrait; + use MoonbeamXcmBenchmarks::XcmGenericBenchmarks as MoonbeamXcmGenericBench; + + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + + return (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig, + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{add_benchmark, BenchmarkBatch, Benchmarking}; + use frame_support::traits::TrackedStorageKey; + use cumulus_primitives_core::ParaId; + + use xcm::latest::prelude::{ + GeneralIndex, Junction, Junctions, Location, Response, NetworkId, AssetId, + Assets as XcmAssets, Fungible, Asset, ParentThen, Parachain, Parent + }; + use frame_benchmarking::BenchmarkError; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime {} + + impl moonbeam_xcm_benchmarks::Config for Runtime {} + impl moonbeam_xcm_benchmarks::generic::Config for Runtime {} + + use pallet_asset_manager::Config as PalletAssetManagerConfig; + + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + type ExistentialDeposit = ConstU128<0>; + parameter_types! { + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + + impl pallet_xcm::benchmarking::Config for Runtime { + fn reachable_dest() -> Option { + Some(Parent.into()) + } + + fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { + // Relay/native token can be teleported between AH and Relay. + Some(( + Asset { + fun: Fungible(ExistentialDeposit::get()), + id: AssetId(Parent.into()) + }, + Parent.into(), + )) + } + + fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { + Some(( + Asset { + fun: Fungible(ExistentialDeposit::get()), + id: AssetId(Parent.into()) + }, + // AH can reserve transfer native token to some random parachain. + ParentThen(Parachain(RandomParaId::get().into()).into()).into(), + )) + } + } + + impl pallet_xcm_benchmarks::Config for Runtime { + type XcmConfig = xcm_config::XcmExecutorConfig; + type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = (); + fn valid_destination() -> Result { + Ok(Location::parent()) + } + fn worst_case_holding(_depositable_count: u32) -> XcmAssets { + // 100 fungibles + const HOLDING_FUNGIBLES: u32 = 100; + let fungibles_amount: u128 = 100; + let assets = (0..HOLDING_FUNGIBLES).map(|i| { + let location: Location = GeneralIndex(i as u128).into(); + Asset { + id: AssetId(location), + fun: Fungible(fungibles_amount * i as u128), + } + .into() + }) + .chain( + core::iter::once( + Asset { + id: AssetId(Location::parent()), + fun: Fungible(u128::MAX) + } + ) + ) + .collect::>(); + + + for (i, asset) in assets.iter().enumerate() { + if let Asset { + id: AssetId(location), + fun: Fungible(_) + } = asset { + ::AssetId, + ::ForeignAssetType> + >::set_asset_type_asset_id( + location.clone().try_into().expect("convert to v3"), + i as u128 + ); + // set 1-1 + ::ForeignAssetType> + >::set_units_per_second( + location.clone().try_into().expect("convert to v3"), + 1_000_000_000_000u128 + ); + } + } + assets.into() + } + } + + impl pallet_xcm_benchmarks::generic::Config for Runtime { + type RuntimeCall = RuntimeCall; + type TransactAsset = Balances; + + fn worst_case_response() -> (u64, Response) { + (0u64, Response::Version(Default::default())) + } + + fn worst_case_asset_exchange() + -> Result<(XcmAssets, XcmAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result<(Location, Junction), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn export_message_origin_and_destination() + -> Result<(Location, NetworkId, Junctions), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn transact_origin_and_runtime_call() + -> Result<(Location, RuntimeCall), BenchmarkError> { + Ok((Location::parent(), frame_system::Call::remark_with_event { + remark: vec![] + }.into())) + } + + fn subscribe_origin() -> Result { + Ok(Location::parent()) + } + + fn claimable_asset() + -> Result<(Location, Location, XcmAssets), BenchmarkError> { + let origin = Location::parent(); + let assets: XcmAssets = (AssetId(Location::parent()), 1_000u128) + .into(); + let ticket = Location { parents: 0, interior: [].into() /* Here */ }; + Ok((origin, ticket, assets)) + } + + fn fee_asset() -> Result { + Err(BenchmarkError::Skip) + } + + fn unlockable_asset() + -> Result<(Location, Location, Asset), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn alias_origin() -> Result<(Location, Location), BenchmarkError> { + Err(BenchmarkError::Skip) + } + } + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "02a5c1b19ab7a04f536c519aca4983ac") + .to_vec().into(), + // Total Issuance + hex_literal::hex!( "c2261276cc9d1f8598ea4b6a74b15c2f" + "57c875e4cff74148e4628f264b974c80") + .to_vec().into(), + // Execution Phase + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "ff553b5a9862a516939d82b3d3d8661a") + .to_vec().into(), + // Event Count + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "0a98fdbe9ce6c55837576c60c7af3850") + .to_vec().into(), + // System Events + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "80d41e5e16056765bc8461851072c9d7") + .to_vec().into(), + // System BlockWeight + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "34abf5cb34d6244378cddbf18e849d96") + .to_vec().into(), + // ParachainStaking Round + hex_literal::hex!( "a686a3043d0adcf2fa655e57bc595a78" + "13792e785168f725b60e2969c7fc2552") + .to_vec().into(), + // Treasury Account (py/trsry) + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "b99d880ec681799c0cf30e8886371da9" + "7be2919ac397ba499ea5e57132180ec6" + "6d6f646c70792f747273727900000000" + "00000000" + ).to_vec().into(), + // Treasury Account (pc/trsry) + hex_literal::hex!( "26aa394eea5630e07c48ae0c9558cef7" + "b99d880ec681799c0cf30e8886371da9" + "7be2919ac397ba499ea5e57132180ec6" + "6d6f646c70632f747273727900000000" + "00000000" + ).to_vec().into(), + // ParachainInfo ParachainId + hex_literal::hex!( "0d715f2646c8f85767b5d2764bb27826" + "04a74d81251e398fd8a0a4d55023bb3f") + .to_vec().into(), + + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmarks!(params, batches); + + if batches.is_empty() { + return Err("Benchmark not found for this pallet.".into()); + } + Ok(batches) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + log::info!("try-runtime::on_runtime_upgrade()"); + // NOTE: intentional expect: we don't want to propagate the error backwards, + // and want to have a backtrace here. If any of the pre/post migration checks + // fail, we shall stop right here and right now. + let weight = Executive::try_runtime_upgrade(checks) + .expect("runtime upgrade logic *must* be infallible"); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect + ) -> Weight { + log::info!( + "try-runtime: executing block {:?} / root checks: {:?} / try-state-select: {:?}", + block.header.hash(), + state_root_check, + select, + ); + // NOTE: intentional unwrap: we don't want to propagate the error backwards, + // and want to have a backtrace here. + Executive::try_execute_block( + block, + state_root_check, + signature_check, + select, + ).expect("execute-block failed") + } + } + } + }; +} diff --git a/tracing/2900/runtime/common/src/benchmarking.rs b/tracing/2900/runtime/common/src/benchmarking.rs new file mode 100644 index 00000000..b6985b5b --- /dev/null +++ b/tracing/2900/runtime/common/src/benchmarking.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2023 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use account::AccountId20; +use pallet_treasury::ArgumentsFactory; + +pub struct BenchmarkHelper; + +impl ArgumentsFactory<(), AccountId20> for BenchmarkHelper { + fn create_asset_kind(_seed: u32) -> () { + () + } + + fn create_beneficiary(seed: [u8; 32]) -> AccountId20 { + AccountId20::from(seed) + } +} diff --git a/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call.rs b/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call.rs new file mode 100644 index 00000000..112c457c --- /dev/null +++ b/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_moonbeam_xcm_call { + {} => { + + pub struct MoonbeamCall; + impl CallDispatcher for MoonbeamCall { + fn dispatch( + call: RuntimeCall, + origin: RuntimeOrigin, + ) -> Result< + PostDispatchInfoOf, + DispatchErrorWithPostInfo> + > { + if let Ok(raw_origin) = TryInto::>::try_into(origin.clone().caller) { + match (call.clone(), raw_origin) { + ( + RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { .. }) | + RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact_through_proxy { .. }), + RawOrigin::Signed(account_id) + ) => { + return RuntimeCall::dispatch( + call, + pallet_ethereum_xcm::Origin::XcmEthereumTransaction( + account_id.into() + ).into() + ); + }, + _ => {} + } + } + RuntimeCall::dispatch(call, origin) + } + } + } +} diff --git a/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs b/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs new file mode 100644 index 00000000..08d2b06f --- /dev/null +++ b/tracing/2900/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs @@ -0,0 +1,118 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_moonbeam_xcm_call_tracing { + {} => { + + type CallResult = + Result< + PostDispatchInfoOf, + DispatchErrorWithPostInfo> + >; + + pub struct MoonbeamCall; + impl CallDispatcher for MoonbeamCall { + fn dispatch( + call: RuntimeCall, + origin: RuntimeOrigin, + ) -> CallResult { + if let Ok(raw_origin) = TryInto::>::try_into(origin.clone().caller) { + match (call.clone(), raw_origin) { + ( + RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }) | + RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact_through_proxy { + xcm_transaction, .. + }), + RawOrigin::Signed(account_id) + ) => { + use crate::EthereumXcm; + use moonbeam_evm_tracer::tracer::EvmTracer; + use xcm_primitives::{ + XcmToEthereum, + EthereumXcmTracingStatus, + ETHEREUM_XCM_TRACING_STORAGE_KEY + }; + use frame_support::storage::unhashed; + use frame_support::traits::Get; + + let dispatch_call = || { + RuntimeCall::dispatch( + call, + pallet_ethereum_xcm::Origin::XcmEthereumTransaction( + account_id.into() + ).into() + ) + }; + + return match unhashed::get( + ETHEREUM_XCM_TRACING_STORAGE_KEY + ) { + // This runtime instance is used for tracing. + Some(tracing_status) => match tracing_status { + // Tracing a block, all calls are done using environmental. + EthereumXcmTracingStatus::Block => { + // Each known extrinsic is a new call stack. + EvmTracer::emit_new(); + let mut res: Option = None; + EvmTracer::new().trace(|| { + res = Some(dispatch_call()); + }); + res.expect("Invalid dispatch result") + }, + // Tracing a transaction, the one matching the trace request + // is done using environmental, the rest dispatched normally. + EthereumXcmTracingStatus::Transaction(traced_transaction_hash) => { + let transaction_hash = xcm_transaction.into_transaction_v2( + EthereumXcm::nonce(), + ::ChainId::get() + ) + .expect("Invalid transaction conversion") + .hash(); + if transaction_hash == traced_transaction_hash { + let mut res: Option = None; + EvmTracer::new().trace(|| { + res = Some(dispatch_call()); + }); + // Tracing runtime work is done, just signal instance exit. + unhashed::put::( + xcm_primitives::ETHEREUM_XCM_TRACING_STORAGE_KEY, + &EthereumXcmTracingStatus::TransactionExited, + ); + return res.expect("Invalid dispatch result"); + } + dispatch_call() + }, + // Tracing a transaction that has already been found and + // executed. There's no need to dispatch the rest of the + // calls. + EthereumXcmTracingStatus::TransactionExited => Ok(crate::PostDispatchInfo { + actual_weight: None, + pays_fee: frame_support::pallet_prelude::Pays::No, + }), + }, + // This runtime instance is importing a block. + None => dispatch_call() + }; + }, + _ => {} + } + } + RuntimeCall::dispatch(call, origin) + } + } + } +} diff --git a/tracing/2900/runtime/common/src/impl_on_charge_evm_transaction.rs b/tracing/2900/runtime/common/src/impl_on_charge_evm_transaction.rs new file mode 100644 index 00000000..619b11c8 --- /dev/null +++ b/tracing/2900/runtime/common/src/impl_on_charge_evm_transaction.rs @@ -0,0 +1,62 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_on_charge_evm_transaction { + {} => { + type FungibleAccountId = ::AccountId; + + type BalanceFor = + <::Currency as Inspect>>::Balance; + + pub struct OnChargeEVMTransaction(sp_std::marker::PhantomData); + + impl OnChargeEVMTransactionT for OnChargeEVMTransaction + where + T: pallet_evm::Config, + T::Currency: Balanced, + OU: OnUnbalanced>, + U256: UniqueSaturatedInto> + { + type LiquidityInfo = Option>; + + fn withdraw_fee(who: &H160, fee: U256) -> Result> { + EVMFungibleAdapter::<::Currency, ()>::withdraw_fee(who, fee) + } + + fn can_withdraw(who: &H160, amount: U256) -> Result<(), pallet_evm::Error> { + EVMFungibleAdapter::<::Currency, ()>::can_withdraw(who, amount) + } + + fn correct_and_deposit_fee( + who: &H160, + corrected_fee: U256, + base_fee: U256, + already_withdrawn: Self::LiquidityInfo, + ) -> Result> { + ::Currency, OU> as OnChargeEVMTransactionT< + T, + >>::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn) + } + + fn pay_priority_fee(tip: Self::LiquidityInfo) { + if let Some(tip) = tip { + OU::on_unbalanced(tip); + } + } + } + } +} diff --git a/tracing/2900/runtime/common/src/impl_self_contained_call.rs b/tracing/2900/runtime/common/src/impl_self_contained_call.rs new file mode 100644 index 00000000..0dbce052 --- /dev/null +++ b/tracing/2900/runtime/common/src/impl_self_contained_call.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_self_contained_call { + {} => { + impl fp_self_contained::SelfContainedCall for RuntimeCall { + type SignedInfo = H160; + + fn is_self_contained(&self) -> bool { + match self { + RuntimeCall::Ethereum(call) => call.is_self_contained(), + _ => false, + } + } + + fn check_self_contained( + &self + ) -> Option> { + match self { + RuntimeCall::Ethereum(call) => call.check_self_contained(), + _ => None, + } + } + + fn validate_self_contained( + &self, + signed_info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option { + match self { + RuntimeCall::Ethereum(call) => call.validate_self_contained(signed_info, dispatch_info, len), + _ => None, + } + } + + fn pre_dispatch_self_contained( + &self, + info: &Self::SignedInfo, + dispatch_info: &DispatchInfoOf, + len: usize, + ) -> Option> { + match self { + RuntimeCall::Ethereum(call) => call.pre_dispatch_self_contained(info, dispatch_info, len), + _ => None, + } + } + + fn apply_self_contained( + self, + info: Self::SignedInfo, + ) -> Option>> { + match self { + call @ RuntimeCall::Ethereum(pallet_ethereum::Call::transact { .. }) => Some( + call.dispatch(RuntimeOrigin::from( + pallet_ethereum::RawOrigin::EthereumTransaction(info) + )) + ), + _ => None, + } + } + } + } +} diff --git a/tracing/2900/runtime/common/src/impl_xcm_evm_runner.rs b/tracing/2900/runtime/common/src/impl_xcm_evm_runner.rs new file mode 100644 index 00000000..7eebe42d --- /dev/null +++ b/tracing/2900/runtime/common/src/impl_xcm_evm_runner.rs @@ -0,0 +1,188 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#[macro_export] +macro_rules! impl_evm_runner_precompile_or_eth_xcm { + {} => { + use fp_evm::{CallInfo, CallOrCreateInfo, Context, Transfer}; + use frame_support::dispatch::CallableCallFor; + use pallet_evm::{Runner, RunnerError}; + use precompile_utils::{prelude::*, evm::handle::with_precompile_handle}; + use sp_core::U256; + use sp_runtime::DispatchError; + use sp_std::vec::Vec; + use xcm_primitives::{EthereumXcmTransaction, EthereumXcmTransactionV2}; + + pub struct EvmRunnerPrecompileOrEthXcm( + core::marker::PhantomData<(CallDispatcher, Runtime)>, + ); + + impl Runner + for EvmRunnerPrecompileOrEthXcm + where + CallDispatcher: xcm_executor::traits::CallDispatcher, + Runtime: pallet_evm::Config + pallet_ethereum_xcm::Config, + Runtime::RuntimeOrigin: From, + { + type Error = DispatchError; + + fn call( + source: H160, + target: H160, + input: Vec, + value: U256, + gas_limit: u64, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + access_list: Vec<(H160, Vec)>, + _is_transactional: bool, + _validate: bool, + _weight_limit: Option, + _transaction_len: Option, + _config: &fp_evm::Config, + ) -> Result> { + // The `with_precompile_handle` function will execute the closure (and return the + // result in a Some) if and only if there is an available EVM context. Otherwise, + // it will return None. + if let Some((exit_reason, value)) = with_precompile_handle(|precompile_handle| { + let transfer = if value.is_zero() { + None + } else { + Some(Transfer { + source, + target, + value, + }) + }; + + precompile_handle.call( + target, + transfer, + input.clone(), + Some(gas_limit), + false, + &Context { + address: target, + caller: source, + apparent_value: value, + }, + ) + }) { + Ok(CallInfo { + exit_reason, + value, + used_gas: fp_evm::UsedGas { + standard: U256::default(), + effective: U256::default(), + }, + logs: Default::default(), + weight_info: None, + }) + } else { + let xcm_transaction = EthereumXcmTransaction::V2(EthereumXcmTransactionV2 { + gas_limit: gas_limit.into(), + action: pallet_ethereum_xcm::TransactionAction::Call(target), + value, + input: input.try_into().map_err(|_| RunnerError { + error: DispatchError::Exhausted, + weight: Default::default(), + })?, + access_list: Some(access_list), + }); + + let mut execution_info: Option = None; + pallet_ethereum::catch_exec_info(&mut execution_info, || { + CallDispatcher::dispatch( + RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }), + RawOrigin::Signed(source.into()).into(), + ) + .map_err(|DispatchErrorWithPostInfo { error, .. }| RunnerError { + error, + weight: Default::default(), + }) + })?; + + if let Some(CallOrCreateInfo::Call(call_info))= execution_info { + Ok(call_info) + } else { + // `execution_info` must have been filled in + Err(RunnerError { + error: DispatchError::Unavailable, + weight: Default::default(), + }) + } + } + } + + fn create( + _source: H160, + _init: Vec, + _value: U256, + _gas_limit: u64, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + _access_list: Vec<(H160, Vec)>, + _is_transactional: bool, + _validate: bool, + _weight_limit: Option, + _transaction_len: Option, + _config: &fp_evm::Config, + ) -> Result> { + unimplemented!() + } + + fn create2( + _source: H160, + _init: Vec, + _salt: H256, + _value: U256, + _gas_limit: u64, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + _access_list: Vec<(H160, Vec)>, + _is_transactional: bool, + _validate: bool, + _weight_limit: Option, + _transaction_len: Option, + _config: &fp_evm::Config, + ) -> Result> { + unimplemented!() + } + + fn validate( + _source: H160, + _target: Option, + _input: Vec, + _value: U256, + _gas_limit: u64, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + _access_list: Vec<(H160, Vec)>, + _is_transactional: bool, + _weight_limit: Option, + _transaction_len: Option, + _evm_config: &fp_evm::Config, + ) -> Result<(), RunnerError> { + unimplemented!() + } + } + + } +} diff --git a/tracing/2900/runtime/common/src/lib.rs b/tracing/2900/runtime/common/src/lib.rs new file mode 100644 index 00000000..a905eb57 --- /dev/null +++ b/tracing/2900/runtime/common/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +mod apis; +mod impl_moonbeam_xcm_call; +mod impl_moonbeam_xcm_call_tracing; +mod impl_on_charge_evm_transaction; +mod impl_self_contained_call; +mod impl_xcm_evm_runner; +pub mod migrations; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; diff --git a/tracing/2900/runtime/common/src/migrations.rs b/tracing/2900/runtime/common/src/migrations.rs new file mode 100644 index 00000000..b9bb8b12 --- /dev/null +++ b/tracing/2900/runtime/common/src/migrations.rs @@ -0,0 +1,369 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! # Migrations +//! +//! This module acts as a registry where each migration is defined. Each migration should implement +//! the "Migration" trait declared in the pallet-migrations crate. + +use frame_support::{ + parameter_types, + storage::unhashed::{clear_prefix, contains_prefixed_key}, + traits::OnRuntimeUpgrade, + weights::Weight, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_author_slot_filter::Config as AuthorSlotFilterConfig; +use pallet_migrations::{GetMigrations, Migration}; +use pallet_parachain_staking::migrations::MigrateRoundWithFirstSlot; +use sp_core::Get; +use sp_std::{marker::PhantomData, prelude::*, vec}; + +pub struct PalletStakingRoundMigration(PhantomData); +impl Migration for PalletStakingRoundMigration +where + Runtime: pallet_parachain_staking::Config, + BlockNumberFor: Into, +{ + fn friendly_name(&self) -> &str { + "MM_MigrateRoundWithFirstSlot" + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade(&self) -> Result, sp_runtime::DispatchError> { + MigrateRoundWithFirstSlot::::pre_upgrade() + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + MigrateRoundWithFirstSlot::::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(&self, state: Vec) -> Result<(), sp_runtime::DispatchError> { + MigrateRoundWithFirstSlot::::post_upgrade(state) + } +} + +parameter_types! { + pub const DemocracyPalletName: &'static str = "Democracy"; +} + +pub struct RemovePalletDemocracy(pub PhantomData); +impl Migration for RemovePalletDemocracy +where + Runtime: frame_system::Config, +{ + fn friendly_name(&self) -> &str { + "MM_RemoveDemocracyPallet" + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + log::info!("Removing pallet democracy"); + + // Democracy: f2794c22e353e9a839f12faab03a911b + // VotingOf: e470c6afbbbc027eb288ade7595953c2 + let prefix = + hex_literal::hex!("f2794c22e353e9a839f12faab03a911be470c6afbbbc027eb288ade7595953c2"); + if contains_prefixed_key(&prefix) { + // PoV failsafe: do not execute the migration if there are VotingOf keys + // that have not been cleaned up + log::info!("Found keys for Democracy.VotingOf pre-removal - skipping migration",); + return Weight::zero(); + }; + frame_support::migrations::RemovePallet::< + DemocracyPalletName, + ::DbWeight, + >::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade(&self) -> Result, sp_runtime::DispatchError> { + let _ = frame_support::migrations::RemovePallet::< + DemocracyPalletName, + ::DbWeight, + >::pre_upgrade(); + + Ok(vec![]) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(&self, _state: Vec) -> Result<(), sp_runtime::DispatchError> { + let _ = frame_support::migrations::RemovePallet::< + DemocracyPalletName, + ::DbWeight, + >::post_upgrade(_state); + + Ok(()) + } +} + +pub struct RemoveCollectivesAddresses(pub PhantomData); +impl Migration for RemoveCollectivesAddresses +where + Runtime: frame_system::Config, +{ + fn friendly_name(&self) -> &str { + "MM_RemoveCollectivesAddresses" + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + log::info!("Removing gov v1 collective addresses storage"); + + // CouncilCollective: d59b9be6f0a7187ca6630c1d0a9bb045 + let prefix = hex_literal::hex!("d59b9be6f0a7187ca6630c1d0a9bb045"); + let result = clear_prefix(&prefix, Some(10), None); + log::info!("Removed {} CouncilCollective keys", result.unique); + + // record how many records have been deleted + let mut writes = result.unique; + + // TechCommitteeCollective: a06bfb73a86f8f98d5c5dc14e20e8a03 + let prefix = hex_literal::hex!("a06bfb73a86f8f98d5c5dc14e20e8a03"); + let result = clear_prefix(&prefix, Some(10), None); + log::info!("Removed {} TechCommitteeCollective", result.unique); + + // Account for weight + writes = writes.saturating_add(result.unique); + let reads = writes.saturating_add(2); + + let w = Runtime::DbWeight::get(); + w.reads_writes(reads.into(), writes.into()) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade(&self) -> Result, sp_runtime::DispatchError> { + // CouncilCollective: d59b9be6f0a7187ca6630c1d0a9bb045 + let prefix = hex_literal::hex!("d59b9be6f0a7187ca6630c1d0a9bb045"); + match contains_prefixed_key(&prefix) { + true => log::info!("Found keys for CouncilCollective (pre-removal"), + false => log::warn!("No keys found for CouncilCollective (pre-removal)"), + }; + + // TechCommitteeCollective: a06bfb73a86f8f98d5c5dc14e20e8a03 + let prefix = hex_literal::hex!("a06bfb73a86f8f98d5c5dc14e20e8a03"); + match contains_prefixed_key(&prefix) { + true => log::info!("Found keys for TechCommitteeCollective (pre-removal)"), + false => log::warn!("No keys found for TechCommitteeCollective (pre-removal)"), + }; + + Ok(sp_std::vec::Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(&self, _state: Vec) -> Result<(), sp_runtime::DispatchError> { + // CouncilCollective: d59b9be6f0a7187ca6630c1d0a9bb045 + let prefix = hex_literal::hex!("d59b9be6f0a7187ca6630c1d0a9bb045"); + match contains_prefixed_key(&prefix) { + true => log::info!("Found keys for CouncilCollective (post-removal) ⚠️"), + false => log::warn!("No keys found for CouncilCollective (post-removal) ✅"), + }; + + // TechCommitteeCollective: a06bfb73a86f8f98d5c5dc14e20e8a03 + let prefix = hex_literal::hex!("a06bfb73a86f8f98d5c5dc14e20e8a03"); + match contains_prefixed_key(&prefix) { + true => log::info!("Found keys for TechCommitteeCollective (post-removal) ⚠️"), + false => log::warn!("No keys found for TechCommitteeCollective (post-removal) ✅"), + }; + Ok(()) + } +} + +pub struct MigrateToLatestXcmVersion(PhantomData); +impl Migration for MigrateToLatestXcmVersion +where + pallet_xcm::migration::MigrateToLatestXcmVersion: OnRuntimeUpgrade, +{ + fn friendly_name(&self) -> &str { + "MM_MigrateToLatestXcmVersion" + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + pallet_xcm::migration::MigrateToLatestXcmVersion::::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade(&self) -> Result, sp_runtime::DispatchError> { + pallet_xcm::migration::MigrateToLatestXcmVersion::::pre_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(&self, state: Vec) -> Result<(), sp_runtime::DispatchError> { + pallet_xcm::migration::MigrateToLatestXcmVersion::::post_upgrade(state) + } +} + +pub struct CommonMigrations(PhantomData); + +impl GetMigrations for CommonMigrations +where + Runtime: pallet_author_mapping::Config, + Runtime: pallet_parachain_staking::Config, + Runtime: pallet_scheduler::Config, + Runtime: AuthorSlotFilterConfig, + Runtime: pallet_preimage::Config, + Runtime: pallet_asset_manager::Config, + Runtime: pallet_xcm_transactor::Config, + Runtime: pallet_moonbeam_orbiters::Config, + Runtime: pallet_balances::Config, + Runtime: pallet_referenda::Config, + Runtime: pallet_xcm::Config, + Runtime::AccountId: Default, + BlockNumberFor: Into, +{ + fn get_migrations() -> Vec> { + // let migration_author_mapping_twox_to_blake = AuthorMappingTwoXToBlake:: { + // 0: Default::default(), + // }; + + // let migration_parachain_staking_purge_stale_storage = + // ParachainStakingPurgeStaleStorage::(Default::default()); + // let migration_parachain_staking_manual_exits = + // ParachainStakingManualExits::(Default::default()); + // let migration_parachain_staking_increase_max_delegations_per_candidate = + // ParachainStakingIncreaseMaxDelegationsPerCandidate::(Default::default()); + // let migration_parachain_staking_split_candidate_state = + // ParachainStakingSplitCandidateState::(Default::default()); + // let migration_parachain_staking_patch_incorrect_delegation_sums = + // ParachainStakingPatchIncorrectDelegationSums::(Default::default()); + + // let migration_scheduler_v3 = SchedulerMigrationV3::(Default::default()); + + // let migration_base_fee = MigrateBaseFeePerGas::(Default::default()); + + // TODO: this is a lot of allocation to do upon every get() call. this *should* be avoided + // except when pallet_migrations undergoes a runtime upgrade -- but TODO: review + + // let migration_author_slot_filter_eligible_ratio_to_eligibility_count = + // AuthorSlotFilterEligibleRatioToEligiblityCount::(Default::default()); + // let migration_author_mapping_add_keys_to_registration_info = + // AuthorMappingAddKeysToRegistrationInfo::(Default::default()); + // let staking_delegator_state_requests = + // ParachainStakingSplitDelegatorStateIntoDelegationScheduledRequests::( + // Default::default(), + // ); + // let migration_author_mapping_add_account_id_to_nimbus_lookup = + // AuthorMappingAddAccountIdToNimbusLookup::(Default::default()); + + // let xcm_transactor_max_weight = + // XcmTransactorMaxTransactWeight::(Default::default()); + + // let asset_manager_units_with_asset_type = + // AssetManagerUnitsWithAssetType::(Default::default()); + + // let asset_manager_populate_asset_type_id_storage = + // AssetManagerPopulateAssetTypeIdStorage::(Default::default()); + + // let asset_manager_change_statemine_prefixes = AssetManagerChangeStateminePrefixes::< + // Runtime, + // StatemineParaIdInfo, + // StatemineAssetsInstanceInfo, + // >(Default::default()); + + // let xcm_supported_assets = XcmPaymentSupportedAssets::(Default::default()); + + // let migration_elasticity = MigrateBaseFeeElasticity::(Default::default()); + //let staking_at_stake_auto_compound = + // ParachainStakingMigrateAtStakeAutoCompound::(Default::default()); + + //let scheduler_to_v4 = SchedulerMigrationV4::(Default::default()); + //let democracy_migration_hash_to_bounded_call = + // DemocracryMigrationHashToBoundedCall::(Default::default()); + //let preimage_migration_hash_to_bounded_call = + // PreimageMigrationHashToBoundedCall::(Default::default()); + //let asset_manager_to_xcm_v3 = + // PalletAssetManagerMigrateXcmV2ToV3::(Default::default()); + //let xcm_transactor_to_xcm_v3 = + // PalletXcmTransactorMigrateXcmV2ToV3::(Default::default()); + //let remove_min_bond_for_old_orbiter_collators = + // RemoveMinBondForOrbiterCollators::(Default::default()); + // let missing_balances_migrations = MissingBalancesMigrations::(Default::default()); + // let fix_pallet_versions = + // FixIncorrectPalletVersions::(Default::default()); + // let pallet_referenda_migrate_v0_to_v1 = + // PalletReferendaMigrateV0ToV1::(Default::default()); + //let pallet_collective_drop_gov_v1_collectives = + // PalletCollectiveDropGovV1Collectives::(Default::default()); + //let pallet_staking_round = PalletStakingRoundMigration::(Default::default()); + let remove_pallet_democracy = RemovePalletDemocracy::(Default::default()); + let remove_collectives_addresses = + RemoveCollectivesAddresses::(Default::default()); + + vec![ + // completed in runtime 800 + // Box::new(migration_author_mapping_twox_to_blake), + // completed in runtime 900 + // completed in runtime 1000 + // Box::new(migration_parachain_staking_purge_stale_storage), + // completed in runtime 1000 + // Box::new(migration_parachain_staking_manual_exits), + // completed in runtime 1101 + // Box::new(migration_parachain_staking_increase_max_delegations_per_candidate), + // completed in runtime 1201 + // Box::new(migration_parachain_staking_split_candidate_state), + // completed in runtime 1201 + // Box::new(xcm_transactor_max_weight), + // completed in runtime 1201 + // Box::new(asset_manager_units_with_asset_type), + // completed in runtime 1201 + // Box::new(asset_manager_change_statemine_prefixes), + // completed in runtime 1201 + // Box::new(asset_manager_populate_asset_type_id_storage), + // completed in runtime 1300 + // Box::new(migration_scheduler_v3), + // completed in runtime 1300 + // Box::new(migration_parachain_staking_patch_incorrect_delegation_sums), + // completed in runtime 1300 + // Box::new(migration_base_fee), + // completed in runtime 1300 + // Box::new(xcm_supported_assets), + // completed in runtime 1500 + // Box::new(migration_author_slot_filter_eligible_ratio_to_eligibility_count), + // Box::new(migration_author_mapping_add_keys_to_registration_info), + // Box::new(staking_delegator_state_requests), + // completed in runtime 1600 + // Box::new(migration_author_mapping_add_account_id_to_nimbus_lookup), + // completed in runtime 1600 + // Box::new(xcm_transactor_transact_signed), + // completed in runtime 1700 + //Box::new(migration_elasticity), + // completed in runtime 1900 + //Box::new(staking_at_stake_auto_compound), + // completed in runtime 2000 + //Box::new(scheduler_to_v4), + //Box::new(democracy_migration_hash_to_bounded_call), + //Box::new(preimage_migration_hash_to_bounded_call), + // completed in runtime 2100 + //Box::new(asset_manager_to_xcm_v3), + //Box::new(xcm_transactor_to_xcm_v3), + // completed in runtime 2600 + //Box::new(remove_min_bond_for_old_orbiter_collators), + // completed in runtime 2700 + // Box::new(missing_balances_migrations), + // Box::new(fix_pallet_versions), + // Box::new(pallet_referenda_migrate_v0_to_v1), + // completed in runtime 2800 + //Box::new(pallet_collective_drop_gov_v1_collectives), + // completed in runtime 2801 + // Box::new(pallet_staking_round), + // Box::new(pallet_collective_drop_gov_v1_collectives), + // completed in runtime 2900 + Box::new(remove_pallet_democracy), + Box::new(remove_collectives_addresses), + // permanent migrations + Box::new(MigrateToLatestXcmVersion::(Default::default())), + ] + } +} diff --git a/tracing/2900/runtime/common/src/weights/cumulus_pallet_xcmp_queue.rs b/tracing/2900/runtime/common/src/weights/cumulus_pallet_xcmp_queue.rs new file mode 100644 index 00000000..fcc7db95 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/cumulus_pallet_xcmp_queue.rs @@ -0,0 +1,154 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `cumulus_pallet_xcmp_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=cumulus_pallet_xcmp_queue +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `cumulus_pallet_xcmp_queue`. +pub struct WeightInfo(PhantomData); +impl cumulus_pallet_xcmp_queue::WeightInfo for WeightInfo { + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:1) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_config_with_u32() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 4_599_000 picoseconds. + Weight::from_parts(4_823_000, 1627) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(134193), added: 136668, mode: `MaxEncodedLen`) + fn enqueue_xcmp_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `148` + // Estimated: `3517` + // Minimum execution time: 11_813_000 picoseconds. + Weight::from_parts(12_241_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn suspend_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 2_901_000 picoseconds. + Weight::from_parts(3_040_000, 1627) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn resume_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `177` + // Estimated: `1662` + // Minimum execution time: 3_742_000 picoseconds. + Weight::from_parts(3_848_000, 1662) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn take_first_concatenated_xcm() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_580_000 picoseconds. + Weight::from_parts(7_910_000, 0) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(134193), added: 136668, mode: `MaxEncodedLen`) + fn on_idle_good_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `134385` + // Estimated: `137850` + // Minimum execution time: 190_319_000 picoseconds. + Weight::from_parts(193_537_000, 137850) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:0) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(134193), added: 136668, mode: `MaxEncodedLen`) + fn on_idle_large_msg() -> Weight { + // Proof Size summary in bytes: + // Measured: `65782` + // Estimated: `69247` + // Minimum execution time: 103_908_000 picoseconds. + Weight::from_parts(105_455_000, 69247) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/mod.rs b/tracing/2900/runtime/common/src/weights/mod.rs new file mode 100644 index 00000000..48b83715 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/mod.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbeam common weights. + +pub mod cumulus_pallet_xcmp_queue; +pub mod pallet_asset_manager; +pub mod pallet_assets; +pub mod pallet_author_inherent; +pub mod pallet_author_mapping; +pub mod pallet_author_slot_filter; +pub mod pallet_balances; +pub mod pallet_collective; +pub mod pallet_conviction_voting; +pub mod pallet_crowdloan_rewards; +pub mod pallet_evm; +pub mod pallet_identity; +pub mod pallet_moonbeam_lazy_migrations; +pub mod pallet_moonbeam_orbiters; +pub mod pallet_multisig; +pub mod pallet_parachain_staking; +pub mod pallet_precompile_benchmarks; +pub mod pallet_preimage; +pub mod pallet_proxy; +pub mod pallet_randomness; +pub mod pallet_referenda; +pub mod pallet_relay_storage_roots; +pub mod pallet_scheduler; +pub mod pallet_sudo; +pub mod pallet_timestamp; +pub mod pallet_treasury; +pub mod pallet_utility; +pub mod pallet_whitelist; +pub mod pallet_xcm; +pub mod pallet_xcm_transactor; diff --git a/tracing/2900/runtime/common/src/weights/pallet_asset_manager.rs b/tracing/2900/runtime/common/src/weights/pallet_asset_manager.rs new file mode 100644 index 00000000..248c113b --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_asset_manager.rs @@ -0,0 +1,144 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_asset_manager` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_manager +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_asset_manager`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_manager::WeightInfo for WeightInfo { + /// Storage: `AssetManager::AssetIdType` (r:1 w:1) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + /// Storage: `AssetManager::AssetTypeId` (r:0 w:1) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn register_foreign_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `82` + // Estimated: `3639` + // Minimum execution time: 29_830_000 picoseconds. + Weight::from_parts(30_736_000, 3639) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `AssetManager::AssetTypeId` (r:1 w:0) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1) + /// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:0 w:1) + /// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 100]`. + fn set_asset_units_per_second(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `611 + x * (9 ±0)` + // Estimated: `4000 + x * (10 ±0)` + // Minimum execution time: 19_988_000 picoseconds. + Weight::from_parts(19_458_970, 4000) + // Standard Error: 2_974 + .saturating_add(Weight::from_parts(773_359, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(x.into())) + } + /// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1) + /// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetIdType` (r:1 w:1) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:1 w:2) + /// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeId` (r:0 w:2) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 100]`. + fn change_existing_asset_type(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `926 + x * (13 ±0)` + // Estimated: `4309 + x * (15 ±0)` + // Minimum execution time: 30_076_000 picoseconds. + Weight::from_parts(30_892_465, 4309) + // Standard Error: 4_039 + .saturating_add(Weight::from_parts(852_893, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(Weight::from_parts(0, 15).saturating_mul(x.into())) + } + /// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1) + /// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:0 w:1) + /// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 100]`. + fn remove_supported_asset(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `196 + x * (5 ±0)` + // Estimated: `1678 + x * (5 ±0)` + // Minimum execution time: 15_396_000 picoseconds. + Weight::from_parts(13_998_856, 1678) + // Standard Error: 2_734 + .saturating_add(Weight::from_parts(706_945, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(x.into())) + } + /// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1) + /// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetIdType` (r:1 w:1) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:0 w:1) + /// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeId` (r:0 w:1) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[5, 100]`. + fn remove_existing_asset_type(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `482 + x * (10 ±0)` + // Estimated: `3955 + x * (10 ±0)` + // Minimum execution time: 21_743_000 picoseconds. + Weight::from_parts(21_130_966, 3955) + // Standard Error: 3_157 + .saturating_add(Weight::from_parts(727_255, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 10).saturating_mul(x.into())) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_assets.rs b/tracing/2900/runtime/common/src/weights/pallet_assets.rs new file mode 100644 index 00000000..1bc42148 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_assets.rs @@ -0,0 +1,485 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_assets` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_assets`. +pub struct WeightInfo(PhantomData); +impl pallet_assets::WeightInfo for WeightInfo { + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 0) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3639` + // Minimum execution time: 9_311_000 picoseconds. + Weight::from_parts(9_640_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `241` + // Estimated: `3639` + // Minimum execution time: 10_710_000 picoseconds. + Weight::from_parts(11_184_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:657 w:656) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:656 w:656) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 656]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1359 + c * (183 ±0)` + // Estimated: `3639 + c * (2597 ±0)` + // Minimum execution time: 15_240_000 picoseconds. + Weight::from_parts(15_481_000, 3639) + // Standard Error: 9_062 + .saturating_add(Weight::from_parts(15_791_127, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2597).saturating_mul(c.into())) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:657 w:656) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(136), added: 2611, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 656]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `313 + a * (74 ±0)` + // Estimated: `3639 + a * (2611 ±0)` + // Minimum execution time: 16_051_000 picoseconds. + Weight::from_parts(16_294_000, 3639) + // Standard Error: 6_449 + .saturating_add(Weight::from_parts(8_360_355, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2611).saturating_mul(a.into())) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 11_619_000 picoseconds. + Weight::from_parts(11_886_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 20_717_000 picoseconds. + Weight::from_parts(21_127_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3639` + // Minimum execution time: 28_139_000 picoseconds. + Weight::from_parts(28_640_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `420` + // Estimated: `6184` + // Minimum execution time: 40_122_000 picoseconds. + Weight::from_parts(40_528_000, 6184) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `420` + // Estimated: `6184` + // Minimum execution time: 35_519_000 picoseconds. + Weight::from_parts(36_732_000, 6184) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `420` + // Estimated: `6184` + // Minimum execution time: 39_819_000 picoseconds. + Weight::from_parts(40_611_000, 6184) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3639` + // Minimum execution time: 14_292_000 picoseconds. + Weight::from_parts(14_764_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3639` + // Minimum execution time: 14_173_000 picoseconds. + Weight::from_parts(14_628_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `241` + // Estimated: `3639` + // Minimum execution time: 10_270_000 picoseconds. + Weight::from_parts(10_712_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `241` + // Estimated: `3639` + // Minimum execution time: 10_229_000 picoseconds. + Weight::from_parts(10_596_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 12_033_000 picoseconds. + Weight::from_parts(12_335_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 10_454_000 picoseconds. + Weight::from_parts(10_743_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 25_352_000 picoseconds. + Weight::from_parts(26_355_659, 3639) + // Standard Error: 538 + .saturating_add(Weight::from_parts(955, 0).saturating_mul(n.into())) + // Standard Error: 538 + .saturating_add(Weight::from_parts(1_407, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `3639` + // Minimum execution time: 26_449_000 picoseconds. + Weight::from_parts(26_877_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `94` + // Estimated: `3639` + // Minimum execution time: 10_953_000 picoseconds. + Weight::from_parts(11_548_090, 3639) + // Standard Error: 283 + .saturating_add(Weight::from_parts(1_176, 0).saturating_mul(n.into())) + // Standard Error: 283 + .saturating_add(Weight::from_parts(586, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `383` + // Estimated: `3639` + // Minimum execution time: 25_835_000 picoseconds. + Weight::from_parts(26_847_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 9_865_000 picoseconds. + Weight::from_parts(10_151_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(136), added: 2611, mode: `MaxEncodedLen`) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `241` + // Estimated: `3639` + // Minimum execution time: 16_508_000 picoseconds. + Weight::from_parts(16_834_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(136), added: 2611, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `513` + // Estimated: `6184` + // Minimum execution time: 48_917_000 picoseconds. + Weight::from_parts(49_824_000, 6184) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(136), added: 2611, mode: `MaxEncodedLen`) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `399` + // Estimated: `3639` + // Minimum execution time: 18_226_000 picoseconds. + Weight::from_parts(18_762_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(136), added: 2611, mode: `MaxEncodedLen`) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `399` + // Estimated: `3639` + // Minimum execution time: 18_655_000 picoseconds. + Weight::from_parts(19_074_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 11_033_000 picoseconds. + Weight::from_parts(11_236_000, 3639) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `435` + // Estimated: `3639` + // Minimum execution time: 30_611_000 picoseconds. + Weight::from_parts(31_492_000, 3639) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `207` + // Estimated: `3639` + // Minimum execution time: 28_482_000 picoseconds. + Weight::from_parts(29_498_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `561` + // Estimated: `3639` + // Minimum execution time: 30_261_000 picoseconds. + Weight::from_parts(31_050_000, 3639) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `3639` + // Minimum execution time: 27_945_000 picoseconds. + Weight::from_parts(28_808_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3639` + // Minimum execution time: 14_179_000 picoseconds. + Weight::from_parts(14_486_000, 3639) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_author_inherent.rs b/tracing/2900/runtime/common/src/weights/pallet_author_inherent.rs new file mode 100644 index 00000000..ab09ba88 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_author_inherent.rs @@ -0,0 +1,70 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_author_inherent` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_author_inherent +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_author_inherent`. +pub struct WeightInfo(PhantomData); +impl pallet_author_inherent::WeightInfo for WeightInfo { + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::Author` (r:1 w:0) + /// Proof: `AuthorInherent::Author` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorFilter::EligibleCount` (r:1 w:0) + /// Proof: `AuthorFilter::EligibleCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::PreviousLocalVrfOutput` (r:1 w:0) + /// Proof: `Randomness::PreviousLocalVrfOutput` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorInherent::InherentIncluded` (r:0 w:1) + /// Proof: `AuthorInherent::InherentIncluded` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn kick_off_authorship_validation() -> Weight { + // Proof Size summary in bytes: + // Measured: `372` + // Estimated: `1857` + // Minimum execution time: 17_417_000 picoseconds. + Weight::from_parts(17_809_000, 1857) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_author_mapping.rs b/tracing/2900/runtime/common/src/weights/pallet_author_mapping.rs new file mode 100644 index 00000000..691b9bf6 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_author_mapping.rs @@ -0,0 +1,122 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_author_mapping` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_author_mapping +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_author_mapping`. +pub struct WeightInfo(PhantomData); +impl pallet_author_mapping::WeightInfo for WeightInfo { + /// Storage: `AuthorMapping::MappingWithDeposit` (r:1 w:1) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AuthorMapping::NimbusLookup` (r:0 w:1) + /// Proof: `AuthorMapping::NimbusLookup` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn add_association() -> Weight { + // Proof Size summary in bytes: + // Measured: `376` + // Estimated: `3841` + // Minimum execution time: 27_782_000 picoseconds. + Weight::from_parts(28_609_000, 3841) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `AuthorMapping::MappingWithDeposit` (r:2 w:2) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AuthorMapping::NimbusLookup` (r:0 w:1) + /// Proof: `AuthorMapping::NimbusLookup` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_association() -> Weight { + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `6265` + // Minimum execution time: 17_725_000 picoseconds. + Weight::from_parts(18_140_000, 6265) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `AuthorMapping::MappingWithDeposit` (r:1 w:1) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AuthorMapping::NimbusLookup` (r:0 w:1) + /// Proof: `AuthorMapping::NimbusLookup` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn clear_association() -> Weight { + // Proof Size summary in bytes: + // Measured: `453` + // Estimated: `3918` + // Minimum execution time: 28_560_000 picoseconds. + Weight::from_parts(29_318_000, 3918) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `AuthorMapping::NimbusLookup` (r:1 w:1) + /// Proof: `AuthorMapping::NimbusLookup` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AuthorMapping::MappingWithDeposit` (r:1 w:1) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn remove_keys() -> Weight { + // Proof Size summary in bytes: + // Measured: `547` + // Estimated: `4012` + // Minimum execution time: 34_779_000 picoseconds. + Weight::from_parts(35_685_000, 4012) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `AuthorMapping::NimbusLookup` (r:1 w:1) + /// Proof: `AuthorMapping::NimbusLookup` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AuthorMapping::MappingWithDeposit` (r:1 w:1) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn set_keys() -> Weight { + // Proof Size summary in bytes: + // Measured: `432` + // Estimated: `3897` + // Minimum execution time: 30_704_000 picoseconds. + Weight::from_parts(31_832_000, 3897) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_author_slot_filter.rs b/tracing/2900/runtime/common/src/weights/pallet_author_slot_filter.rs new file mode 100644 index 00000000..1d7842fe --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_author_slot_filter.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_author_slot_filter` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_author_slot_filter +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_author_slot_filter`. +pub struct WeightInfo(PhantomData); +impl pallet_author_slot_filter::WeightInfo for WeightInfo { + /// Storage: `AuthorFilter::EligibleCount` (r:0 w:1) + /// Proof: `AuthorFilter::EligibleCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_eligible() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_543_000 picoseconds. + Weight::from_parts(4_833_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_balances.rs b/tracing/2900/runtime/common/src/weights/pallet_balances.rs new file mode 100644 index 00000000..71120250 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_balances.rs @@ -0,0 +1,151 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_balances` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_balances +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_balances`. +pub struct WeightInfo(PhantomData); +impl pallet_balances::WeightInfo for WeightInfo { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `3581` + // Minimum execution time: 41_684_000 picoseconds. + Weight::from_parts(42_289_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `3581` + // Minimum execution time: 40_145_000 picoseconds. + Weight::from_parts(40_934_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_set_balance_creating() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3581` + // Minimum execution time: 15_448_000 picoseconds. + Weight::from_parts(15_726_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_set_balance_killing() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3581` + // Minimum execution time: 16_390_000 picoseconds. + Weight::from_parts(16_702_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `267` + // Estimated: `6172` + // Minimum execution time: 44_041_000 picoseconds. + Weight::from_parts(44_655_000, 6172) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `72` + // Estimated: `3581` + // Minimum execution time: 44_316_000 picoseconds. + Weight::from_parts(45_153_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn force_unreserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `195` + // Estimated: `3581` + // Minimum execution time: 18_223_000 picoseconds. + Weight::from_parts(18_633_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1137 + u * (123 ±0)` + // Estimated: `990 + u * (2591 ±0)` + // Minimum execution time: 16_981_000 picoseconds. + Weight::from_parts(17_151_000, 990) + // Standard Error: 10_657 + .saturating_add(Weight::from_parts(13_838_684, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(u.into())) + } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 6_481_000 picoseconds. + Weight::from_parts(6_671_000, 1501) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_collective.rs b/tracing/2900/runtime/common/src/weights/pallet_collective.rs new file mode 100644 index 00000000..5c70e97f --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_collective.rs @@ -0,0 +1,308 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . +//! Autogenerated weights for `pallet_collective` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_collective +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/common/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_collective`. +pub struct WeightInfo(PhantomData); +impl pallet_collective::WeightInfo for WeightInfo { + /// Storage: CouncilCollective Members (r:1 w:1) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:0) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Voting (r:100 w:100) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Prime (r:0 w:1) + /// Proof Skipped: CouncilCollective Prime (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `m` is `[0, 100]`. + /// The range of component `n` is `[0, 100]`. + /// The range of component `p` is `[0, 100]`. + fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + m * (2021 ±0) + p * (2026 ±0)` + // Estimated: `12238 + m * (1231 ±14) + p * (3660 ±14)` + // Minimum execution time: 13_785_000 picoseconds. + Weight::from_parts(14_085_000, 0) + .saturating_add(Weight::from_parts(0, 12238)) + // Standard Error: 34_315 + .saturating_add(Weight::from_parts(2_414_144, 0).saturating_mul(m.into())) + // Standard Error: 34_315 + .saturating_add(Weight::from_parts(5_345_562, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_parts(0, 1231).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 3660).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: MaintenanceMode MaintenanceMode (r:1 w:0) + /// Proof Skipped: MaintenanceMode MaintenanceMode (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. + /// The range of component `m` is `[1, 100]`. + fn execute(b: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `149 + m * (20 ±0)` + // Estimated: `1634 + m * (20 ±0)` + // Minimum execution time: 12_726_000 picoseconds. + Weight::from_parts(12_294_605, 0) + .saturating_add(Weight::from_parts(0, 1634)) + // Standard Error: 18 + .saturating_add(Weight::from_parts(910, 0).saturating_mul(b.into())) + // Standard Error: 192 + .saturating_add(Weight::from_parts(13_235, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(Weight::from_parts(0, 20).saturating_mul(m.into())) + } + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:1 w:0) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: MaintenanceMode MaintenanceMode (r:1 w:0) + /// Proof Skipped: MaintenanceMode MaintenanceMode (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. + /// The range of component `m` is `[1, 100]`. + fn propose_execute(b: u32, m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `149 + m * (20 ±0)` + // Estimated: `3614 + m * (20 ±0)` + // Minimum execution time: 14_722_000 picoseconds. + Weight::from_parts(14_487_427, 0) + .saturating_add(Weight::from_parts(0, 3614)) + // Standard Error: 20 + .saturating_add(Weight::from_parts(944, 0).saturating_mul(b.into())) + // Standard Error: 215 + .saturating_add(Weight::from_parts(25_619, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(Weight::from_parts(0, 20).saturating_mul(m.into())) + } + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:1 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalCount (r:1 w:1) + /// Proof Skipped: CouncilCollective ProposalCount (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Voting (r:0 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. + /// The range of component `m` is `[2, 100]`. + /// The range of component `p` is `[1, 100]`. + fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `395 + m * (20 ±0) + p * (36 ±0)` + // Estimated: `3789 + m * (21 ±0) + p * (36 ±0)` + // Minimum execution time: 17_107_000 picoseconds. + Weight::from_parts(15_172_014, 0) + .saturating_add(Weight::from_parts(0, 3789)) + // Standard Error: 104 + .saturating_add(Weight::from_parts(3_191, 0).saturating_mul(b.into())) + // Standard Error: 1_090 + .saturating_add(Weight::from_parts(31_145, 0).saturating_mul(m.into())) + // Standard Error: 1_077 + .saturating_add(Weight::from_parts(131_855, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(Weight::from_parts(0, 21).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Voting (r:1 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// The range of component `m` is `[5, 100]`. + fn vote(m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `870 + m * (40 ±0)` + // Estimated: `4334 + m * (40 ±0)` + // Minimum execution time: 21_423_000 picoseconds. + Weight::from_parts(22_311_906, 0) + .saturating_add(Weight::from_parts(0, 4334)) + // Standard Error: 415 + .saturating_add(Weight::from_parts(32_990, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(m.into())) + } + /// Storage: CouncilCollective Voting (r:1 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:0 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// The range of component `m` is `[4, 100]`. + /// The range of component `p` is `[1, 100]`. + fn close_early_disapproved(m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `447 + m * (40 ±0) + p * (36 ±0)` + // Estimated: `3892 + m * (41 ±0) + p * (36 ±0)` + // Minimum execution time: 20_010_000 picoseconds. + Weight::from_parts(20_008_777, 0) + .saturating_add(Weight::from_parts(0, 3892)) + // Standard Error: 1_391 + .saturating_add(Weight::from_parts(39_960, 0).saturating_mul(m.into())) + // Standard Error: 1_356 + .saturating_add(Weight::from_parts(126_447, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 41).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Voting (r:1 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:1 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: MaintenanceMode MaintenanceMode (r:1 w:0) + /// Proof Skipped: MaintenanceMode MaintenanceMode (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. + /// The range of component `m` is `[4, 100]`. + /// The range of component `p` is `[1, 100]`. + fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `791 + b * (1 ±0) + m * (40 ±0) + p * (40 ±0)` + // Estimated: `4108 + b * (1 ±0) + m * (42 ±0) + p * (40 ±0)` + // Minimum execution time: 30_647_000 picoseconds. + Weight::from_parts(30_084_699, 0) + .saturating_add(Weight::from_parts(0, 4108)) + // Standard Error: 123 + .saturating_add(Weight::from_parts(2_876, 0).saturating_mul(b.into())) + // Standard Error: 1_307 + .saturating_add(Weight::from_parts(31_661, 0).saturating_mul(m.into())) + // Standard Error: 1_274 + .saturating_add(Weight::from_parts(154_567, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 42).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Voting (r:1 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Prime (r:1 w:0) + /// Proof Skipped: CouncilCollective Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:0 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// The range of component `m` is `[4, 100]`. + /// The range of component `p` is `[1, 100]`. + fn close_disapproved(m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `516 + m * (30 ±0) + p * (36 ±0)` + // Estimated: `3958 + m * (31 ±0) + p * (36 ±0)` + // Minimum execution time: 20_735_000 picoseconds. + Weight::from_parts(22_649_363, 0) + .saturating_add(Weight::from_parts(0, 3958)) + // Standard Error: 1_082 + .saturating_add(Weight::from_parts(32_331, 0).saturating_mul(m.into())) + // Standard Error: 1_055 + .saturating_add(Weight::from_parts(122_034, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 31).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Voting (r:1 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective Members (r:1 w:0) + /// Proof Skipped: CouncilCollective Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Prime (r:1 w:0) + /// Proof Skipped: CouncilCollective Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:1 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: MaintenanceMode MaintenanceMode (r:1 w:0) + /// Proof Skipped: MaintenanceMode MaintenanceMode (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// The range of component `b` is `[2, 1024]`. + /// The range of component `m` is `[4, 100]`. + /// The range of component `p` is `[1, 100]`. + fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `811 + b * (1 ±0) + m * (40 ±0) + p * (40 ±0)` + // Estimated: `4128 + b * (1 ±0) + m * (42 ±0) + p * (40 ±0)` + // Minimum execution time: 32_927_000 picoseconds. + Weight::from_parts(32_086_367, 0) + .saturating_add(Weight::from_parts(0, 4128)) + // Standard Error: 122 + .saturating_add(Weight::from_parts(2_962, 0).saturating_mul(b.into())) + // Standard Error: 1_299 + .saturating_add(Weight::from_parts(32_167, 0).saturating_mul(m.into())) + // Standard Error: 1_266 + .saturating_add(Weight::from_parts(154_131, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 42).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) + } + /// Storage: CouncilCollective Proposals (r:1 w:1) + /// Proof Skipped: CouncilCollective Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: CouncilCollective Voting (r:0 w:1) + /// Proof Skipped: CouncilCollective Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: CouncilCollective ProposalOf (r:0 w:1) + /// Proof Skipped: CouncilCollective ProposalOf (max_values: None, max_size: None, mode: Measured) + /// The range of component `p` is `[1, 100]`. + fn disapprove_proposal(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `264 + p * (32 ±0)` + // Estimated: `1749 + p * (32 ±0)` + // Minimum execution time: 10_334_000 picoseconds. + Weight::from_parts(11_413_201, 0) + .saturating_add(Weight::from_parts(0, 1749)) + // Standard Error: 1_033 + .saturating_add(Weight::from_parts(95_458, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_conviction_voting.rs b/tracing/2900/runtime/common/src/weights/pallet_conviction_voting.rs new file mode 100644 index 00000000..b64f16db --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_conviction_voting.rs @@ -0,0 +1,184 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_conviction_voting` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_conviction_voting +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_conviction_voting`. +pub struct WeightInfo(PhantomData); +impl pallet_conviction_voting::WeightInfo for WeightInfo { + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn vote_new() -> Weight { + // Proof Size summary in bytes: + // Measured: `1961` + // Estimated: `42428` + // Minimum execution time: 63_255_000 picoseconds. + Weight::from_parts(65_721_000, 42428) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn vote_existing() -> Weight { + // Proof Size summary in bytes: + // Measured: `2262` + // Estimated: `83866` + // Minimum execution time: 83_134_000 picoseconds. + Weight::from_parts(85_859_000, 83866) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn remove_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `1840` + // Estimated: `83866` + // Minimum execution time: 55_539_000 picoseconds. + Weight::from_parts(57_196_000, 83866) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + fn remove_other_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `1384` + // Estimated: `4617` + // Minimum execution time: 20_722_000 picoseconds. + Weight::from_parts(21_199_000, 4617) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:20 w:20) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// The range of component `r` is `[0, 20]`. + fn delegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1620 + r * (248 ±0)` + // Estimated: `83866 + r * (3387 ±0)` + // Minimum execution time: 44_300_000 picoseconds. + Weight::from_parts(39_609_524, 83866) + // Standard Error: 80_179 + .saturating_add(Weight::from_parts(24_498_559, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 3387).saturating_mul(r.into())) + } + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:20 w:20) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `r` is `[0, 20]`. + fn undelegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1339 + r * (248 ±0)` + // Estimated: `83866 + r * (3387 ±0)` + // Minimum execution time: 20_213_000 picoseconds. + Weight::from_parts(11_563_375, 83866) + // Standard Error: 81_737 + .saturating_add(Weight::from_parts(24_404_856, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 3387).saturating_mul(r.into())) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1152), added: 3627, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + fn unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1228` + // Estimated: `4752` + // Minimum execution time: 47_709_000 picoseconds. + Weight::from_parts(48_634_000, 4752) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_crowdloan_rewards.rs b/tracing/2900/runtime/common/src/weights/pallet_crowdloan_rewards.rs new file mode 100644 index 00000000..de7fec94 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_crowdloan_rewards.rs @@ -0,0 +1,163 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_crowdloan_rewards` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_crowdloan_rewards +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_crowdloan_rewards`. +pub struct WeightInfo(PhantomData); +impl pallet_crowdloan_rewards::WeightInfo for WeightInfo { + /// Storage: `CrowdloanRewards::Initialized` (r:1 w:0) + /// Proof: `CrowdloanRewards::Initialized` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::InitializedRewardAmount` (r:1 w:1) + /// Proof: `CrowdloanRewards::InitializedRewardAmount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::TotalContributors` (r:1 w:1) + /// Proof: `CrowdloanRewards::TotalContributors` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:501 w:501) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `CrowdloanRewards::ClaimedRelayChainIds` (r:500 w:500) + /// Proof: `CrowdloanRewards::ClaimedRelayChainIds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::UnassociatedContributions` (r:500 w:0) + /// Proof: `CrowdloanRewards::UnassociatedContributions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::AccountsPayable` (r:500 w:500) + /// Proof: `CrowdloanRewards::AccountsPayable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 500]`. + fn initialize_reward_vec(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `75971 + x * (659 ±0)` + // Estimated: `66133 + x * (3161 ±5)` + // Minimum execution time: 110_884_000 picoseconds. + Weight::from_parts(147_336_064, 66133) + // Standard Error: 40_142 + .saturating_add(Weight::from_parts(54_050_188, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3161).saturating_mul(x.into())) + } + /// Storage: `CrowdloanRewards::Initialized` (r:1 w:1) + /// Proof: `CrowdloanRewards::Initialized` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::InitRelayBlock` (r:1 w:0) + /// Proof: `CrowdloanRewards::InitRelayBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::InitializedRewardAmount` (r:1 w:0) + /// Proof: `CrowdloanRewards::InitializedRewardAmount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `CrowdloanRewards::TotalContributors` (r:1 w:0) + /// Proof: `CrowdloanRewards::TotalContributors` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::EndRelayBlock` (r:0 w:1) + /// Proof: `CrowdloanRewards::EndRelayBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn complete_initialization() -> Weight { + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `3581` + // Minimum execution time: 15_351_000 picoseconds. + Weight::from_parts(15_735_000, 3581) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `CrowdloanRewards::Initialized` (r:1 w:0) + /// Proof: `CrowdloanRewards::Initialized` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::AccountsPayable` (r:1 w:1) + /// Proof: `CrowdloanRewards::AccountsPayable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::InitRelayBlock` (r:1 w:0) + /// Proof: `CrowdloanRewards::InitRelayBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::EndRelayBlock` (r:1 w:0) + /// Proof: `CrowdloanRewards::EndRelayBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `1100` + // Estimated: `6172` + // Minimum execution time: 62_595_000 picoseconds. + Weight::from_parts(63_760_000, 6172) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `CrowdloanRewards::AccountsPayable` (r:2 w:2) + /// Proof: `CrowdloanRewards::AccountsPayable` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_reward_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `397` + // Estimated: `6337` + // Minimum execution time: 17_926_000 picoseconds. + Weight::from_parts(18_219_000, 6337) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `CrowdloanRewards::UnassociatedContributions` (r:1 w:1) + /// Proof: `CrowdloanRewards::UnassociatedContributions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::ClaimedRelayChainIds` (r:1 w:1) + /// Proof: `CrowdloanRewards::ClaimedRelayChainIds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CrowdloanRewards::AccountsPayable` (r:1 w:1) + /// Proof: `CrowdloanRewards::AccountsPayable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn associate_native_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `934` + // Estimated: `6172` + // Minimum execution time: 122_312_000 picoseconds. + Weight::from_parts(124_403_000, 6172) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `CrowdloanRewards::AccountsPayable` (r:2 w:2) + /// Proof: `CrowdloanRewards::AccountsPayable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 500]`. + fn change_association_with_relay_keys(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `366 + x * (32 ±0)` + // Estimated: `6306 + x * (32 ±0)` + // Minimum execution time: 76_926_000 picoseconds. + Weight::from_parts(77_278_000, 6306) + // Standard Error: 11_622 + .saturating_add(Weight::from_parts(57_377_263, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(x.into())) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_evm.rs b/tracing/2900/runtime/common/src/weights/pallet_evm.rs new file mode 100644 index 00000000..5a506cbd --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_evm.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_evm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_evm +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_evm`. +pub struct WeightInfo(PhantomData); +impl pallet_evm::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `EthereumChainId::ChainId` (r:1 w:0) + /// Proof: `EthereumChainId::ChainId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `EVM::AccountCodes` (r:2 w:0) + /// Proof: `EVM::AccountCodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EVM::AccountStorages` (r:1 w:0) + /// Proof: `EVM::AccountStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 10000000]`. + fn runner_execute(_x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1574` + // Estimated: `7514` + // Minimum execution time: 25_094_983_000 picoseconds. + Weight::from_parts(25_279_087_568, 7514) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + fn withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_878_000 picoseconds. + Weight::from_parts(2_005_000, 0) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_identity.rs b/tracing/2900/runtime/common/src/weights/pallet_identity.rs new file mode 100644 index 00000000..66abc430 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_identity.rs @@ -0,0 +1,411 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_identity` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_identity +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_identity`. +pub struct WeightInfo(PhantomData); +impl pallet_identity::WeightInfo for WeightInfo { + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 19]`. + fn add_registrar(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `32 + r * (45 ±0)` + // Estimated: `2386` + // Minimum execution time: 8_498_000 picoseconds. + Weight::from_parts(9_024_352, 2386) + // Standard Error: 1_263 + .saturating_add(Weight::from_parts(86_602, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 20]`. + fn set_identity(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `6966 + r * (5 ±0)` + // Estimated: `11025` + // Minimum execution time: 112_680_000 picoseconds. + Weight::from_parts(115_285_152, 11025) + // Standard Error: 5_277 + .saturating_add(Weight::from_parts(190_684, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:100 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 100]`. + fn set_subs_new(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `89` + // Estimated: `11025 + s * (2565 ±0)` + // Minimum execution time: 9_044_000 picoseconds. + Weight::from_parts(22_565_890, 11025) + // Standard Error: 4_822 + .saturating_add(Weight::from_parts(3_282_830, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + .saturating_add(Weight::from_parts(0, 2565).saturating_mul(s.into())) + } + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 100]`. + fn set_subs_old(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `169 + p * (20 ±0)` + // Estimated: `11025` + // Minimum execution time: 9_038_000 picoseconds. + Weight::from_parts(22_528_876, 11025) + // Standard Error: 3_237 + .saturating_add(Weight::from_parts(1_326_320, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + } + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + fn clear_identity(r: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `7046 + r * (5 ±0) + s * (20 ±0)` + // Estimated: `11025` + // Minimum execution time: 57_176_000 picoseconds. + Weight::from_parts(57_333_594, 11025) + // Standard Error: 7_257 + .saturating_add(Weight::from_parts(195_450, 0).saturating_mul(r.into())) + // Standard Error: 1_416 + .saturating_add(Weight::from_parts(1_326_826, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 20]`. + fn request_judgement(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `6956 + r * (45 ±0)` + // Estimated: `11025` + // Minimum execution time: 78_701_000 picoseconds. + Weight::from_parts(80_440_545, 11025) + // Standard Error: 3_857 + .saturating_add(Weight::from_parts(129_413, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 20]`. + fn cancel_request(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `6987` + // Estimated: `11025` + // Minimum execution time: 76_445_000 picoseconds. + Weight::from_parts(77_426_259, 11025) + // Standard Error: 3_373 + .saturating_add(Weight::from_parts(127_374, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 19]`. + fn set_fee(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `77 + r * (45 ±0)` + // Estimated: `2386` + // Minimum execution time: 6_225_000 picoseconds. + Weight::from_parts(6_559_852, 2386) + // Standard Error: 923 + .saturating_add(Weight::from_parts(70_556, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 19]`. + fn set_account_id(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `77 + r * (45 ±0)` + // Estimated: `2386` + // Minimum execution time: 6_252_000 picoseconds. + Weight::from_parts(6_559_957, 2386) + // Standard Error: 855 + .saturating_add(Weight::from_parts(69_177, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 19]`. + fn set_fields(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `77 + r * (45 ±0)` + // Estimated: `2386` + // Minimum execution time: 6_183_000 picoseconds. + Weight::from_parts(6_528_283, 2386) + // Standard Error: 732 + .saturating_add(Weight::from_parts(65_415, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(901), added: 1396, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 19]`. + fn provide_judgement(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `7022 + r * (45 ±0)` + // Estimated: `11025` + // Minimum execution time: 99_700_000 picoseconds. + Weight::from_parts(101_176_078, 11025) + // Standard Error: 4_582 + .saturating_add(Weight::from_parts(114_818, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + fn kill_identity(r: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `7313 + r * (5 ±0) + s * (20 ±0)` + // Estimated: `11025` + // Minimum execution time: 77_872_000 picoseconds. + Weight::from_parts(77_876_810, 11025) + // Standard Error: 8_973 + .saturating_add(Weight::from_parts(244_712, 0).saturating_mul(r.into())) + // Standard Error: 1_751 + .saturating_add(Weight::from_parts(1_336_482, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) + } + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 99]`. + fn add_sub(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `536 + s * (23 ±0)` + // Estimated: `11025` + // Minimum execution time: 26_168_000 picoseconds. + Weight::from_parts(30_873_268, 11025) + // Standard Error: 1_061 + .saturating_add(Weight::from_parts(70_656, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 100]`. + fn rename_sub(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `615 + s * (4 ±0)` + // Estimated: `11025` + // Minimum execution time: 12_535_000 picoseconds. + Weight::from_parts(15_886_319, 11025) + // Standard Error: 1_341 + .saturating_add(Weight::from_parts(60_303, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 100]`. + fn remove_sub(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `647 + s * (24 ±0)` + // Estimated: `11025` + // Minimum execution time: 29_877_000 picoseconds. + Weight::from_parts(34_605_810, 11025) + // Standard Error: 1_261 + .saturating_add(Weight::from_parts(87_624, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(90), added: 2565, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(2046), added: 4521, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 99]`. + fn quit_sub(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `620 + s * (24 ±0)` + // Estimated: `5511` + // Minimum execution time: 22_763_000 picoseconds. + Weight::from_parts(24_456_476, 5511) + // Standard Error: 1_256 + .saturating_add(Weight::from_parts(103_277, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Identity::UsernameAuthorities` (r:0 w:1) + /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn add_username_authority() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_045_000 picoseconds. + Weight::from_parts(6_504_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) + /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn remove_username_authority() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `3505` + // Minimum execution time: 9_372_000 picoseconds. + Weight::from_parts(9_591_000, 3505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) + /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Identity::AccountOfUsername` (r:1 w:1) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Identity::PendingUsernames` (r:1 w:0) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + fn set_username_for() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `11025` + // Minimum execution time: 69_326_000 picoseconds. + Weight::from_parts(70_625_000, 11025) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Identity::PendingUsernames` (r:1 w:1) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + /// Storage: `Identity::AccountOfUsername` (r:0 w:1) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + fn accept_username() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `11025` + // Minimum execution time: 20_230_000 picoseconds. + Weight::from_parts(20_655_000, 11025) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Identity::PendingUsernames` (r:1 w:1) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + fn remove_expired_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3538` + // Minimum execution time: 13_416_000 picoseconds. + Weight::from_parts(13_914_000, 3538) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::AccountOfUsername` (r:1 w:0) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + fn set_primary_username() -> Weight { + // Proof Size summary in bytes: + // Measured: `232` + // Estimated: `11025` + // Minimum execution time: 16_426_000 picoseconds. + Weight::from_parts(16_697_000, 11025) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Identity::AccountOfUsername` (r:1 w:1) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7560), added: 10035, mode: `MaxEncodedLen`) + fn remove_dangling_username() -> Weight { + // Proof Size summary in bytes: + // Measured: `86` + // Estimated: `11025` + // Minimum execution time: 11_743_000 picoseconds. + Weight::from_parts(12_016_000, 11025) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_moonbeam_lazy_migrations.rs b/tracing/2900/runtime/common/src/weights/pallet_moonbeam_lazy_migrations.rs new file mode 100644 index 00000000..7be5b3c4 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_moonbeam_lazy_migrations.rs @@ -0,0 +1,77 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_moonbeam_lazy_migrations` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_moonbeam_lazy_migrations +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_moonbeam_lazy_migrations`. +pub struct WeightInfo(PhantomData); +impl pallet_moonbeam_lazy_migrations::WeightInfo for WeightInfo { + /// Storage: `EVM::Suicided` (r:100 w:0) + /// Proof: `EVM::Suicided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `EVM::AccountCodes` (r:100 w:0) + /// Proof: `EVM::AccountCodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `EVM::AccountStorages` (r:1000 w:900) + /// Proof: `EVM::AccountStorages` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamLazyMigrations::SuicidedContractsRemoved` (r:1 w:1) + /// Proof: `MoonbeamLazyMigrations::SuicidedContractsRemoved` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// The range of component `a` is `[1, 100]`. + /// The range of component `l` is `[1, 1000]`. + fn clear_suicided_storage(a: u32, l: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `4155 + a * (11 ±0) + l * (84 ±0)` + // Estimated: `37723 + a * (2228 ±19) + l * (2536 ±1)` + // Minimum execution time: 47_583_000 picoseconds. + Weight::from_parts(47_755_000, 37723) + // Standard Error: 2_582_851 + .saturating_add(Weight::from_parts(42_961_979, 0).saturating_mul(a.into())) + // Standard Error: 258_009 + .saturating_add(Weight::from_parts(27_276_989, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(l.into()))) + .saturating_add(T::DbWeight::get().writes(41_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(l.into()))) + .saturating_add(Weight::from_parts(0, 2228).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2536).saturating_mul(l.into())) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_moonbeam_orbiters.rs b/tracing/2900/runtime/common/src/weights/pallet_moonbeam_orbiters.rs new file mode 100644 index 00000000..dc60e4b4 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_moonbeam_orbiters.rs @@ -0,0 +1,203 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_moonbeam_orbiters` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_moonbeam_orbiters +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_moonbeam_orbiters`. +pub struct WeightInfo(PhantomData); +impl pallet_moonbeam_orbiters::WeightInfo for WeightInfo { + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Reserves` (r:1 w:0) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1037), added: 3512, mode: `MaxEncodedLen`) + fn collator_add_orbiter() -> Weight { + // Proof Size summary in bytes: + // Measured: `562` + // Estimated: `4502` + // Minimum execution time: 20_069_000 picoseconds. + Weight::from_parts(20_744_000, 4502) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn collator_remove_orbiter() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `3831` + // Minimum execution time: 15_654_000 picoseconds. + Weight::from_parts(16_327_000, 3831) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn orbiter_leave_collator_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `3831` + // Minimum execution time: 15_840_000 picoseconds. + Weight::from_parts(16_363_000, 3831) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MoonbeamOrbiters::MinOrbiterDeposit` (r:1 w:0) + /// Proof: `MoonbeamOrbiters::MinOrbiterDeposit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Reserves` (r:1 w:1) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1037), added: 3512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MoonbeamOrbiters::RegisteredOrbiter` (r:0 w:1) + /// Proof: `MoonbeamOrbiters::RegisteredOrbiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn orbiter_register() -> Weight { + // Proof Size summary in bytes: + // Measured: `282` + // Estimated: `4502` + // Minimum execution time: 30_234_000 picoseconds. + Weight::from_parts(30_911_000, 4502) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `MoonbeamOrbiters::CounterForCollatorsPool` (r:1 w:0) + /// Proof: `MoonbeamOrbiters::CounterForCollatorsPool` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:101 w:0) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Reserves` (r:1 w:1) + /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1037), added: 3512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MoonbeamOrbiters::RegisteredOrbiter` (r:0 w:1) + /// Proof: `MoonbeamOrbiters::RegisteredOrbiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 100]`. + fn orbiter_unregister(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `391 + n * (48 ±0)` + // Estimated: `4502 + n * (2524 ±0)` + // Minimum execution time: 37_436_000 picoseconds. + Weight::from_parts(37_712_989, 4502) + // Standard Error: 7_879 + .saturating_add(Weight::from_parts(7_381_630, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 2524).saturating_mul(n.into())) + } + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::CounterForCollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CounterForCollatorsPool` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn add_collator() -> Weight { + // Proof Size summary in bytes: + // Measured: `82` + // Estimated: `3547` + // Minimum execution time: 11_002_000 picoseconds. + Weight::from_parts(11_373_000, 3547) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::CounterForCollatorsPool` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::CounterForCollatorsPool` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MoonbeamOrbiters::AccountLookupOverride` (r:0 w:9) + /// Proof: `MoonbeamOrbiters::AccountLookupOverride` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_collator() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `3831` + // Minimum execution time: 23_018_000 picoseconds. + Weight::from_parts(23_690_000, 3831) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(11_u64)) + } + /// Storage: `MoonbeamOrbiters::CurrentRound` (r:1 w:0) + /// Proof: `MoonbeamOrbiters::CurrentRound` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::OrbiterPerRound` (r:100 w:100) + /// Proof: `MoonbeamOrbiters::OrbiterPerRound` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 100]`. + fn on_initialize(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `140 + x * (61 ±0)` + // Estimated: `1624 + x * (2537 ±0)` + // Minimum execution time: 6_769_000 picoseconds. + Weight::from_parts(6_649_109, 1624) + // Standard Error: 1_243 + .saturating_add(Weight::from_parts(860_679, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 2537).saturating_mul(x.into())) + } + /// Storage: `MoonbeamOrbiters::OrbiterPerRound` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::OrbiterPerRound` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn distribute_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `298` + // Estimated: `3763` + // Minimum execution time: 21_346_000 picoseconds. + Weight::from_parts(21_697_000, 3763) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `MoonbeamOrbiters::ForceRotation` (r:1 w:1) + /// Proof: `MoonbeamOrbiters::ForceRotation` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::CollatorsPool` (r:2 w:1) + /// Proof: `MoonbeamOrbiters::CollatorsPool` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::OrbiterPerRound` (r:0 w:3) + /// Proof: `MoonbeamOrbiters::OrbiterPerRound` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::AccountLookupOverride` (r:0 w:3) + /// Proof: `MoonbeamOrbiters::AccountLookupOverride` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::CurrentRound` (r:0 w:1) + /// Proof: `MoonbeamOrbiters::CurrentRound` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn on_new_round() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6196` + // Minimum execution time: 29_700_000 picoseconds. + Weight::from_parts(30_769_000, 6196) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_multisig.rs b/tracing/2900/runtime/common/src/weights/pallet_multisig.rs new file mode 100644 index 00000000..77c0b5d0 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_multisig.rs @@ -0,0 +1,160 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_multisig` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_multisig +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_multisig`. +pub struct WeightInfo(PhantomData); +impl pallet_multisig::WeightInfo for WeightInfo { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `z` is `[0, 10000]`. + fn as_multi_threshold_1(z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 15_050_000 picoseconds. + Weight::from_parts(15_716_040, 1527) + // Standard Error: 2 + .saturating_add(Weight::from_parts(508, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_create(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `304` + // Estimated: `5587` + // Minimum execution time: 41_040_000 picoseconds. + Weight::from_parts(31_041_570, 5587) + // Standard Error: 1_531 + .saturating_add(Weight::from_parts(111_181, 0).saturating_mul(s.into())) + // Standard Error: 15 + .saturating_add(Weight::from_parts(1_470, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[3, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_approve(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `5587` + // Minimum execution time: 25_982_000 picoseconds. + Weight::from_parts(17_347_712, 5587) + // Standard Error: 413 + .saturating_add(Weight::from_parts(96_686, 0).saturating_mul(s.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_423, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[2, 100]`. + /// The range of component `z` is `[0, 10000]`. + fn as_multi_complete(s: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `475 + s * (20 ±0)` + // Estimated: `5587 + s * (20 ±0)` + // Minimum execution time: 49_756_000 picoseconds. + Weight::from_parts(37_536_364, 5587) + // Standard Error: 732 + .saturating_add(Weight::from_parts(134_613, 0).saturating_mul(s.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 20).saturating_mul(s.into())) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_create(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `304` + // Estimated: `5587` + // Minimum execution time: 29_297_000 picoseconds. + Weight::from_parts(30_042_055, 5587) + // Standard Error: 758 + .saturating_add(Weight::from_parts(105_815, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn approve_as_multi_approve(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `312` + // Estimated: `5587` + // Minimum execution time: 15_306_000 picoseconds. + Weight::from_parts(15_795_893, 5587) + // Standard Error: 464 + .saturating_add(Weight::from_parts(95_884, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(2122), added: 4597, mode: `MaxEncodedLen`) + /// The range of component `s` is `[2, 100]`. + fn cancel_as_multi(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `474` + // Estimated: `5587` + // Minimum execution time: 30_377_000 picoseconds. + Weight::from_parts(31_017_956, 5587) + // Standard Error: 645 + .saturating_add(Weight::from_parts(104_927, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_parachain_staking.rs b/tracing/2900/runtime/common/src/weights/pallet_parachain_staking.rs new file mode 100644 index 00000000..407880f7 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_parachain_staking.rs @@ -0,0 +1,864 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_parachain_staking` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_parachain_staking +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_parachain_staking`. +pub struct WeightInfo(PhantomData); +impl pallet_parachain_staking::WeightInfo for WeightInfo { + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_staking_expectations() -> Weight { + // Proof Size summary in bytes: + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 8_423_000 picoseconds. + Weight::from_parts(8_775_000, 1573) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_inflation() -> Weight { + // Proof Size summary in bytes: + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 36_410_000 picoseconds. + Weight::from_parts(37_149_000, 1573) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `7` + // Estimated: `1492` + // Minimum execution time: 7_299_000 picoseconds. + Weight::from_parts(7_659_000, 1492) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_reserve_percent() -> Weight { + // Proof Size summary in bytes: + // Measured: `7` + // Estimated: `1492` + // Minimum execution time: 7_208_000 picoseconds. + Weight::from_parts(7_443_000, 1492) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:1) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_total_selected() -> Weight { + // Proof Size summary in bytes: + // Measured: `28` + // Estimated: `1513` + // Minimum execution time: 7_838_000 picoseconds. + Weight::from_parts(8_123_000, 1513) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:1) + /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_collator_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `27` + // Estimated: `1512` + // Minimum execution time: 7_417_000 picoseconds. + Weight::from_parts(7_833_000, 1512) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_blocks_per_round() -> Weight { + // Proof Size summary in bytes: + // Measured: `116` + // Estimated: `1601` + // Minimum execution time: 39_491_000 picoseconds. + Weight::from_parts(39_898_000, 1601) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn join_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1417 + x * (38 ±0)` + // Estimated: `4752 + x * (41 ±0)` + // Minimum execution time: 47_716_000 picoseconds. + Weight::from_parts(57_234_897, 4752) + // Standard Error: 1_674 + .saturating_add(Weight::from_parts(86_593, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 41).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn schedule_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `665 + x * (37 ±0)` + // Estimated: `4023 + x * (38 ±0)` + // Minimum execution time: 16_081_000 picoseconds. + Weight::from_parts(21_529_015, 4023) + // Standard Error: 923 + .saturating_add(Weight::from_parts(62_703, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + fn execute_leave_candidates_worst_case(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1562 + x * (431 ±0)` + // Estimated: `4842 + x * (3762 ±0)` + // Minimum execution time: 97_318_000 picoseconds. + Weight::from_parts(99_043_000, 4842) + // Standard Error: 74_692 + .saturating_add(Weight::from_parts(35_574_906, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + /// The range of component `y` is `[2, 350]`. + fn execute_leave_candidates_ideal(x: u32, _y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1556 + x * (431 ±0)` + // Estimated: `4808 + x * (3762 ±0)` + // Minimum execution time: 90_086_000 picoseconds. + Weight::from_parts(91_574_000, 4808) + // Standard Error: 30_540 + .saturating_add(Weight::from_parts(36_983_808, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn cancel_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `633 + x * (37 ±0)` + // Estimated: `3991 + x * (38 ±0)` + // Minimum execution time: 15_188_000 picoseconds. + Weight::from_parts(20_439_228, 3991) + // Standard Error: 855 + .saturating_add(Weight::from_parts(58_850, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_offline(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `530 + x * (38 ±0)` + // Estimated: `3931 + x * (39 ±0)` + // Minimum execution time: 14_671_000 picoseconds. + Weight::from_parts(19_424_558, 3931) + // Standard Error: 931 + .saturating_add(Weight::from_parts(64_169, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_online(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `494 + x * (38 ±0)` + // Estimated: `3895 + x * (39 ±0)` + // Minimum execution time: 14_489_000 picoseconds. + Weight::from_parts(19_202_303, 3895) + // Standard Error: 941 + .saturating_add(Weight::from_parts(63_608, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn candidate_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1233 + x * (42 ±0)` + // Estimated: `4752 + x * (44 ±0)` + // Minimum execution time: 42_764_000 picoseconds. + Weight::from_parts(50_891_582, 4752) + // Standard Error: 1_584 + .saturating_add(Weight::from_parts(93_002, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn schedule_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `171` + // Estimated: `3636` + // Minimum execution time: 13_196_000 picoseconds. + Weight::from_parts(13_600_000, 3636) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn execute_candidate_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1322 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 46_535_000 picoseconds. + Weight::from_parts(54_211_512, 4752) + // Standard Error: 1_417 + .saturating_add(Weight::from_parts(68_025, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn set_candidate_bond_to_zero(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1302 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 43_747_000 picoseconds. + Weight::from_parts(48_740_388, 4752) + // Standard Error: 1_109 + .saturating_add(Weight::from_parts(74_397, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn cancel_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `191` + // Estimated: `3656` + // Minimum execution time: 11_711_000 picoseconds. + Weight::from_parts(12_297_000, 3656) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 100]`. + /// The range of component `y` is `[2, 300]`. + fn delegate(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2461 + x * (79 ±0) + y * (38 ±0)` + // Estimated: `5710 + x * (81 ±0) + y * (39 ±0)` + // Minimum execution time: 87_413_000 picoseconds. + Weight::from_parts(78_118_122, 5710) + // Standard Error: 2_180 + .saturating_add(Weight::from_parts(139_900, 0).saturating_mul(x.into())) + // Standard Error: 715 + .saturating_add(Weight::from_parts(44_970, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 81).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_revoke_delegation(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `567 + x * (42 ±0)` + // Estimated: `4013 + x * (43 ±0)` + // Minimum execution time: 15_923_000 picoseconds. + Weight::from_parts(22_402_960, 4013) + // Standard Error: 698 + .saturating_add(Weight::from_parts(68_223, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn delegator_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2029 + x * (79 ±0)` + // Estimated: `5425 + x * (79 ±0)` + // Minimum execution time: 60_083_000 picoseconds. + Weight::from_parts(75_619_654, 5425) + // Standard Error: 1_349 + .saturating_add(Weight::from_parts(112_362, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 79).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_delegator_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `567 + x * (42 ±0)` + // Estimated: `4013 + x * (43 ±0)` + // Minimum execution time: 16_270_000 picoseconds. + Weight::from_parts(22_794_243, 4013) + // Standard Error: 721 + .saturating_add(Weight::from_parts(71_247, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_revoke_delegation() -> Weight { + // Proof Size summary in bytes: + // Measured: `998` + // Estimated: `4752` + // Minimum execution time: 72_795_000 picoseconds. + Weight::from_parts(75_139_000, 4752) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_revoke_delegation_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `37342` + // Estimated: `40807` + // Minimum execution time: 135_013_000 picoseconds. + Weight::from_parts(138_816_000, 40807) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_bond_less_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `29964` + // Estimated: `33429` + // Minimum execution time: 115_091_000 picoseconds. + Weight::from_parts(120_774_000, 33429) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn cancel_delegation_request(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `664 + x * (42 ±0)` + // Estimated: `4093 + x * (43 ±0)` + // Minimum execution time: 19_716_000 picoseconds. + Weight::from_parts(28_122_355, 4093) + // Standard Error: 844 + .saturating_add(Weight::from_parts(59_741, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn prepare_staking_payouts() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3468` + // Minimum execution time: 3_034_000 picoseconds. + Weight::from_parts(3_101_000, 3468) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[0, 100]`. + fn get_rewardable_delegators(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `73 + y * (36 ±0)` + // Estimated: `3537 + y * (36 ±0)` + // Minimum execution time: 6_387_000 picoseconds. + Weight::from_parts(7_916_133, 3537) + // Standard Error: 621 + .saturating_add(Weight::from_parts(53_996, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:0) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:51 w:0) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:51 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:51 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:51 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:0 w:1) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:0 w:51) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 50]`. + /// The range of component `y` is `[0, 100]`. + fn select_top_candidates(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (3816 ±0) + y * (1800 ±0)` + // Estimated: `3693 + x * (3975 ±39) + y * (639 ±19)` + // Minimum execution time: 20_027_000 picoseconds. + Weight::from_parts(20_584_000, 3693) + // Standard Error: 78_348 + .saturating_add(Weight::from_parts(16_329_365, 0).saturating_mul(x.into())) + // Standard Error: 39_070 + .saturating_add(Weight::from_parts(1_389_236, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3975).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 639).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:349 w:349) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:349 w:349) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:349 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 349]`. + fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (396 ±0) + y * (156 ±0) + z * (41 ±0)` + // Estimated: `126334 + x * (2591 ±1) + y * (2234 ±1) + z * (28 ±0)` + // Minimum execution time: 259_000 picoseconds. + Weight::from_parts(283_000, 126334) + // Standard Error: 505_065 + .saturating_add(Weight::from_parts(48_122_269, 0).saturating_mul(x.into())) + // Standard Error: 505_065 + .saturating_add(Weight::from_parts(29_215_314, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(y.into()))) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(y.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2234).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 28).saturating_mul(z.into())) + } + /// Storage: `ParachainStaking::DelayedPayouts` (r:1 w:0) + /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:2 w:1) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:1 w:1) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::OrbiterPerRound` (r:1 w:0) + /// Proof: `MoonbeamOrbiters::OrbiterPerRound` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:301 w:301) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `y` is `[0, 300]`. + fn pay_one_collator_reward(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1632 + y * (161 ±0)` + // Estimated: `7342 + y * (2591 ±0)` + // Minimum execution time: 47_848_000 picoseconds. + Weight::from_parts(45_087_022, 7342) + // Standard Error: 4_616 + .saturating_add(Weight::from_parts(14_463_358, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(y.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(y.into())) + } + fn base_on_initialize() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 792_000 picoseconds. + Weight::from_parts(840_000, 0) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 300]`. + /// The range of component `y` is `[0, 100]`. + fn set_auto_compound(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `672 + x * (22 ±0) + y * (36 ±0)` + // Estimated: `4028 + x * (23 ±0) + y * (36 ±0)` + // Minimum execution time: 24_649_000 picoseconds. + Weight::from_parts(24_852_289, 4028) + // Standard Error: 395 + .saturating_add(Weight::from_parts(40_606, 0).saturating_mul(x.into())) + // Standard Error: 1_185 + .saturating_add(Weight::from_parts(41_268, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 23).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 350]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 99]`. + fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (60 ±0) + y * (21 ±0) + z * (78 ±0)` + // Estimated: `26217 + x * (44 ±0) + y * (19 ±0) + z * (76 ±0)` + // Minimum execution time: 95_653_000 picoseconds. + Weight::from_parts(79_711_783, 26217) + // Standard Error: 1_052 + .saturating_add(Weight::from_parts(110_031, 0).saturating_mul(x.into())) + // Standard Error: 3_716 + .saturating_add(Weight::from_parts(184_852, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 19).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 76).saturating_mul(z.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:2 w:2) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:2 w:2) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:2 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(37), added: 2512, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn delegate_with_auto_compound_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `48131` + // Estimated: `54071` + // Minimum execution time: 213_168_000 picoseconds. + Weight::from_parts(215_021_000, 54071) + .saturating_add(T::DbWeight::get().reads(15_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn mint_collator_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `128` + // Estimated: `3581` + // Minimum execution time: 16_105_000 picoseconds. + Weight::from_parts(16_522_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::EnableMarkingOffline` (r:1 w:0) + /// Proof: `ParachainStaking::EnableMarkingOffline` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:2 w:0) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:2 w:0) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MoonbeamOrbiters::OrbiterPerRound` (r:1 w:0) + /// Proof: `MoonbeamOrbiters::OrbiterPerRound` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_inactive_collator() -> Weight { + // Proof Size summary in bytes: + // Measured: `11566` + // Estimated: `17506` + // Minimum execution time: 69_389_000 picoseconds. + Weight::from_parts(70_737_000, 17506) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_precompile_benchmarks.rs b/tracing/2900/runtime/common/src/weights/pallet_precompile_benchmarks.rs new file mode 100644 index 00000000..96ec49fd --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_precompile_benchmarks.rs @@ -0,0 +1,69 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_precompile_benchmarks` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_precompile_benchmarks +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_precompile_benchmarks`. +pub struct WeightInfo(PhantomData); +impl pallet_precompile_benchmarks::WeightInfo for WeightInfo { + /// The range of component `x` is `[100, 2000]`. + fn verify_entry(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 75_902_000 picoseconds. + Weight::from_parts(76_529_000, 0) + // Standard Error: 2_414 + .saturating_add(Weight::from_parts(667_294, 0).saturating_mul(x.into())) + } + /// Storage: `RelayStorageRoots::RelayStorageRootKeys` (r:1 w:0) + /// Proof: `RelayStorageRoots::RelayStorageRootKeys` (`max_values`: Some(1), `max_size`: Some(121), added: 616, mode: `MaxEncodedLen`) + fn latest_relay_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `227` + // Estimated: `1606` + // Minimum execution time: 4_691_000 picoseconds. + Weight::from_parts(4_871_000, 1606) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_preimage.rs b/tracing/2900/runtime/common/src/weights/pallet_preimage.rs new file mode 100644 index 00000000..00d48781 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_preimage.rs @@ -0,0 +1,251 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_preimage` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_preimage +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_preimage`. +pub struct WeightInfo(PhantomData); +impl pallet_preimage::WeightInfo for WeightInfo { + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// 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 `s` is `[0, 4194304]`. + fn note_preimage(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3544` + // Minimum execution time: 46_791_000 picoseconds. + Weight::from_parts(47_593_000, 3544) + // Standard Error: 7 + .saturating_add(Weight::from_parts(2_265, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// 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 `s` is `[0, 4194304]`. + fn note_requested_preimage(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3544` + // Minimum execution time: 15_130_000 picoseconds. + Weight::from_parts(15_406_000, 3544) + // Standard Error: 4 + .saturating_add(Weight::from_parts(2_230, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// 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 `s` is `[0, 4194304]`. + fn note_no_deposit_preimage(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3544` + // Minimum execution time: 14_306_000 picoseconds. + Weight::from_parts(14_766_000, 3544) + // Standard Error: 4 + .saturating_add(Weight::from_parts(2_229, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + fn unnote_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `220` + // Estimated: `3544` + // Minimum execution time: 49_118_000 picoseconds. + Weight::from_parts(50_474_000, 3544) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + fn unnote_no_deposit_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `3544` + // Minimum execution time: 21_773_000 picoseconds. + Weight::from_parts(22_501_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn request_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `176` + // Estimated: `3544` + // Minimum execution time: 17_577_000 picoseconds. + Weight::from_parts(18_107_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn request_no_deposit_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `3544` + // Minimum execution time: 12_826_000 picoseconds. + Weight::from_parts(13_152_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn request_unnoted_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3544` + // Minimum execution time: 15_147_000 picoseconds. + Weight::from_parts(16_469_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn request_requested_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3544` + // Minimum execution time: 10_434_000 picoseconds. + Weight::from_parts(10_732_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) + fn unrequest_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `144` + // Estimated: `3544` + // Minimum execution time: 18_677_000 picoseconds. + Weight::from_parts(19_164_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn unrequest_unnoted_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3544` + // Minimum execution time: 10_197_000 picoseconds. + Weight::from_parts(10_613_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn unrequest_multi_referenced_preimage() -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3544` + // Minimum execution time: 10_107_000 picoseconds. + Weight::from_parts(10_496_000, 3544) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Preimage::StatusFor` (r:1023 w:1023) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1023 w:1023) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1023 w:1023) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:0 w:1023) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1024]`. + fn ensure_updated(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1325 + n * (203 ±0)` + // Estimated: `990 + n * (2591 ±0)` + // Minimum execution time: 54_379_000 picoseconds. + Weight::from_parts(55_099_000, 990) + // Standard Error: 28_761 + .saturating_add(Weight::from_parts(53_321_985, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(n.into())) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_proxy.rs b/tracing/2900/runtime/common/src/weights/pallet_proxy.rs new file mode 100644 index 00000000..1b9557f2 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_proxy.rs @@ -0,0 +1,217 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_proxy +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `191 + p * (25 ±0)` + // Estimated: `4310 + p * (25 ±0)` + // Minimum execution time: 14_996_000 picoseconds. + Weight::from_parts(15_655_775, 4310) + // Standard Error: 971 + .saturating_add(Weight::from_parts(32_864, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(0, 25).saturating_mul(p.into())) + // 1 DB read that happen when filtering the proxy call transaction + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `441 + a * (56 ±0) + p * (25 ±0)` + // Estimated: `5302 + a * (60 ±0) + p * (22 ±0)` + // Minimum execution time: 38_376_000 picoseconds. + Weight::from_parts(38_873_739, 5302) + // Standard Error: 2_130 + .saturating_add(Weight::from_parts(148_768, 0).saturating_mul(a.into())) + // Standard Error: 2_201 + .saturating_add(Weight::from_parts(18_553, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 60).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 22).saturating_mul(p.into())) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, _p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `329 + a * (56 ±0)` + // Estimated: `5302` + // Minimum execution time: 22_494_000 picoseconds. + Weight::from_parts(23_762_783, 5302) + // Standard Error: 4_665 + .saturating_add(Weight::from_parts(172_490, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, _p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `329 + a * (56 ±0)` + // Estimated: `5302` + // Minimum execution time: 22_352_000 picoseconds. + Weight::from_parts(23_872_044, 5302) + // Standard Error: 4_564 + .saturating_add(Weight::from_parts(171_997, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(1837), added: 4312, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `343 + a * (56 ±0) + p * (25 ±0)` + // Estimated: `5302` + // Minimum execution time: 29_508_000 picoseconds. + Weight::from_parts(32_411_876, 5302) + // Standard Error: 2_224 + .saturating_add(Weight::from_parts(161_492, 0).saturating_mul(a.into())) + // Standard Error: 2_298 + .saturating_add(Weight::from_parts(19_882, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `149 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 22_057_000 picoseconds. + Weight::from_parts(22_782_046, 4310) + // Standard Error: 1_051 + .saturating_add(Weight::from_parts(35_662, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `149 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 21_604_000 picoseconds. + Weight::from_parts(22_877_238, 4310) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(31_231, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `149 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 21_184_000 picoseconds. + Weight::from_parts(22_024_457, 4310) + // Standard Error: 1_120 + .saturating_add(Weight::from_parts(34_085, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `161` + // Estimated: `4310` + // Minimum execution time: 23_011_000 picoseconds. + Weight::from_parts(24_057_173, 4310) + // Standard Error: 1_110 + .saturating_add(Weight::from_parts(11_499, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(845), added: 3320, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `174 + p * (25 ±0)` + // Estimated: `4310` + // Minimum execution time: 21_882_000 picoseconds. + Weight::from_parts(22_687_947, 4310) + // Standard Error: 938 + .saturating_add(Weight::from_parts(30_922, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_randomness.rs b/tracing/2900/runtime/common/src/weights/pallet_randomness.rs new file mode 100644 index 00000000..d2f4d60b --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_randomness.rs @@ -0,0 +1,162 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_randomness` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_randomness +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_randomness`. +pub struct WeightInfo(PhantomData); +impl pallet_randomness::WeightInfo for WeightInfo { + /// Storage: `Randomness::RelayEpoch` (r:1 w:1) + /// Proof: `Randomness::RelayEpoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::RelayStateProof` (r:1 w:0) + /// Proof: `ParachainSystem::RelayStateProof` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:1) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::InherentIncluded` (r:0 w:1) + /// Proof: `Randomness::InherentIncluded` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_babe_randomness_results() -> Weight { + // Proof Size summary in bytes: + // Measured: `297` + // Estimated: `3762` + // Minimum execution time: 13_127_000 picoseconds. + Weight::from_parts(13_446_000, 3762) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Randomness::NotFirstBlock` (r:1 w:0) + /// Proof: `Randomness::NotFirstBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AuthorMapping::MappingWithDeposit` (r:1 w:0) + /// Proof: `AuthorMapping::MappingWithDeposit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::LocalVrfOutput` (r:1 w:1) + /// Proof: `Randomness::LocalVrfOutput` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:1) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn on_initialize() -> Weight { + // Proof Size summary in bytes: + // Measured: `719` + // Estimated: `4184` + // Minimum execution time: 512_629_000 picoseconds. + Weight::from_parts(514_922_000, 4184) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Randomness::RequestCount` (r:1 w:1) + /// Proof: `Randomness::RequestCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:1) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::Requests` (r:0 w:1) + /// Proof: `Randomness::Requests` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn request_randomness() -> Weight { + // Proof Size summary in bytes: + // Measured: `549` + // Estimated: `6172` + // Minimum execution time: 54_291_000 picoseconds. + Weight::from_parts(55_178_000, 6172) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Randomness::Requests` (r:1 w:0) + /// Proof: `Randomness::Requests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:0) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 100]`. + fn prepare_fulfillment(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `412` + // Estimated: `3877` + // Minimum execution time: 9_589_000 picoseconds. + Weight::from_parts(10_126_652, 3877) + // Standard Error: 301 + .saturating_add(Weight::from_parts(262_389, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:1) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Randomness::Requests` (r:0 w:1) + /// Proof: `Randomness::Requests` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn finish_fulfillment() -> Weight { + // Proof Size summary in bytes: + // Measured: `739` + // Estimated: `6172` + // Minimum execution time: 50_250_000 picoseconds. + Weight::from_parts(51_170_000, 6172) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Randomness::Requests` (r:1 w:1) + /// Proof: `Randomness::Requests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn increase_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `825` + // Estimated: `6172` + // Minimum execution time: 46_960_000 picoseconds. + Weight::from_parts(47_761_000, 6172) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Randomness::Requests` (r:1 w:1) + /// Proof: `Randomness::Requests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Randomness::RandomnessResults` (r:1 w:1) + /// Proof: `Randomness::RandomnessResults` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_request_expiration() -> Weight { + // Proof Size summary in bytes: + // Measured: `868` + // Estimated: `6172` + // Minimum execution time: 54_447_000 picoseconds. + Weight::from_parts(55_170_000, 6172) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_referenda.rs b/tracing/2900/runtime/common/src/weights/pallet_referenda.rs new file mode 100644 index 00000000..4a0459b5 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_referenda.rs @@ -0,0 +1,469 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_referenda` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_referenda +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_referenda`. +pub struct WeightInfo(PhantomData); +impl pallet_referenda::WeightInfo for WeightInfo { + /// 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(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `269` + // Estimated: `42428` + // Minimum execution time: 32_827_000 picoseconds. + Weight::from_parts(33_549_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_preparing() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `83866` + // Minimum execution time: 42_052_000 picoseconds. + Weight::from_parts(43_028_000, 83866) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3303` + // Estimated: `42428` + // Minimum execution time: 58_526_000 picoseconds. + Weight::from_parts(60_058_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_not_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3323` + // Estimated: `42428` + // Minimum execution time: 58_289_000 picoseconds. + Weight::from_parts(59_858_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `83866` + // Minimum execution time: 53_199_000 picoseconds. + Weight::from_parts(54_655_000, 83866) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `83866` + // Minimum execution time: 48_847_000 picoseconds. + Weight::from_parts(49_711_000, 83866) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + fn refund_decision_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `453` + // Estimated: `4377` + // Minimum execution time: 27_623_000 picoseconds. + Weight::from_parts(28_687_000, 4377) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + fn refund_submission_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `378` + // Estimated: `4377` + // Minimum execution time: 27_406_000 picoseconds. + Weight::from_parts(28_129_000, 4377) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn cancel() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `83866` + // Minimum execution time: 28_691_000 picoseconds. + Weight::from_parts(29_412_000, 83866) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:0) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn kill() -> Weight { + // Proof Size summary in bytes: + // Measured: `884` + // Estimated: `83866` + // Minimum execution time: 95_480_000 picoseconds. + Weight::from_parts(97_420_000, 83866) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:0) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + fn one_fewer_deciding_queue_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `102` + // Estimated: `5477` + // Minimum execution time: 9_372_000 picoseconds. + Weight::from_parts(9_658_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn one_fewer_deciding_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `3090` + // Estimated: `42428` + // Minimum execution time: 41_452_000 picoseconds. + Weight::from_parts(42_664_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn one_fewer_deciding_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `3090` + // Estimated: `42428` + // Minimum execution time: 43_157_000 picoseconds. + Weight::from_parts(44_712_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_requeued_insertion() -> Weight { + // Proof Size summary in bytes: + // Measured: `2915` + // Estimated: `5477` + // Minimum execution time: 23_193_000 picoseconds. + Weight::from_parts(24_048_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_requeued_slide() -> Weight { + // Proof Size summary in bytes: + // Measured: `2915` + // Estimated: `5477` + // Minimum execution time: 22_963_000 picoseconds. + Weight::from_parts(23_854_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `2919` + // Estimated: `5477` + // Minimum execution time: 27_080_000 picoseconds. + Weight::from_parts(27_852_000, 5477) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_not_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `2939` + // Estimated: `5477` + // Minimum execution time: 27_082_000 picoseconds. + Weight::from_parts(27_867_000, 5477) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_no_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `249` + // Estimated: `42428` + // Minimum execution time: 19_614_000 picoseconds. + Weight::from_parts(20_158_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_preparing() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `42428` + // Minimum execution time: 20_156_000 picoseconds. + Weight::from_parts(20_537_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + fn nudge_referendum_timed_out() -> Weight { + // Proof Size summary in bytes: + // Measured: `194` + // Estimated: `4377` + // Minimum execution time: 12_796_000 picoseconds. + Weight::from_parts(13_096_000, 4377) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_deciding_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `42428` + // Minimum execution time: 26_036_000 picoseconds. + Weight::from_parts(26_786_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_deciding_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `285` + // Estimated: `42428` + // Minimum execution time: 27_742_000 picoseconds. + Weight::from_parts(28_339_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `338` + // Estimated: `42428` + // Minimum execution time: 22_841_000 picoseconds. + Weight::from_parts(23_443_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_end_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `321` + // Estimated: `42428` + // Minimum execution time: 23_140_000 picoseconds. + Weight::from_parts(23_618_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_continue_not_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `338` + // Estimated: `42428` + // Minimum execution time: 21_869_000 picoseconds. + Weight::from_parts(22_501_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_continue_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `342` + // Estimated: `42428` + // Minimum execution time: 21_447_000 picoseconds. + Weight::from_parts(21_862_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + fn nudge_referendum_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `342` + // Estimated: `83866` + // Minimum execution time: 32_823_000 picoseconds. + Weight::from_parts(33_860_000, 83866) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_rejected() -> Weight { + // Proof Size summary in bytes: + // Measured: `338` + // Estimated: `42428` + // Minimum execution time: 22_972_000 picoseconds. + Weight::from_parts(23_681_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:0 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn set_some_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `338` + // Estimated: `4377` + // Minimum execution time: 18_814_000 picoseconds. + Weight::from_parts(19_354_000, 4377) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(912), added: 3387, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `271` + // Estimated: `4377` + // Minimum execution time: 14_836_000 picoseconds. + Weight::from_parts(15_234_000, 4377) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_relay_storage_roots.rs b/tracing/2900/runtime/common/src/weights/pallet_relay_storage_roots.rs new file mode 100644 index 00000000..c41982ae --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_relay_storage_roots.rs @@ -0,0 +1,64 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_relay_storage_roots` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_relay_storage_roots +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_relay_storage_roots`. +pub struct WeightInfo(PhantomData); +impl pallet_relay_storage_roots::WeightInfo for WeightInfo { + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `RelayStorageRoots::RelayStorageRoot` (r:1 w:2) + /// Proof: `RelayStorageRoots::RelayStorageRoot` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `RelayStorageRoots::RelayStorageRootKeys` (r:1 w:1) + /// Proof: `RelayStorageRoots::RelayStorageRootKeys` (`max_values`: Some(1), `max_size`: Some(121), added: 616, mode: `MaxEncodedLen`) + fn set_relay_storage_root() -> Weight { + // Proof Size summary in bytes: + // Measured: `972` + // Estimated: `3509` + // Minimum execution time: 17_372_000 picoseconds. + Weight::from_parts(17_813_000, 3509) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_scheduler.rs b/tracing/2900/runtime/common/src/weights/pallet_scheduler.rs new file mode 100644 index 00000000..2863e317 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_scheduler.rs @@ -0,0 +1,196 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_scheduler` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_scheduler +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_scheduler`. +pub struct WeightInfo(PhantomData); +impl pallet_scheduler::WeightInfo for WeightInfo { + /// Storage: `Scheduler::IncompleteSince` (r:1 w:1) + /// Proof: `Scheduler::IncompleteSince` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn service_agendas_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `31` + // Estimated: `1489` + // Minimum execution time: 3_104_000 picoseconds. + Weight::from_parts(3_295_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 50]`. + fn service_agenda_base(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `78 + s * (177 ±0)` + // Estimated: `42428` + // Minimum execution time: 3_587_000 picoseconds. + Weight::from_parts(6_435_444, 42428) + // Standard Error: 1_351 + .saturating_add(Weight::from_parts(345_330, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn service_task_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_225_000 picoseconds. + Weight::from_parts(3_398_000, 0) + } + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// The range of component `s` is `[128, 4194304]`. + fn service_task_fetched(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `179 + s * (1 ±0)` + // Estimated: `3644 + s * (1 ±0)` + // Minimum execution time: 17_780_000 picoseconds. + Weight::from_parts(18_049_000, 3644) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_218, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) + } + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + fn service_task_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_829_000 picoseconds. + Weight::from_parts(5_019_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn service_task_periodic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_171_000 picoseconds. + Weight::from_parts(3_408_000, 0) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_dispatch_signed() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 4_346_000 picoseconds. + Weight::from_parts(4_595_000, 1527) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn execute_dispatch_unsigned() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_129_000 picoseconds. + Weight::from_parts(2_298_000, 0) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 49]`. + fn schedule(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `78 + s * (177 ±0)` + // Estimated: `42428` + // Minimum execution time: 10_293_000 picoseconds. + Weight::from_parts(12_886_613, 42428) + // Standard Error: 1_265 + .saturating_add(Weight::from_parts(373_933, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 50]`. + fn cancel(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `78 + s * (177 ±0)` + // Estimated: `42428` + // Minimum execution time: 13_648_000 picoseconds. + Weight::from_parts(13_302_568, 42428) + // Standard Error: 911 + .saturating_add(Weight::from_parts(578_580, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 49]`. + fn schedule_named(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + s * (185 ±0)` + // Estimated: `42428` + // Minimum execution time: 13_612_000 picoseconds. + Weight::from_parts(16_943_772, 42428) + // Standard Error: 2_062 + .saturating_add(Weight::from_parts(406_396, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 50]`. + fn cancel_named(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `281 + s * (185 ±0)` + // Estimated: `42428` + // Minimum execution time: 15_726_000 picoseconds. + Weight::from_parts(16_031_966, 42428) + // Standard Error: 1_907 + .saturating_add(Weight::from_parts(621_828, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_sudo.rs b/tracing/2900/runtime/common/src/weights/pallet_sudo.rs new file mode 100644 index 00000000..560d1f3d --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_sudo.rs @@ -0,0 +1,91 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_sudo` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_sudo +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_sudo`. +pub struct WeightInfo(PhantomData); +impl pallet_sudo::WeightInfo for WeightInfo { + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn set_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `1505` + // Minimum execution time: 9_134_000 picoseconds. + Weight::from_parts(9_369_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn sudo() -> Weight { + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `1505` + // Minimum execution time: 9_886_000 picoseconds. + Weight::from_parts(10_411_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn sudo_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `1505` + // Minimum execution time: 9_917_000 picoseconds. + Weight::from_parts(10_293_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Sudo::Key` (r:1 w:1) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + fn remove_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `153` + // Estimated: `1505` + // Minimum execution time: 8_231_000 picoseconds. + Weight::from_parts(8_704_000, 1505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_timestamp.rs b/tracing/2900/runtime/common/src/weights/pallet_timestamp.rs new file mode 100644 index 00000000..5c4ece73 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_timestamp.rs @@ -0,0 +1,67 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_timestamp` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_timestamp +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_timestamp`. +pub struct WeightInfo(PhantomData); +impl pallet_timestamp::WeightInfo for WeightInfo { + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + fn set() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `1493` + // Minimum execution time: 4_623_000 picoseconds. + Weight::from_parts(4_903_000, 1493) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_finalize() -> Weight { + // Proof Size summary in bytes: + // Measured: `57` + // Estimated: `0` + // Minimum execution time: 3_322_000 picoseconds. + Weight::from_parts(3_492_000, 0) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_treasury.rs b/tracing/2900/runtime/common/src/weights/pallet_treasury.rs new file mode 100644 index 00000000..5e350e55 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_treasury.rs @@ -0,0 +1,187 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_treasury` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_treasury +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_treasury`. +pub struct WeightInfo(PhantomData); +impl pallet_treasury::WeightInfo for WeightInfo { + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn spend_local() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `1887` + // Minimum execution time: 11_763_000 picoseconds. + Weight::from_parts(12_162_000, 1887) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn propose_spend() -> Weight { + // Proof Size summary in bytes: + // Measured: `446` + // Estimated: `1489` + // Minimum execution time: 25_433_000 picoseconds. + Weight::from_parts(26_250_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Treasury::Proposals` (r:1 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn reject_proposal() -> Weight { + // Proof Size summary in bytes: + // Measured: `619` + // Estimated: `6172` + // Minimum execution time: 47_079_000 picoseconds. + Weight::from_parts(48_432_000, 6172) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Treasury::Proposals` (r:1 w:0) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 99]`. + fn approve_proposal(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `575 + p * (8 ±0)` + // Estimated: `3549` + // Minimum execution time: 8_884_000 picoseconds. + Weight::from_parts(11_714_738, 3549) + // Standard Error: 1_462 + .saturating_add(Weight::from_parts(78_570, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + fn remove_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `232` + // Estimated: `1887` + // Minimum execution time: 7_051_000 picoseconds. + Weight::from_parts(7_284_000, 1887) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Deactivated` (r:1 w:0) + /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:99 w:0) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 99]`. + fn on_initialize_proposals(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `356 + p * (97 ±0)` + // Estimated: `3581 + p * (2559 ±0)` + // Minimum execution time: 16_123_000 picoseconds. + Weight::from_parts(16_106_571, 3581) + // Standard Error: 6_320 + .saturating_add(Weight::from_parts(3_350_601, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 2559).saturating_mul(p.into())) + } + /// Storage: `Treasury::SpendCount` (r:1 w:1) + /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Spends` (r:0 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + fn spend() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `1489` + // Minimum execution time: 10_468_000 picoseconds. + Weight::from_parts(11_009_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn payout() -> Weight { + // Proof Size summary in bytes: + // Measured: `553` + // Estimated: `6172` + // Minimum execution time: 52_510_000 picoseconds. + Weight::from_parts(53_515_000, 6172) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + fn check_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `3522` + // Minimum execution time: 12_219_000 picoseconds. + Weight::from_parts(12_408_000, 3522) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + fn void_spend() -> Weight { + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `3522` + // Minimum execution time: 10_891_000 picoseconds. + Weight::from_parts(11_255_000, 3522) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_utility.rs b/tracing/2900/runtime/common/src/weights/pallet_utility.rs new file mode 100644 index 00000000..8bb2ed9b --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_utility.rs @@ -0,0 +1,105 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_utility` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_utility +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_utility`. +pub struct WeightInfo(PhantomData); +impl pallet_utility::WeightInfo for WeightInfo { + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `c` is `[0, 1000]`. + fn batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 4_486_000 picoseconds. + Weight::from_parts(6_860_468, 1527) + // Standard Error: 3_965 + .saturating_add(Weight::from_parts(4_026_122, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn as_derivative() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 6_909_000 picoseconds. + Weight::from_parts(7_328_000, 1527) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `c` is `[0, 1000]`. + fn batch_all(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 4_548_000 picoseconds. + Weight::from_parts(4_273_693, 1527) + // Standard Error: 3_802 + .saturating_add(Weight::from_parts(4_263_522, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn dispatch_as() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_579_000 picoseconds. + Weight::from_parts(6_808_000, 0) + } + /// Storage: `MaintenanceMode::MaintenanceMode` (r:1 w:0) + /// Proof: `MaintenanceMode::MaintenanceMode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `c` is `[0, 1000]`. + fn force_batch(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `1527` + // Minimum execution time: 4_440_000 picoseconds. + Weight::from_parts(4_897_102, 1527) + // Standard Error: 4_345 + .saturating_add(Weight::from_parts(4_032_053, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_whitelist.rs b/tracing/2900/runtime/common/src/weights/pallet_whitelist.rs new file mode 100644 index 00000000..f3fd82aa --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_whitelist.rs @@ -0,0 +1,118 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_whitelist` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_whitelist +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_whitelist`. +pub struct WeightInfo(PhantomData); +impl pallet_whitelist::WeightInfo for WeightInfo { + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn whitelist_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `46` + // Estimated: `3544` + // Minimum execution time: 16_848_000 picoseconds. + Weight::from_parts(17_387_000, 3544) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + fn remove_whitelisted_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `3544` + // Minimum execution time: 16_154_000 picoseconds. + Weight::from_parts(16_777_000, 3544) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 4194294]`. + fn dispatch_whitelisted_call(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `251 + n * (1 ±0)` + // Estimated: `3715 + n * (1 ±0)` + // Minimum execution time: 27_084_000 picoseconds. + Weight::from_parts(27_353_000, 3715) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(79), added: 2554, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 10000]`. + fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `175` + // Estimated: `3544` + // Minimum execution time: 20_164_000 picoseconds. + Weight::from_parts(20_913_813, 3544) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_382, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_xcm.rs b/tracing/2900/runtime/common/src/weights/pallet_xcm.rs new file mode 100644 index 00000000..80d9f5f9 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_xcm.rs @@ -0,0 +1,318 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-02-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("moonbase-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_xcm +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --output=./runtime/common/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 23_849_000 picoseconds. + Weight::from_parts(24_416_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `AssetManager::AssetTypeId` (r:1 w:0) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3541` + // Minimum execution time: 27_460_000 picoseconds. + Weight::from_parts(28_098_000, 0) + .saturating_add(Weight::from_parts(0, 3541)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn execute() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_970_000 picoseconds. + Weight::from_parts(8_200_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_023_000 picoseconds. + Weight::from_parts(8_437_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_default_xcm_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_570_000 picoseconds. + Weight::from_parts(2_861_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_subscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 26_700_000 picoseconds. + Weight::from_parts(27_526_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn force_unsubscribe_version_notify() -> Weight { + // Proof Size summary in bytes: + // Measured: `328` + // Estimated: `3793` + // Minimum execution time: 28_689_000 picoseconds. + Weight::from_parts(29_471_000, 0) + .saturating_add(Weight::from_parts(0, 3793)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn force_suspension() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_629_000 picoseconds. + Weight::from_parts(2_840_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_supported_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `167` + // Estimated: `11057` + // Minimum execution time: 24_006_000 picoseconds. + Weight::from_parts(24_516_000, 0) + .saturating_add(Weight::from_parts(0, 11057)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notifiers() -> Weight { + // Proof Size summary in bytes: + // Measured: `171` + // Estimated: `11061` + // Minimum execution time: 23_352_000 picoseconds. + Weight::from_parts(24_140_000, 0) + .saturating_add(Weight::from_parts(0, 11061)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn already_notified_target() -> Weight { + // Proof Size summary in bytes: + // Measured: `178` + // Estimated: `13543` + // Minimum execution time: 28_644_000 picoseconds. + Weight::from_parts(29_377_000, 0) + .saturating_add(Weight::from_parts(0, 13543)) + .saturating_add(T::DbWeight::get().reads(5)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_current_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `6152` + // Minimum execution time: 27_677_000 picoseconds. + Weight::from_parts(28_668_000, 0) + .saturating_add(Weight::from_parts(0, 6152)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn notify_target_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `172` + // Estimated: `8587` + // Minimum execution time: 15_050_000 picoseconds. + Weight::from_parts(15_377_000, 0) + .saturating_add(Weight::from_parts(0, 8587)) + .saturating_add(T::DbWeight::get().reads(3)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn migrate_version_notify_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `178` + // Estimated: `11068` + // Minimum execution time: 24_211_000 picoseconds. + Weight::from_parts(24_757_000, 0) + .saturating_add(Weight::from_parts(0, 11068)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn migrate_and_notify_old_targets() -> Weight { + // Proof Size summary in bytes: + // Measured: `254` + // Estimated: `11144` + // Minimum execution time: 39_443_000 picoseconds. + Weight::from_parts(40_030_000, 0) + .saturating_add(Weight::from_parts(0, 11144)) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn new_query() -> Weight { + // Proof Size summary in bytes: + // Measured: `69` + // Estimated: `1554` + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(5_000_000, 0) + .saturating_add(Weight::from_parts(0, 1554)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::Queries` (r:1 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn take_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `7706` + // Estimated: `11171` + // Minimum execution time: 29_000_000 picoseconds. + Weight::from_parts(31_000_000, 0) + .saturating_add(Weight::from_parts(0, 11171)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/tracing/2900/runtime/common/src/weights/pallet_xcm_transactor.rs b/tracing/2900/runtime/common/src/weights/pallet_xcm_transactor.rs new file mode 100644 index 00000000..b357ce64 --- /dev/null +++ b/tracing/2900/runtime/common/src/weights/pallet_xcm_transactor.rs @@ -0,0 +1,192 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Autogenerated weights for `pallet_xcm_transactor` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("moonbase-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/moonbeam +// benchmark +// pallet +// --chain=moonbase-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_xcm_transactor +// --extrinsic=* +// --wasm-execution=compiled +// --header=./file_header.txt +// --template=./benchmarking/frame-weight-template.hbs +// --output=./runtime/common/src/weights/ + +#![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; + +/// Weights for `pallet_xcm_transactor`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_transactor::WeightInfo for WeightInfo { + /// Storage: `XcmTransactor::IndexToAccount` (r:1 w:1) + /// Proof: `XcmTransactor::IndexToAccount` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `3579` + // Minimum execution time: 9_646_000 picoseconds. + Weight::from_parts(9_987_000, 3579) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmTransactor::IndexToAccount` (r:0 w:1) + /// Proof: `XcmTransactor::IndexToAccount` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_810_000 picoseconds. + Weight::from_parts(5_995_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:0 w:1) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_transact_info() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_207_000 picoseconds. + Weight::from_parts(7_399_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:0 w:1) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_transact_info() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_469_000 picoseconds. + Weight::from_parts(6_698_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `XcmTransactor::DestinationAssetFeePerSecond` (r:0 w:1) + /// Proof: `XcmTransactor::DestinationAssetFeePerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_fee_per_second() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_491_000 picoseconds. + Weight::from_parts(6_877_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetManager::AssetIdType` (r:1 w:0) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::IndexToAccount` (r:1 w:0) + /// Proof: `XcmTransactor::IndexToAccount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::RelayIndices` (r:1 w:0) + /// Proof: `XcmTransactor::RelayIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:1 w:0) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::DestinationAssetFeePerSecond` (r:1 w:0) + /// Proof: `XcmTransactor::DestinationAssetFeePerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeId` (r:1 w:0) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn transact_through_derivative() -> Weight { + // Proof Size summary in bytes: + // Measured: `489` + // Estimated: `3954` + // Minimum execution time: 30_621_000 picoseconds. + Weight::from_parts(31_185_000, 3954) + .saturating_add(T::DbWeight::get().reads(7_u64)) + } + /// Storage: `AssetManager::AssetIdType` (r:1 w:0) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:1 w:0) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::DestinationAssetFeePerSecond` (r:1 w:0) + /// Proof: `XcmTransactor::DestinationAssetFeePerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetTypeId` (r:1 w:0) + /// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`) + fn transact_through_sovereign() -> Weight { + // Proof Size summary in bytes: + // Measured: `423` + // Estimated: `3888` + // Minimum execution time: 22_815_000 picoseconds. + Weight::from_parts(23_179_000, 3888) + .saturating_add(T::DbWeight::get().reads(5_u64)) + } + /// Storage: `AssetManager::AssetIdType` (r:1 w:0) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:1 w:0) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::DestinationAssetFeePerSecond` (r:1 w:0) + /// Proof: `XcmTransactor::DestinationAssetFeePerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn transact_through_signed() -> Weight { + // Proof Size summary in bytes: + // Measured: `467` + // Estimated: `3932` + // Minimum execution time: 38_105_000 picoseconds. + Weight::from_parts(38_977_000, 3932) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `XcmTransactor::RelayIndices` (r:1 w:0) + /// Proof: `XcmTransactor::RelayIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AssetManager::AssetIdType` (r:1 w:0) + /// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::TransactInfoWithWeightLimit` (r:1 w:0) + /// Proof: `XcmTransactor::TransactInfoWithWeightLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmTransactor::DestinationAssetFeePerSecond` (r:1 w:0) + /// Proof: `XcmTransactor::DestinationAssetFeePerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn hrmp_manage() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3936` + // Minimum execution time: 40_787_000 picoseconds. + Weight::from_parts(41_832_000, 3936) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} diff --git a/tracing/2900/runtime/moonbase/Cargo.toml b/tracing/2900/runtime/moonbase/Cargo.toml new file mode 100644 index 00000000..8536ea54 --- /dev/null +++ b/tracing/2900/runtime/moonbase/Cargo.toml @@ -0,0 +1,420 @@ +[package] +authors = { workspace = true } +build = "build.rs" +description = "Moonbase Runtime" +edition = "2021" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +name = "moonbase-runtime" +version = "0.8.4" + +[dependencies] +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +num_enum = { workspace = true } +rlp = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +sha3 = { workspace = true, optional = true } +smallvec = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } + +# Moonbeam +account = { workspace = true } +moonbeam-core-primitives = { workspace = true } +moonbeam-relay-encoder = { workspace = true } +moonbeam-runtime-common = { workspace = true } +precompile-utils = { workspace = true } +session-keys-primitives = { workspace = true } +xcm-primitives = { workspace = true } + +# Moonbeam pallets +moonbeam-xcm-benchmarks = { workspace = true } +pallet-asset-manager = { workspace = true } +pallet-author-mapping = { workspace = true } +pallet-crowdloan-rewards = { workspace = true } +pallet-erc20-xcm-bridge = { workspace = true } +pallet-ethereum-xcm = { workspace = true } +pallet-evm-chain-id = { workspace = true } +pallet-maintenance-mode = { workspace = true, features = ["xcm-support"] } +pallet-migrations = { workspace = true } +pallet-moonbeam-lazy-migrations = { workspace = true } +pallet-moonbeam-orbiters = { workspace = true } +pallet-parachain-staking = { workspace = true } +pallet-precompile-benchmarks = { workspace = true } +pallet-proxy-genesis-companion = { workspace = true } +pallet-randomness = { workspace = true } +pallet-xcm-transactor = { workspace = true } + +# Moonbeam precompiles +pallet-evm-precompile-author-mapping = { workspace = true } +pallet-evm-precompile-balances-erc20 = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } +pallet-evm-precompile-call-permit = { workspace = true } +pallet-evm-precompile-collective = { workspace = true } +pallet-evm-precompile-conviction-voting = { workspace = true } +pallet-evm-precompile-crowdloan-rewards = { workspace = true } +pallet-evm-precompile-gmp = { workspace = true } +pallet-evm-precompile-identity = { workspace = true } +pallet-evm-precompile-parachain-staking = { workspace = true } +pallet-evm-precompile-preimage = { workspace = true } +pallet-evm-precompile-proxy = { workspace = true } +pallet-evm-precompile-randomness = { workspace = true } +pallet-evm-precompile-referenda = { workspace = true } +pallet-evm-precompile-registry = { workspace = true } +pallet-evm-precompile-relay-encoder = { workspace = true } +pallet-evm-precompile-relay-verifier = { workspace = true } +pallet-evm-precompile-xcm-transactor = { workspace = true } +pallet-evm-precompile-xcm-utils = { workspace = true } +pallet-evm-precompile-xtokens = { workspace = true } +pallet-evm-precompileset-assets-erc20 = { workspace = true } + +# Moonbeam tracing +evm-tracing-events = { workspace = true, optional = true } +moonbeam-evm-tracer = { workspace = true, optional = true } +moonbeam-rpc-primitives-debug = { workspace = true } +moonbeam-rpc-primitives-txpool = { workspace = true } + +# Substrate +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true, features = ["insecure_zero_ed"] } +pallet-collective = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-identity = { workspace = true } +pallet-multisig = { workspace = true } +pallet-preimage = { workspace = true } +pallet-proxy = { workspace = true } +pallet-referenda = { workspace = true } +pallet-root-testing = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-society = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } +pallet-treasury = { workspace = true } +pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } +parity-scale-codec = { workspace = true, features = [ + "derive", + "max-encoded-len", + "chain-error", +] } +scale-info = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-debug-derive = { workspace = true } +sp-inherents = { workspace = true } +sp-io = { workspace = true, features = ["improved_panic_error_reporting"] } +sp-offchain = { workspace = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-version = { workspace = true } +sp-weights = { workspace = true } +sp-genesis-builder = { workspace = true } + +# Frontier +fp-evm = { workspace = true } +fp-rpc = { workspace = true } +fp-self-contained = { workspace = true, features = ["serde"] } +pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm-precompile-blake2 = { workspace = true } +pallet-evm-precompile-bn128 = { workspace = true } +pallet-evm-precompile-dispatch = { workspace = true } +pallet-evm-precompile-modexp = { workspace = true } +pallet-evm-precompile-sha3fips = { workspace = true } +pallet-evm-precompile-simple = { workspace = true } +pallet-evm-precompile-storage-cleaner = { workspace = true } + + +# Polkadot / XCM +orml-traits = { workspace = true } +orml-xcm-support = { workspace = true } +orml-xtokens = { workspace = true } +pallet-xcm = { workspace = true } +pallet-xcm-benchmarks = { workspace = true, optional = true } +pallet-message-queue = { workspace = true } +polkadot-core-primitives = { workspace = true } +polkadot-parachain = { workspace = true } +polkadot-runtime-common = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +# Cumulus +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +parachain-info = { workspace = true } +parachains-common = { workspace = true } + +# Moonkit +async-backing-primitives = { workspace = true } +moonkit-xcm-primitives = { workspace = true } +nimbus-primitives = { workspace = true } +pallet-async-backing = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-author-slot-filter = { workspace = true } +pallet-emergency-para-xcm = { workspace = true } +pallet-relay-storage-roots = { workspace = true } + +# Benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } +frame-try-runtime = { workspace = true, optional = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +default = ["std", "evm-tracing"] +std = [ + "account/std", + "async-backing-primitives/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "evm-tracing-events/std", + "fp-evm/std", + "fp-rpc/std", + "fp-self-contained/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "moonbeam-core-primitives/std", + "moonbeam-evm-tracer/std", + "moonbeam-relay-encoder/std", + "moonbeam-rpc-primitives-debug/std", + "moonbeam-rpc-primitives-txpool/std", + "moonbeam-runtime-common/std", + "moonkit-xcm-primitives/std", + "nimbus-primitives/std", + "orml-xtokens/std", + "pallet-asset-manager/std", + "pallet-assets/std", + "pallet-async-backing/std", + "pallet-author-inherent/std", + "pallet-author-mapping/std", + "pallet-author-slot-filter/std", + "pallet-balances/std", + "pallet-collective/std", + "pallet-conviction-voting/std", + "pallet-crowdloan-rewards/std", + "pallet-emergency-para-xcm/std", + "pallet-erc20-xcm-bridge/std", + "pallet-evm-chain-id/std", + "pallet-ethereum-xcm/std", + "pallet-ethereum/std", + "pallet-evm-precompile-author-mapping/std", + "pallet-evm-precompile-balances-erc20/std", + "pallet-evm-precompile-batch/std", + "pallet-evm-precompile-call-permit/std", + "pallet-evm-precompile-collective/std", + "pallet-evm-precompile-conviction-voting/std", + "pallet-evm-precompile-parachain-staking/std", + "pallet-evm-precompile-preimage/std", + "pallet-evm-precompile-randomness/std", + "pallet-evm-precompile-referenda/std", + "pallet-evm-precompile-registry/std", + "pallet-evm-precompile-relay-verifier/std", + "pallet-evm-precompile-xcm-transactor/std", + "pallet-evm-precompile-xcm-utils/std", + "pallet-evm-precompile-xtokens/std", + "pallet-evm-precompileset-assets-erc20/std", + "pallet-evm-precompile-storage-cleaner/std", + "pallet-evm/std", + "pallet-identity/std", + "pallet-maintenance-mode/std", + "pallet-migrations/std", + "pallet-moonbeam-lazy-migrations/std", + "pallet-moonbeam-orbiters/std", + "pallet-multisig/std", + "pallet-parachain-staking/std", + "pallet-precompile-benchmarks/std", + "pallet-preimage/std", + "pallet-proxy-genesis-companion/std", + "pallet-proxy/std", + "pallet-randomness/std", + "pallet-referenda/std", + "pallet-relay-storage-roots/std", + "pallet-root-testing/std", + "pallet-scheduler/std", + "pallet-society/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-treasury/std", + "pallet-utility/std", + "pallet-whitelist/std", + "pallet-xcm-transactor/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parity-scale-codec/std", + "precompile-utils/std", + "scale-info/std", + "session-keys-primitives/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "sp-genesis-builder/std", + "strum/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-primitives/std", + "xcm/std", +] + +# Must be enabled for tracing runtimes only +evm-tracing = ["evm-tracing-events", "moonbeam-evm-tracer", "rlp", "sha3"] + +# Allow to print logs details (no wasm:stripped) +force-debug = ["sp-debug-derive/force-debug"] + +# Will be enabled by the `wasm-builder` when building the runtime for WASM. +runtime-wasm = [] + +# A feature that should be enabled when the runtime should be build for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller like logging for example. +on-chain-release-build = ["sp-api/disable-logging"] + +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "moonbeam-relay-encoder/runtime-benchmarks", + "moonbeam-runtime-common/runtime-benchmarks", + "moonbeam-xcm-benchmarks/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "pallet-asset-manager/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-author-mapping/runtime-benchmarks", + "pallet-author-slot-filter/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-crowdloan-rewards/runtime-benchmarks", + "pallet-ethereum-xcm/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-identity/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", + "pallet-moonbeam-lazy-migrations/runtime-benchmarks", + "pallet-moonbeam-orbiters/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-parachain-staking/runtime-benchmarks", + "pallet-precompile-benchmarks/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-randomness/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-relay-storage-roots/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-society/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-treasury/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-transactor/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "session-keys-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] + +try-runtime = [ + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-dmp-queue/try-runtime", + "fp-self-contained/try-runtime", + "frame-executive/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime", + "moonbeam-runtime-common/try-runtime", + "pallet-asset-manager/try-runtime", + "pallet-author-mapping/try-runtime", + "pallet-author-slot-filter/try-runtime", + "pallet-balances/try-runtime", + "pallet-collective/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-emergency-para-xcm/try-runtime", + "pallet-maintenance-mode/try-runtime", + "pallet-migrations/try-runtime", + "pallet-moonbeam-lazy-migrations/try-runtime", + "pallet-parachain-staking/try-runtime", + "pallet-preimage/try-runtime", + "pallet-referenda/try-runtime", + "pallet-relay-storage-roots/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-society/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-xcm-transactor/try-runtime", + "pallet-xcm/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-utility/try-runtime", + "pallet-sudo/try-runtime", + "pallet-transaction-payment/try-runtime", + "parachain-info/try-runtime", + "pallet-evm-chain-id/try-runtime", + "parachain-info/try-runtime", + "pallet-evm/try-runtime", + "pallet-ethereum/try-runtime", + "pallet-treasury/try-runtime", + "pallet-author-inherent/try-runtime", + "pallet-crowdloan-rewards/try-runtime", + "pallet-proxy/try-runtime", + "pallet-identity/try-runtime", + "orml-xtokens/try-runtime", + "pallet-assets/try-runtime", + "pallet-xcm-transactor/try-runtime", + "pallet-proxy-genesis-companion/try-runtime", + "pallet-moonbeam-orbiters/try-runtime", + "pallet-ethereum-xcm/try-runtime", + "pallet-randomness/try-runtime", + "pallet-whitelist/try-runtime", + "pallet-erc20-xcm-bridge/try-runtime", + "pallet-multisig/try-runtime", + "pallet-async-backing/try-runtime", + "pallet-precompile-benchmarks/try-runtime", +] + +moonbase-runtime-benchmarks = [] diff --git a/tracing/2900/runtime/moonbase/build.rs b/tracing/2900/runtime/moonbase/build.rs new file mode 100644 index 00000000..3934b9c5 --- /dev/null +++ b/tracing/2900/runtime/moonbase/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/tracing/2900/runtime/moonbase/src/asset_config.rs b/tracing/2900/runtime/moonbase/src/asset_config.rs new file mode 100644 index 00000000..9040423e --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/asset_config.rs @@ -0,0 +1,221 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset configuration for Moonbase. +//! + +use crate::OpenTechCommitteeInstance; + +use super::{ + currency, governance, xcm_config, AccountId, AssetId, AssetManager, Assets, Balance, Balances, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; + +use frame_support::{ + dispatch::GetDispatchInfo, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse}, + weights::Weight, +}; + +use frame_system::{EnsureNever, EnsureRoot}; +use parity_scale_codec::{Compact, Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::H160; + +use sp_std::{ + convert::{From, Into}, + prelude::*, +}; + +// Number of items that can be destroyed with our configured max extrinsic proof size. +// x = (a - b) / c where: +// a: maxExtrinsic proof size +// b: base proof size for destroy_accounts in pallet_assets weights +// c: proof size for each item +// 656.87 = (3_407_872 - 8232) / 5180 +const REMOVE_ITEMS_LIMIT: u32 = 656; + +// Not to disrupt the previous asset instance, we assign () to Foreign +pub type ForeignAssetInstance = (); + +// For foreign assets, these parameters dont matter much +// as this will only be called by root with the forced arguments +// No deposit is substracted with those methods +parameter_types! { + pub const AssetDeposit: Balance = 100 * currency::UNIT * currency::SUPPLY_FACTOR; + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = currency::deposit(1,68); + pub const MetadataDepositPerByte: Balance = currency::deposit(0, 1); +} + +/// We allow Root and General Admin to execute privileged asset operations. +pub type AssetsForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +// Foreign assets +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = ConstU128<{ currency::deposit(1, 18) }>; + type WeightInfo = moonbeam_weights::pallet_assets::WeightInfo; + type RemoveItemsLimit = ConstU32<{ REMOVE_ITEMS_LIMIT }>; + type AssetIdParameter = Compact; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::{pallet_prelude::DispatchResult, transactional}; + +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + #[transactional] + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetRegistrarMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset.into(), + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + // Lastly, the metadata + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset.into(), + metadata.name, + metadata.symbol, + metadata.decimals, + metadata.is_frozen, + ) + } + + #[transactional] + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + // For us both of them (Foreign and Local) have the same annotated weight for a given + // witness + // We need to take the dispatch info from the destroy call, which is already annotated in + // the assets pallet + + // This is the dispatch info of destroy + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetRegistrarMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, + pub is_frozen: bool, +} + +pub type ForeignAssetModifierOrigin = EitherOfDiverse< + EnsureRoot, + EitherOfDiverse< + pallet_collective::EnsureProportionMoreThan, + governance::custom_origins::GeneralAdmin, + >, +>; + +pub type LocalAssetModifierOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetRegistrarMetadata; + type ForeignAssetType = xcm_config::AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = ForeignAssetModifierOrigin; + type WeightInfo = moonbeam_weights::pallet_asset_manager::WeightInfo; +} + +// Instruct how to go from an H160 to an AssetID +// We just take the lowest 128 bits +impl AccountIdAssetIdConversion for Runtime { + /// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF + /// and by taking the lowest 128 bits as the assetId + fn account_to_asset_id(account: AccountId) -> Option<(Vec, AssetId)> { + let h160_account: H160 = account.into(); + let mut data = [0u8; 16]; + let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(4); + if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX { + data.copy_from_slice(id_part); + let asset_id: AssetId = u128::from_be_bytes(data).into(); + Some((prefix_part.to_vec(), asset_id)) + } else { + None + } + } + + // The opposite conversion + fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId { + let mut data = [0u8; 20]; + data[0..4].copy_from_slice(prefix); + data[4..20].copy_from_slice(&asset_id.to_be_bytes()); + AccountId::from(data) + } +} diff --git a/tracing/2900/runtime/moonbase/src/governance/councils.rs b/tracing/2900/runtime/moonbase/src/governance/councils.rs new file mode 100644 index 00000000..d6addeae --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/governance/councils.rs @@ -0,0 +1,62 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; +use moonbeam_runtime_common::weights as moonbeam_weights; + +pub type TreasuryCouncilInstance = pallet_collective::Instance3; +pub type OpenTechCommitteeInstance = pallet_collective::Instance4; + +parameter_types! { + // TODO: Check value of this parameter + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * BlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for treasury council members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 3 * DAYS }>; + /// The maximum number of proposals that can be open in the treasury council at once. + type MaxProposals = ConstU32<20>; + /// The maximum number of treasury council members. + type MaxMembers = ConstU32<9>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for technical committee members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 14 * DAYS }>; + /// The maximum number of proposals that can be open in the technical committee at once. + type MaxProposals = ConstU32<100>; + /// The maximum number of technical committee members. + type MaxMembers = ConstU32<100>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} diff --git a/tracing/2900/runtime/moonbase/src/governance/mod.rs b/tracing/2900/runtime/moonbase/src/governance/mod.rs new file mode 100644 index 00000000..36a2c6be --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/governance/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Governance configurations + +pub mod councils; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{ + custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller, +}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/tracing/2900/runtime/moonbase/src/governance/origins.rs b/tracing/2900/runtime/moonbase/src/governance/origins.rs new file mode 100644 index 00000000..e8de3c35 --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/governance/origins.rs @@ -0,0 +1,83 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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. + +//! Custom origins for governance interventions. +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive( + PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString, + )] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + /// Fast General Admin + FastGeneralAdmin, + } + + 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!( + ReferendumCanceller, + ReferendumKiller, + WhitelistedCaller, + GeneralAdmin, + FastGeneralAdmin, + ); +} diff --git a/tracing/2900/runtime/moonbase/src/governance/referenda.rs b/tracing/2900/runtime/moonbase/src/governance/referenda.rs new file mode 100644 index 00000000..96bb3628 --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/governance/referenda.rs @@ -0,0 +1,101 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use crate::currency::*; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use moonbeam_runtime_common::weights as moonbeam_weights; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_conviction_voting::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub const SubmissionDeposit: Balance = 10 * UNIT * SUPPLY_FACTOR; + pub const UndecidingTimeout: BlockNumber = 21 * DAYS; +} + +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; + +/// The policy allows for Root or FastGeneralAdmin. +pub type FastGeneralAdminOrRoot = EitherOf, origins::FastGeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_whitelist::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast< + Self::AccountId, + OpenTechCommitteeInstance, + 5, + 9, + >, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +impl pallet_referenda::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_referenda::WeightInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + 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; +} diff --git a/tracing/2900/runtime/moonbase/src/governance/tracks.rs b/tracing/2900/runtime/moonbase/src/governance/tracks.rs new file mode 100644 index 00000000..72e18b9f --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/governance/tracks.rs @@ -0,0 +1,193 @@ +// Copyright 2022 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 crate::currency::{KILOUNIT, SUPPLY_FACTOR, UNIT}; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +use pallet_referenda::Curve; +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 6] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 100 * KILOUNIT * SUPPLY_FACTOR, + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 10 * KILOUNIT * SUPPLY_FACTOR, + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 500 * UNIT * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 10 * KILOUNIT * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(50)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 20 * KILOUNIT * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 5, + pallet_referenda::TrackInfo { + name: "fast_general_admin", + max_deciding: 10, + decision_deposit: 500 * UNIT * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(5, 14, percent(1), percent(0), percent(50)), + }, + ), +]; + +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(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks() + .into_iter() + .find(|(_, track)| track.name == "root") + { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in TRACKS_DATA { + assert!( + ::VoteLockingPeriod::get() + >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in TRACKS_DATA { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/tracing/2900/runtime/moonbase/src/lib.rs b/tracing/2900/runtime/moonbase/src/lib.rs new file mode 100644 index 00000000..58b1ba17 --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/lib.rs @@ -0,0 +1,1803 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! The Moonbase Runtime. +//! +//! Primary features of this runtime include: +//! * Ethereum compatibility +//! * Moonbase tokenomics + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "512"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub mod asset_config; +pub mod governance; +pub mod timestamp; +pub mod xcm_config; + +mod migrations; +mod precompiles; + +// Re-export required by get! macro. +#[cfg(feature = "std")] +pub use fp_evm::GenesisAccount; +pub use frame_support::traits::Get; +pub use moonbeam_core_primitives::{ + AccountId, AccountIndex, Address, AssetId, Balance, BlockNumber, DigestItem, Hash, Header, + Index, Signature, +}; +pub use pallet_author_slot_filter::EligibilityValue; +pub use pallet_parachain_staking::{weights::WeightInfo, InflationInfo, Range}; +pub use precompiles::{ + MoonbasePrecompiles, PrecompileName, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +use account::AccountId20; +use cumulus_pallet_parachain_system::{RelayChainStateProof, RelaychainDataProvider}; +use cumulus_primitives_core::{relay_chain, AggregateMessageOrigin}; +use fp_rpc::TransactionStatus; +use frame_support::{ + construct_runtime, + dispatch::{DispatchClass, GetDispatchInfo, PostDispatchInfo}, + ensure, + pallet_prelude::DispatchResult, + parameter_types, + traits::{ + fungible::{Balanced, Credit, HoldConsideration, Inspect}, + tokens::imbalance::ResolveTo, + tokens::{PayFromAccount, UnityAssetBalanceConversion}, + ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Contains, EitherOfDiverse, + EqualPrivilegeOnly, FindAuthor, Imbalance, InstanceFilter, LinearStoragePrice, OnFinalize, + OnUnbalanced, + }, + weights::{ + constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + PalletId, +}; + +use frame_system::{EnsureRoot, EnsureSigned}; +use governance::councils::*; +use moonbeam_rpc_primitives_txpool::TxPoolResponse; +use moonbeam_runtime_common::weights as moonbeam_weights; +use nimbus_primitives::CanAuthor; +use pallet_ethereum::Call::transact; +use pallet_ethereum::{PostLogContent, Transaction as EthereumTransaction}; +use pallet_evm::{ + Account as EVMAccount, EVMFungibleAdapter, EnsureAddressNever, EnsureAddressRoot, + FeeCalculator, GasWeightMapping, IdentityAddressMapping, + OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner, +}; +use pallet_transaction_payment::{FungibleAdapter, Multiplier, TargetedFeeAdjustment}; +use pallet_treasury::TreasuryAccountId; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_api::impl_runtime_apis; +use sp_consensus_slots::Slot; +use sp_core::{OpaqueMetadata, H160, H256, U256}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{ + BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentityLookup, + PostDispatchInfoOf, UniqueSaturatedInto, Zero, + }, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + }, + ApplyExtrinsicResult, DispatchErrorWithPostInfo, FixedPointNumber, Perbill, Permill, + Perquintill, +}; +use sp_std::{ + convert::{From, Into}, + prelude::*, +}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use smallvec::smallvec; +use sp_runtime::serde::{Deserialize, Serialize}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +pub type Precompiles = MoonbasePrecompiles; + +/// UNIT, the native token, uses 18 decimals of precision. +pub mod currency { + use super::Balance; + + // Provide a common factor between runtimes based on a supply of 10_000_000 tokens. + pub const SUPPLY_FACTOR: Balance = 1; + + pub const WEI: Balance = 1; + pub const KILOWEI: Balance = 1_000; + pub const MEGAWEI: Balance = 1_000_000; + pub const GIGAWEI: Balance = 1_000_000_000; + pub const MICROUNIT: Balance = 1_000_000_000_000; + pub const MILLIUNIT: Balance = 1_000_000_000_000_000; + pub const UNIT: Balance = 1_000_000_000_000_000_000; + pub const KILOUNIT: Balance = 1_000_000_000_000_000_000_000; + + pub const TRANSACTION_BYTE_FEE: Balance = 1 * GIGAWEI * SUPPLY_FACTOR; + pub const STORAGE_BYTE_FEE: Balance = 100 * MICROUNIT * SUPPLY_FACTOR; + pub const WEIGHT_FEE: Balance = 50 * KILOWEI * SUPPLY_FACTOR; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 1 * UNIT * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE + } +} + +/// Maximum weight per block +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX) + .saturating_mul(2) + .set_proof_size(relay_chain::MAX_POV_SIZE as u64); + +pub const MILLISECS_PER_BLOCK: u64 = 6_000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const WEEKS: BlockNumber = DAYS * 7; +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + pub type Block = generic::Block; + + impl_opaque_keys! { + pub struct SessionKeys { + pub nimbus: AuthorInherent, + pub vrf: session_keys_primitives::VrfSessionKey, + } + } +} + +/// This runtime version. +/// The spec_version is composed of 2x2 digits. The first 2 digits represent major changes +/// that can't be skipped, such as data migration upgrades. The last 2 digits represent minor +/// changes which can be skipped. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("moonbase"), + impl_name: create_runtime_str!("moonbase"), + authoring_version: 4, + spec_version: 2900, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 2, + state_version: 0, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +pub const NORMAL_WEIGHT: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_mul(3).saturating_div(4); +// Here we assume Ethereum's base fee of 21000 gas and convert to weight, but we +// subtract roughly the cost of a balance transfer from it (about 1/3 the cost) +// and some cost to account for per-byte-fee. +// TODO: we should use benchmarking's overhead feature to measure this +pub const EXTRINSIC_BASE_WEIGHT: Weight = Weight::from_parts(10000 * WEIGHT_PER_GAS, 0); + +pub struct RuntimeBlockWeights; +impl Get for RuntimeBlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .for_class(DispatchClass::Normal, |weights| { + weights.base_extrinsic = EXTRINSIC_BASE_WEIGHT; + weights.max_total = NORMAL_WEIGHT.into(); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = MAXIMUM_BLOCK_WEIGHT.into(); + weights.reserved = (MAXIMUM_BLOCK_WEIGHT - NORMAL_WEIGHT).into(); + }) + .avg_block_initialization(Perbill::from_percent(10)) + .build() + .expect("Provided BlockWeight definitions are valid, qed") + } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + /// TODO: this is left here so that `impl_runtime_apis_plus_common` will find the same type for + /// `BlockWeights` in all runtimes. It can probably be removed once the custom + /// `RuntimeBlockWeights` has been pushed to each runtime. + pub BlockWeights: frame_system::limits::BlockWeights = RuntimeBlockWeights::get(); + /// We allow for 5 MB blocks. + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); +} + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Index; + /// The index type for blocks. + type Block = Block; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// The aggregated RuntimeTask type. + type RuntimeTask = RuntimeTask; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = ConstU32<256>; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type BlockWeights = RuntimeBlockWeights; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type BlockLength = BlockLength; + /// Runtime version. + type Version = Version; + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = RocksDbWeight; + type BaseCallFilter = MaintenanceMode; + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = ConstU16<1287>; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = moonbeam_weights::pallet_utility::WeightInfo; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<3000>; + type WeightInfo = moonbeam_weights::pallet_timestamp::WeightInfo; +} + +impl pallet_balances::Config for Runtime { + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 4]; + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<0>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = moonbeam_weights::pallet_balances::WeightInfo; +} + +pub struct DealWithFees(sp_std::marker::PhantomData); +impl OnUnbalanced>> for DealWithFees +where + R: pallet_balances::Config + pallet_treasury::Config, +{ + // this seems to be called for substrate-based transactions + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>>, + ) { + if let Some(fees) = fees_then_tips.next() { + // for fees, 80% are burned, 20% to the treasury + let (_, to_treasury) = fees.ration(80, 20); + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + + // handle tip if there is one + if let Some(tip) = fees_then_tips.next() { + // for now we use the same burn/treasury strategy used for regular fees + let (_, to_treasury) = tip.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + } + } + } + + // this is called from pallet_evm for Ethereum-based transactions + // (technically, it calls on_unbalanced, which calls this when non-zero) + fn on_nonzero_unbalanced(amount: Credit>) { + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + let (_, to_treasury) = amount.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced(to_treasury); + } +} + +pub struct LengthToFee; +impl WeightToFeePolynomial for LengthToFee { + type Balance = Balance; + + fn polynomial() -> WeightToFeeCoefficients { + smallvec![ + WeightToFeeCoefficient { + degree: 1, + coeff_frac: Perbill::zero(), + coeff_integer: currency::TRANSACTION_BYTE_FEE, + negative: false, + }, + WeightToFeeCoefficient { + degree: 3, + coeff_frac: Perbill::zero(), + coeff_integer: 1 * currency::SUPPLY_FACTOR, + negative: false, + }, + ] + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = FungibleAdapter>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = ConstantMultiplier>; + type LengthToFee = LengthToFee; + type FeeMultiplierUpdate = FastAdjustingFeeUpdate; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = moonbeam_weights::pallet_sudo::WeightInfo; +} + +impl pallet_evm_chain_id::Config for Runtime {} + +/// Current approximation of the gas/s consumption considering +/// EVM execution over compiled WASM (on 4.4Ghz CPU). +/// Given the 2 sec Weight, from which 75% only are used for transactions, +/// the total EVM execution gas limit is: GAS_PER_SECOND * 2 * 0.75 ~= 60_000_000. +pub const GAS_PER_SECOND: u64 = 40_000_000; + +/// Approximate ratio of the amount of Weight per Gas. +/// u64 works for approximations because Weight is a very small unit compared to gas. +pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND; +/// The highest amount of new storage that can be created in a block (160KB). +pub const BLOCK_STORAGE_LIMIT: u64 = 160 * 1024; +parameter_types! { + pub BlockGasLimit: U256 + = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); + /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less + /// than this will decrease the weight and more will increase. + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(50); + /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to + /// change the fees more rapidly. This fast multiplier responds by doubling/halving in + /// approximately one hour at extreme block congestion levels. + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(4, 1_000); + /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure + /// that combined with `AdjustmentVariable`, we can recover from the minimum. + /// See `multiplier_can_grow_from_zero` in integration_tests.rs. + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 10); + /// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act + /// as a safety net. + pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128); + pub PrecompilesValue: MoonbasePrecompiles = MoonbasePrecompiles::<_>::new(); + pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); + /// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare + /// it with the pov_size for a block. E.g. + /// ceil( + /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS + /// ) + pub const GasLimitPovSizeRatio: u64 = 16; + /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT + /// (60_000_000 / 160 kb) + pub GasLimitStorageGrowthRatio: u64 = 366; +} + +pub struct TransactionPaymentAsGasPrice; +impl FeeCalculator for TransactionPaymentAsGasPrice { + fn min_gas_price() -> (U256, Weight) { + // TODO: transaction-payment differs from EIP-1559 in that its tip and length fees are not + // scaled by the multiplier, which means its multiplier will be overstated when + // applied to an ethereum transaction + // note: transaction-payment uses both a congestion modifier (next_fee_multiplier, which is + // updated once per block in on_finalize) and a 'WeightToFee' implementation. Our + // runtime implements this as a 'ConstantModifier', so we can get away with a simple + // multiplication here. + // It is imperative that `saturating_mul_int` be performed as late as possible in the + // expression since it involves fixed point multiplication with a division by a fixed + // divisor. This leads to truncation and subsequent precision loss if performed too early. + // This can lead to min_gas_price being same across blocks even if the multiplier changes. + // There's still some precision loss when the final `gas_price` (used_gas * min_gas_price) + // is computed in frontier, but that's currently unavoidable. + let min_gas_price = TransactionPayment::next_fee_multiplier() + .saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128)); + ( + min_gas_price.into(), + ::DbWeight::get().reads(1), + ) + } +} + +/// A "Fast" TargetedFeeAdjustment. Parameters chosen based on model described here: +/// https://research.web3.foundation/en/latest/polkadot/overview/2-token-economics.html#-1.-fast-adjusting-mechanism // editorconfig-checker-disable-line +/// +/// The adjustment algorithm boils down to: +/// +/// diff = (previous_block_weight - target) / maximum_block_weight +/// next_multiplier = prev_multiplier * (1 + (v * diff) + ((v * diff)^2 / 2)) +/// assert(next_multiplier > min) +/// where: v is AdjustmentVariable +/// target is TargetBlockFullness +/// min is MinimumMultiplier +pub type FastAdjustingFeeUpdate = TargetedFeeAdjustment< + R, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, +>; + +/// The author inherent provides an AccountId, but pallet evm needs an H160. +/// This simple adapter makes the conversion for any types T, U such that T: Into +pub struct FindAuthorAdapter(sp_std::marker::PhantomData<(T, U, Inner)>); + +impl FindAuthor for FindAuthorAdapter +where + T: Into, + Inner: FindAuthor, +{ + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + Inner::find_author(digests).map(Into::into) + } +} + +moonbeam_runtime_common::impl_on_charge_evm_transaction!(); + +impl pallet_evm::Config for Runtime { + type FeeCalculator = TransactionPaymentAsGasPrice; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = MoonbasePrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = EthereumChainId; + type OnChargeTransaction = OnChargeEVMTransaction>; + type BlockGasLimit = BlockGasLimit; + type FindAuthor = FindAuthorAdapter; + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = crate::timestamp::RelayTimestamp; + type WeightInfo = moonbeam_weights::pallet_evm::WeightInfo; +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = NORMAL_DISPATCH_RATIO * RuntimeBlockWeights::get().max_block; + pub const NoPreimagePostponement: Option = Some(10); +} + +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<50>; + type WeightInfo = moonbeam_weights::pallet_scheduler::WeightInfo; + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +parameter_types! { + pub const PreimageBaseDeposit: Balance = 5 * currency::UNIT * currency::SUPPLY_FACTOR ; + pub const PreimageByteDeposit: Balance = currency::STORAGE_BYTE_FEE; + pub const PreimageHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + +impl pallet_preimage::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_preimage::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +type TreasuryApproveOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +type TreasuryRejectOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionMoreThan, +>; + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + // At least three-fifths majority of the council is required (or root) to approve a proposal + type ApproveOrigin = TreasuryApproveOrigin; + // More than half of the council is required (or root) to reject a proposal + type RejectOrigin = TreasuryRejectOrigin; + type RuntimeEvent = RuntimeEvent; + // If spending proposal rejected, transfer proposer bond to treasury + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>; + type SpendPeriod = ConstU32<{ 6 * DAYS }>; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = ConstU32<100>; + type WeightInfo = moonbeam_weights::pallet_treasury::WeightInfo; + type SpendFunds = (); + type ProposalBondMaximum = (); + #[cfg(not(feature = "runtime-benchmarks"))] + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Disabled, no spending + #[cfg(feature = "runtime-benchmarks")] + type SpendOrigin = + frame_system::EnsureWithSuccess, AccountId, benches::MaxBalance>; + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<{ 30 * DAYS }>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +parameter_types! { + pub const MaxSubAccounts: u32 = 100; + pub const MaxAdditionalFields: u32 = 100; + pub const MaxRegistrars: u32 = 20; + pub const PendingUsernameExpiration: u32 = 7 * DAYS; + pub const MaxSuffixLength: u32 = 7; + pub const MaxUsernameLength: u32 = 32; +} + +type IdentityForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type IdentityRegistrarOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_identity::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + // Add one item in storage and take 258 bytes + type BasicDeposit = ConstU128<{ currency::deposit(1, 258) }>; + // Does not add any item to the storage but takes 1 bytes + type ByteDeposit = ConstU128<{ currency::deposit(0, 1) }>; + // Add one item in storage and take 53 bytes + type SubAccountDeposit = ConstU128<{ currency::deposit(1, 53) }>; + type MaxSubAccounts = MaxSubAccounts; + type IdentityInformation = pallet_identity::legacy::IdentityInfo; + type MaxRegistrars = MaxRegistrars; + type Slashed = Treasury; + type ForceOrigin = IdentityForceOrigin; + type RegistrarOrigin = IdentityRegistrarOrigin; + type OffchainSignature = Signature; + type SigningPublicKey = ::Signer; + type UsernameAuthorityOrigin = EnsureRoot; + type PendingUsernameExpiration = PendingUsernameExpiration; + type MaxSuffixLength = MaxSuffixLength; + type MaxUsernameLength = MaxUsernameLength; + type WeightInfo = moonbeam_weights::pallet_identity::WeightInfo; +} + +pub struct TransactionConverter; + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will immediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included +/// into the relay chain. +const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; +/// How many parachain blocks are processed by the relay chain per parent. Limits the +/// number of blocks authored per slot. +const BLOCK_PROCESSING_VELOCITY: u32 = 1; + +type ConsensusHook = pallet_async_backing::consensus_hook::FixedVelocityConsensusHook< + Runtime, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = ParachainInfo; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = EmergencyParaXcm; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = EmergencyParaXcm; + type ConsensusHook = crate::timestamp::ConsensusHookWrapperForRelayTimestamp; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_parachain_system::weights::SubstrateWeight; +} + +impl parachain_info::Config for Runtime {} + +pub struct OnNewRound; +impl pallet_parachain_staking::OnNewRound for OnNewRound { + fn on_new_round(round_index: pallet_parachain_staking::RoundIndex) -> Weight { + MoonbeamOrbiters::on_new_round(round_index) + } +} +pub struct PayoutCollatorOrOrbiterReward; +impl pallet_parachain_staking::PayoutCollatorReward for PayoutCollatorOrOrbiterReward { + fn payout_collator_reward( + for_round: pallet_parachain_staking::RoundIndex, + collator_id: AccountId, + amount: Balance, + ) -> Weight { + let extra_weight = + if MoonbeamOrbiters::is_collator_pool_with_active_orbiter(for_round, collator_id) { + MoonbeamOrbiters::distribute_rewards(for_round, collator_id, amount) + } else { + ParachainStaking::mint_collator_reward(for_round, collator_id, amount) + }; + + ::DbWeight::get() + .reads(1) + .saturating_add(extra_weight) + } +} + +pub struct OnInactiveCollator; +impl pallet_parachain_staking::OnInactiveCollator for OnInactiveCollator { + fn on_inactive_collator( + collator_id: AccountId, + round: pallet_parachain_staking::RoundIndex, + ) -> Result> { + let extra_weight = if !MoonbeamOrbiters::is_collator_pool_with_active_orbiter( + round, + collator_id.clone(), + ) { + ParachainStaking::go_offline_inner(collator_id)?; + ::WeightInfo::go_offline( + pallet_parachain_staking::MAX_CANDIDATES, + ) + } else { + Weight::zero() + }; + + Ok(::DbWeight::get() + .reads(1) + .saturating_add(extra_weight)) + } +} + +type MonetaryGovernanceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +pub struct RelayChainSlotProvider; +impl Get for RelayChainSlotProvider { + fn get() -> Slot { + let slot_info = pallet_async_backing::pallet::Pallet::::slot_info(); + slot_info.unwrap_or_default().0 + } +} + +impl pallet_parachain_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MonetaryGovernanceOrigin = MonetaryGovernanceOrigin; + /// Minimum round length is 2 minutes (10 * 12 second block times) + type MinBlocksPerRound = ConstU32<10>; + /// If a collator doesn't produce any block on this number of rounds, it is notified as inactive + type MaxOfflineRounds = ConstU32<2>; + /// Rounds before the collator leaving the candidates request can be executed + type LeaveCandidatesDelay = ConstU32<2>; + /// Rounds before the candidate bond increase/decrease can be executed + type CandidateBondLessDelay = ConstU32<2>; + /// Rounds before the delegator exit can be executed + type LeaveDelegatorsDelay = ConstU32<2>; + /// Rounds before the delegator revocation can be executed + type RevokeDelegationDelay = ConstU32<2>; + /// Rounds before the delegator bond increase/decrease can be executed + type DelegationBondLessDelay = ConstU32<2>; + /// Rounds before the reward is paid + type RewardPaymentDelay = ConstU32<2>; + /// Minimum collators selected per round, default at genesis and minimum forever after + type MinSelectedCandidates = ConstU32<8>; + /// Maximum top delegations per candidate + type MaxTopDelegationsPerCandidate = ConstU32<300>; + /// Maximum bottom delegations per candidate + type MaxBottomDelegationsPerCandidate = ConstU32<50>; + /// Maximum delegations per delegator + type MaxDelegationsPerDelegator = ConstU32<100>; + /// Minimum stake required to be reserved to be a candidate + type MinCandidateStk = ConstU128<{ 500 * currency::UNIT * currency::SUPPLY_FACTOR }>; + /// Minimum stake required to be reserved to be a delegator + type MinDelegation = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>; + type BlockAuthor = AuthorInherent; + type OnCollatorPayout = (); + type PayoutCollatorReward = PayoutCollatorOrOrbiterReward; + type OnInactiveCollator = OnInactiveCollator; + type OnNewRound = OnNewRound; + type SlotProvider = RelayChainSlotProvider; + type WeightInfo = moonbeam_weights::pallet_parachain_staking::WeightInfo; + type MaxCandidates = ConstU32<200>; + type SlotDuration = ConstU64<6_000>; + type BlockTime = ConstU64<6_000>; +} + +impl pallet_author_inherent::Config for Runtime { + type SlotBeacon = RelaychainDataProvider; + type AccountLookup = MoonbeamOrbiters; + type CanAuthor = AuthorFilter; + type AuthorId = AccountId; + type WeightInfo = moonbeam_weights::pallet_author_inherent::WeightInfo; +} + +#[cfg(test)] +mod mock { + use super::*; + pub struct MockRandomness; + impl frame_support::traits::Randomness for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumber) { + (H256(sp_io::hashing::blake2_256(subject)), 0) + } + } +} + +impl pallet_author_slot_filter::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(test))] + type RandomnessSource = Randomness; + #[cfg(test)] + type RandomnessSource = mock::MockRandomness; + type PotentialAuthors = ParachainStaking; + type WeightInfo = moonbeam_weights::pallet_author_slot_filter::WeightInfo; +} + +impl pallet_async_backing::Config for Runtime { + type AllowMultipleBlocksPerSlot = ConstBool; + type GetAndVerifySlot = pallet_async_backing::RelaySlot; + type ExpectedBlockTime = ConstU64<6000>; +} + +parameter_types! { + pub const InitializationPayment: Perbill = Perbill::from_percent(30); + pub const RelaySignaturesThreshold: Perbill = Perbill::from_percent(100); + pub const SignatureNetworkIdentifier: &'static [u8] = b"moonbase-"; + +} + +impl pallet_crowdloan_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Initialized = ConstBool; + type InitializationPayment = InitializationPayment; + type MaxInitContributors = ConstU32<500>; + // TODO to be revisited + type MinimumReward = ConstU128<0>; + type RewardCurrency = Balances; + type RelayChainAccountId = [u8; 32]; + type RewardAddressAssociateOrigin = EnsureSigned; + type RewardAddressChangeOrigin = EnsureSigned; + type RewardAddressRelayVoteThreshold = RelaySignaturesThreshold; + type SignatureNetworkIdentifier = SignatureNetworkIdentifier; + type VestingBlockNumber = relay_chain::BlockNumber; + type VestingBlockProvider = RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_crowdloan_rewards::WeightInfo; +} + +// This is a simple session key manager. It should probably either work with, or be replaced +// entirely by pallet sessions +impl pallet_author_mapping::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DepositCurrency = Balances; + type DepositAmount = ConstU128<{ 100 * currency::UNIT * currency::SUPPLY_FACTOR }>; + type Keys = session_keys_primitives::VrfId; + type WeightInfo = moonbeam_weights::pallet_author_mapping::WeightInfo; +} + +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, +)] +pub enum ProxyType { + /// All calls can be proxied. This is the trivial/most permissive filter. + Any = 0, + /// Only extrinsics that do not transfer funds. + NonTransfer = 1, + /// Only extrinsics related to governance (democracy and collectives). + Governance = 2, + /// Only extrinsics related to staking. + Staking = 3, + /// Allow to veto an announced proxy call. + CancelProxy = 4, + /// Allow extrinsic related to Balances. + Balances = 5, + /// Allow extrinsic related to AuthorMapping. + AuthorMapping = 6, + /// Allow extrinsic related to IdentityJudgement. + IdentityJudgement = 7, +} + +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +fn is_governance_precompile(precompile_name: &precompiles::PrecompileName) -> bool { + matches!( + precompile_name, + PrecompileName::TreasuryCouncilInstance + | PrecompileName::ReferendaPrecompile + | PrecompileName::ConvictionVotingPrecompile + | PrecompileName::PreimagePrecompile + | PrecompileName::OpenTechCommitteeInstance, + ) +} + +// Be careful: Each time this filter is modified, the substrate filter must also be modified +// consistently. +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType { + fn is_evm_proxy_call_allowed( + &self, + call: &pallet_evm_precompile_proxy::EvmSubCall, + recipient_has_code: bool, + gas: u64, + ) -> precompile_utils::EvmResult { + Ok(match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + call.value == U256::zero() + && match PrecompileName::from_address(call.to.0) { + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::IdentityPrecompile + | PrecompileName::ParachainStakingPrecompile, + ) => true, + Some(ref precompile) if is_governance_precompile(precompile) => true, + _ => false, + } + } + ProxyType::Governance => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(ref precompile) if is_governance_precompile(precompile) + ) + } + ProxyType::Staking => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile + ) + ) + } + // The proxy precompile does not contain method cancel_proxy + ProxyType::CancelProxy => false, + ProxyType::Balances => { + // Allow only "simple" accounts as recipient (no code nor precompile). + // Note: Checking the presence of the code is not enough because some precompiles + // have no code. + !recipient_has_code + && !precompile_utils::precompile_set::is_precompile_or_fail::( + call.to.0, gas, + )? + } + ProxyType::AuthorMapping => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(PrecompileName::AuthorMappingPrecompile) + ) + } + // There is no identity precompile + ProxyType::IdentityJudgement => false, + }) + } +} + +// Be careful: Each time this filter is modified, the EVM filter must also be modified consistently. +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + matches!( + c, + RuntimeCall::System(..) + | RuntimeCall::ParachainSystem(..) + | RuntimeCall::Timestamp(..) + | RuntimeCall::ParachainStaking(..) + | RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Identity(..) + | RuntimeCall::Utility(..) + | RuntimeCall::Proxy(..) | RuntimeCall::AuthorMapping(..) + | RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::claim { .. } + ) + ) + } + ProxyType::Governance => matches!( + c, + RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Utility(..) + ), + ProxyType::Staking => matches!( + c, + RuntimeCall::ParachainStaking(..) + | RuntimeCall::Utility(..) + | RuntimeCall::AuthorMapping(..) + | RuntimeCall::MoonbeamOrbiters(..) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) + ), + ProxyType::Balances => { + matches!(c, RuntimeCall::Balances(..) | RuntimeCall::Utility(..)) + } + ProxyType::AuthorMapping => matches!(c, RuntimeCall::AuthorMapping(..)), + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) + | RuntimeCall::Utility(..) + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + _ => false, + } + } +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + // One storage item; key size 32, value size 8 + type ProxyDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 21 bytes (20 bytes AccountId + 1 byte sizeof(ProxyType)). + type ProxyDepositFactor = ConstU128<{ currency::deposit(0, 21) }>; + type MaxProxies = ConstU32<32>; + type WeightInfo = moonbeam_weights::pallet_proxy::WeightInfo; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 56 bytes: + // - 20 bytes AccountId + // - 32 bytes Hasher (Blake2256) + // - 4 bytes BlockNumber (u32) + type AnnouncementDepositFactor = ConstU128<{ currency::deposit(0, 56) }>; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // TODO wire up our correct list of migrations here. Maybe this shouldn't be in + // `moonbeam_runtime_common`. + type MigrationsList = ( + moonbeam_runtime_common::migrations::CommonMigrations, + migrations::MoonbaseMigrations, + ); + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_moonbeam_lazy_migrations::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_moonbeam_lazy_migrations::WeightInfo; +} + +/// Maintenance mode Call filter +pub struct MaintenanceFilter; +impl Contains for MaintenanceFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(_) => false, + RuntimeCall::Balances(_) => false, + RuntimeCall::CrowdloanRewards(_) => false, + RuntimeCall::Ethereum(_) => false, + RuntimeCall::EVM(_) => false, + RuntimeCall::Identity(_) => false, + RuntimeCall::XTokens(_) => false, + RuntimeCall::ParachainStaking(_) => false, + RuntimeCall::MoonbeamOrbiters(_) => false, + RuntimeCall::PolkadotXcm(_) => false, + RuntimeCall::Treasury(_) => false, + RuntimeCall::XcmTransactor(_) => false, + RuntimeCall::EthereumXcm(_) => false, + _ => true, + } + } +} + +/// Normal Call Filter +/// We dont allow to create nor mint assets, this for now is disabled +/// We only allow transfers. For now creation of assets will go through +/// asset-manager, while minting/burning only happens through xcm messages +/// This can change in the future +pub struct NormalFilter; +impl Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(method) => match method { + pallet_assets::Call::transfer { .. } => true, + pallet_assets::Call::transfer_keep_alive { .. } => true, + pallet_assets::Call::approve_transfer { .. } => true, + pallet_assets::Call::transfer_approved { .. } => true, + pallet_assets::Call::cancel_approval { .. } => true, + pallet_assets::Call::destroy_accounts { .. } => true, + pallet_assets::Call::destroy_approvals { .. } => true, + pallet_assets::Call::finish_destroy { .. } => true, + _ => false, + }, + // We filter anonymous proxy as they make "reserve" inconsistent + // See: https://github.com/paritytech/substrate/blob/37cca710eed3dadd4ed5364c7686608f5175cce1/frame/proxy/src/lib.rs#L270 // editorconfig-checker-disable-line + RuntimeCall::Proxy(method) => match method { + pallet_proxy::Call::create_pure { .. } => false, + pallet_proxy::Call::kill_pure { .. } => false, + pallet_proxy::Call::proxy { real, .. } => { + !pallet_evm::AccountCodes::::contains_key(H160::from(*real)) + } + _ => true, + }, + // Filtering the EVM prevents possible re-entrancy from the precompiles which could + // lead to unexpected scenarios. + // See https://github.com/PureStake/sr-moonbeam/issues/30 + // Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so + // this can be seen as an additional security + RuntimeCall::EVM(_) => false, + RuntimeCall::Treasury( + pallet_treasury::Call::spend { .. } + | pallet_treasury::Call::payout { .. } + | pallet_treasury::Call::check_status { .. } + | pallet_treasury::Call::void_spend { .. }, + ) => false, + _ => true, + } + } +} + +pub struct XcmExecutionManager; +impl moonkit_xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} + +impl pallet_maintenance_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NormalCallFilter = NormalFilter; + type MaintenanceCallFilter = MaintenanceFilter; + type MaintenanceOrigin = + pallet_collective::EnsureProportionAtLeast; + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_proxy_genesis_companion::Config for Runtime { + type ProxyType = ProxyType; +} + +parameter_types! { + pub OrbiterReserveIdentifier: [u8; 4] = [b'o', b'r', b'b', b'i']; +} + +type AddCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type DelCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_moonbeam_orbiters::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AccountLookup = AuthorMapping; + type AddCollatorOrigin = AddCollatorOrigin; + type Currency = Balances; + type DelCollatorOrigin = DelCollatorOrigin; + /// Maximum number of orbiters per collator + type MaxPoolSize = ConstU32<8>; + /// Maximum number of round to keep on storage + type MaxRoundArchive = ConstU32<4>; + type OrbiterReserveIdentifier = OrbiterReserveIdentifier; + type RotatePeriod = ConstU32<3>; + /// Round index type. + type RoundIndex = pallet_parachain_staking::RoundIndex; + type WeightInfo = moonbeam_weights::pallet_moonbeam_orbiters::WeightInfo; +} + +/// Only callable after `set_validation_data` is called which forms this proof the same way +fn relay_chain_state_proof() -> RelayChainStateProof { + let relay_storage_root = ParachainSystem::validation_data() + .expect("set in `set_validation_data`") + .relay_parent_storage_root; + let relay_chain_state = + ParachainSystem::relay_state_proof().expect("set in `set_validation_data`"); + RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) + .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") +} + +pub struct BabeDataGetter; +impl pallet_randomness::GetBabeData> for BabeDataGetter { + // Tolerate panic here because only ever called in inherent (so can be omitted) + fn get_epoch_index() -> u64 { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + const BENCHMARKING_NEW_EPOCH: u64 = 10u64; + return BENCHMARKING_NEW_EPOCH; + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::EPOCH_INDEX) + .ok() + .flatten() + .expect("expected to be able to read epoch index from relay chain state proof") + } + fn get_epoch_randomness() -> Option { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + let benchmarking_babe_output = Hash::default(); + return Some(benchmarking_babe_output); + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) + .ok() + .flatten() + } +} + +impl pallet_randomness::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddressMapping = sp_runtime::traits::ConvertInto; + type Currency = Balances; + type BabeDataGetter = BabeDataGetter; + type VrfKeyLookup = AuthorMapping; + type Deposit = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>; + type MaxRandomWords = ConstU8<100>; + type MinBlockDelay = ConstU32<2>; + type MaxBlockDelay = ConstU32<2_000>; + type BlockExpirationDelay = ConstU32<10_000>; + type EpochExpirationDelay = ConstU64<10_000>; + type WeightInfo = moonbeam_weights::pallet_randomness::WeightInfo; +} + +impl pallet_root_testing::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +parameter_types! { + // One storage item; key size is 32 + 20; value is size 4+4+16+20 bytes = 44 bytes. + pub const DepositBase: Balance = currency::deposit(1, 96); + // Additional storage item size of 20 bytes. + pub const DepositFactor: Balance = currency::deposit(0, 20); + pub const MaxSignatories: u32 = 100; +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = MaxSignatories; + type WeightInfo = moonbeam_weights::pallet_multisig::WeightInfo; +} + +impl pallet_relay_storage_roots::Config for Runtime { + type MaxStorageRoots = ConstU32<30>; + type RelaychainStateProvider = cumulus_pallet_parachain_system::RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_relay_storage_roots::WeightInfo; +} + +impl pallet_precompile_benchmarks::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_precompile_benchmarks::WeightInfo; +} + +construct_runtime! { + pub enum Runtime + { + System: frame_system::{Pallet, Call, Storage, Config, Event} = 0, + Utility: pallet_utility::{Pallet, Call, Event} = 1, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 2, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 3, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event} = 4, + // Previously 5: pallet_randomness_collective_flip + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 6, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Config, Event} = 7, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 8, + EthereumChainId: pallet_evm_chain_id::{Pallet, Storage, Config} = 9, + EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 10, + Ethereum: pallet_ethereum::{Pallet, Call, Storage, Event, Origin, Config} = 11, + ParachainStaking: pallet_parachain_staking::{Pallet, Call, Storage, Event, Config} = 12, + Scheduler: pallet_scheduler::{Pallet, Storage, Event, Call} = 13, + // Previously 14: pallet_democracy::{Pallet, Storage, Config, Event, Call} = 14, + // Previously 15: CouncilCollective: pallet_collective:: + // Previously 16: TechCommitteeCollective: pallet_collective:: + Treasury: pallet_treasury::{Pallet, Storage, Config, Event, Call} = 17, + AuthorInherent: pallet_author_inherent::{Pallet, Call, Storage, Inherent} = 18, + AuthorFilter: pallet_author_slot_filter::{Pallet, Call, Storage, Event, Config} = 19, + CrowdloanRewards: pallet_crowdloan_rewards::{Pallet, Call, Config, Storage, Event} = 20, + AuthorMapping: pallet_author_mapping::{Pallet, Call, Config, Storage, Event} = 21, + Proxy: pallet_proxy::{Pallet, Call, Storage, Event} = 22, + MaintenanceMode: pallet_maintenance_mode::{Pallet, Call, Config, Storage, Event} = 23, + Identity: pallet_identity::{Pallet, Call, Storage, Event} = 24, + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 25, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 26, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 27, + PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 28, + Assets: pallet_assets::{Pallet, Call, Storage, Event} = 29, + XTokens: orml_xtokens::{Pallet, Call, Storage, Event} = 30, + AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event} = 31, + Migrations: pallet_migrations::{Pallet, Storage, Config, Event} = 32, + XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Config, Storage, Event} = 33, + ProxyGenesisCompanion: pallet_proxy_genesis_companion::{Pallet, Config} = 34, + // Previously 35: BaseFee + // Previously 36: pallet_assets:: + MoonbeamOrbiters: pallet_moonbeam_orbiters::{Pallet, Call, Storage, Event, Config} = 37, + EthereumXcm: pallet_ethereum_xcm::{Pallet, Call, Storage, Origin} = 38, + Randomness: pallet_randomness::{Pallet, Call, Storage, Event, Inherent} = 39, + TreasuryCouncilCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 40, + ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event} = 41, + Referenda: pallet_referenda::{Pallet, Call, Storage, Event} = 42, + Origins: governance::custom_origins::{Origin} = 43, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event, HoldReason} = 44, + Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event} = 45, + OpenTechCommitteeCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 46, + RootTesting: pallet_root_testing::{Pallet, Call, Storage, Event} = 47, + Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 48, + Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 49, + AsyncBacking: pallet_async_backing::{Pallet, Storage} = 50, + MoonbeamLazyMigrations: pallet_moonbeam_lazy_migrations::{Pallet, Call, Storage} = 51, + RelayStorageRoots: pallet_relay_storage_roots::{Pallet, Storage} = 52, + PrecompileBenchmarks: pallet_precompile_benchmarks::{Pallet} = 53, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 54, + EmergencyParaXcm: pallet_emergency_para_xcm::{Pallet, Call, Storage, Event} = 55, + } +} + +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + fp_self_contained::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = + fp_self_contained::CheckedExtrinsic; +/// Executive: handles dispatch to the various pallets. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_support::parameter_types! { + pub const MaxBalance: crate::Balance = crate::Balance::max_value(); + } + + frame_benchmarking::define_benchmarks!( + [pallet_utility, Utility] + [pallet_timestamp, Timestamp] + [pallet_balances, Balances] + [pallet_sudo, Sudo] + [pallet_evm, EVM] + [pallet_assets, Assets] + [pallet_parachain_staking, ParachainStaking] + [pallet_scheduler, Scheduler] + [pallet_treasury, Treasury] + [pallet_author_inherent, AuthorInherent] + [pallet_author_slot_filter, AuthorFilter] + [pallet_crowdloan_rewards, CrowdloanRewards] + [pallet_author_mapping, AuthorMapping] + [pallet_proxy, Proxy] + [pallet_identity, Identity] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_asset_manager, AssetManager] + [pallet_xcm_transactor, XcmTransactor] + [pallet_moonbeam_orbiters, MoonbeamOrbiters] + [pallet_randomness, Randomness] + [pallet_conviction_voting, ConvictionVoting] + [pallet_referenda, Referenda] + [pallet_preimage, Preimage] + [pallet_whitelist, Whitelist] + [pallet_multisig, Multisig] + [pallet_relay_storage_roots, RelayStorageRoots] + [pallet_precompile_benchmarks, PrecompileBenchmarks] + [pallet_moonbeam_lazy_migrations, MoonbeamLazyMigrations] + ); +} + +// All of our runtimes share most of their Runtime API implementations. +// We use a macro to implement this common part and add runtime-specific additional implementations. +// This macro expands to : +// ``` +// impl_runtime_apis! { +// // All impl blocks shared between all runtimes. +// +// // Specific impls provided to the `impl_runtime_apis_plus_common!` macro. +// } +// ``` +moonbeam_runtime_common::impl_runtime_apis_plus_common! { + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + xt: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + // Filtered calls should not enter the tx pool as they'll fail if inserted. + // If this call is not allowed, we return early. + if !::BaseCallFilter::contains(&xt.0.function) { + return InvalidTransaction::Call.into(); + } + + // This runtime uses Substrate's pallet transaction payment. This + // makes the chain feel like a standard Substrate chain when submitting + // frame transactions and using Substrate ecosystem tools. It has the downside that + // transaction are not prioritized by gas_price. The following code reprioritizes + // transactions to overcome this. + // + // A more elegant, ethereum-first solution is + // a pallet that replaces pallet transaction payment, and allows users + // to directly specify a gas price rather than computing an effective one. + // #HopefullySomeday + + // First we pass the transactions to the standard FRAME executive. This calculates all the + // necessary tags, longevity and other properties that we will leave unchanged. + // This also assigns some priority that we don't care about and will overwrite next. + let mut intermediate_valid = Executive::validate_transaction(source, xt.clone(), block_hash)?; + + let dispatch_info = xt.get_dispatch_info(); + + // If this is a pallet ethereum transaction, then its priority is already set + // according to effective priority fee from pallet ethereum. If it is any other kind of + // transaction, we modify its priority. The goal is to arrive at a similar metric used + // by pallet ethereum, which means we derive a fee-per-gas from the txn's tip and + // weight. + Ok(match &xt.0.function { + RuntimeCall::Ethereum(transact { .. }) => intermediate_valid, + _ if dispatch_info.class != DispatchClass::Normal => intermediate_valid, + _ => { + let tip = match xt.0.signature { + None => 0, + Some((_, _, ref signed_extra)) => { + // Yuck, this depends on the index of charge transaction in Signed Extra + let charge_transaction = &signed_extra.7; + charge_transaction.tip() + } + }; + + let effective_gas = + ::GasWeightMapping::weight_to_gas( + dispatch_info.weight + ); + let tip_per_gas = if effective_gas > 0 { + tip.saturating_div(effective_gas as u128) + } else { + 0 + }; + + // Overwrite the original prioritization with this ethereum one + intermediate_valid.priority = tip_per_gas as u64; + intermediate_valid + } + }) + } + } + + impl async_backing_primitives::UnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: async_backing_primitives::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } +} + +struct CheckInherents; + +// Parity has decided to depreciate this trait, but does not offer a satisfactory replacement, +// see issue: https://github.com/paritytech/polkadot-sdk/issues/2841 +#[allow(deprecated)] +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + inherent_data.check_extrinsics(block) + } +} + +// Nimbus's Executive wrapper allows relay validators to verify the seal digest +cumulus_pallet_parachain_system::register_validate_block!( + Runtime = Runtime, + BlockExecutor = pallet_author_inherent::BlockExecutor::, + CheckInherents = CheckInherents, +); + +moonbeam_runtime_common::impl_self_contained_call!(); + +// Shorthand for a Get field of a pallet Config. +#[macro_export] +macro_rules! get { + ($pallet:ident, $name:ident, $type:ty) => { + <<$crate::Runtime as $pallet::Config>::$name as $crate::Get<$type>>::get() + }; +} + +#[cfg(test)] +mod tests { + use super::{currency::*, *}; + + #[test] + // Helps us to identify a Pallet Call in case it exceeds the 1kb limit. + // Hint: this should be a rare case. If that happens, one or more of the dispatchable arguments + // need to be Boxed. + fn call_max_size() { + const CALL_ALIGN: u32 = 1024; + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + } + + #[test] + fn currency_constants_are_correct() { + assert_eq!(SUPPLY_FACTOR, 1); + + // txn fees + assert_eq!(TRANSACTION_BYTE_FEE, Balance::from(1 * GIGAWEI)); + assert_eq!( + get!(pallet_transaction_payment, OperationalFeeMultiplier, u8), + 5_u8 + ); + assert_eq!(STORAGE_BYTE_FEE, Balance::from(100 * MICROUNIT)); + + // treasury minimums + assert_eq!( + get!(pallet_treasury, ProposalBondMinimum, u128), + Balance::from(1 * UNIT) + ); + + // pallet_identity deposits + assert_eq!( + get!(pallet_identity, BasicDeposit, u128), + Balance::from(1 * UNIT + 25800 * MICROUNIT) + ); + assert_eq!( + get!(pallet_identity, ByteDeposit, u128), + Balance::from(100 * MICROUNIT) + ); + assert_eq!( + get!(pallet_identity, SubAccountDeposit, u128), + Balance::from(1 * UNIT + 5300 * MICROUNIT) + ); + + // staking minimums + assert_eq!( + get!(pallet_parachain_staking, MinCandidateStk, u128), + Balance::from(500 * UNIT) + ); + assert_eq!( + get!(pallet_parachain_staking, MinDelegation, u128), + Balance::from(1 * UNIT) + ); + + // crowdloan min reward + assert_eq!( + get!(pallet_crowdloan_rewards, MinimumReward, u128), + Balance::from(0u128) + ); + + // deposit for AuthorMapping + assert_eq!( + get!(pallet_author_mapping, DepositAmount, u128), + Balance::from(100 * UNIT) + ); + + // proxy deposits + assert_eq!( + get!(pallet_proxy, ProxyDepositBase, u128), + Balance::from(1 * UNIT + 800 * MICROUNIT) + ); + assert_eq!( + get!(pallet_proxy, ProxyDepositFactor, u128), + Balance::from(2100 * MICROUNIT) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositBase, u128), + Balance::from(1 * UNIT + 800 * MICROUNIT) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositFactor, u128), + Balance::from(5600 * MICROUNIT) + ); + } + + #[test] + fn max_offline_rounds_lower_or_eq_than_reward_payment_delay() { + assert!( + get!(pallet_parachain_staking, MaxOfflineRounds, u32) + <= get!(pallet_parachain_staking, RewardPaymentDelay, u32) + ); + } + + #[test] + // Required migration is + // pallet_parachain_staking::migrations::IncreaseMaxTopDelegationsPerCandidate + // Purpose of this test is to remind of required migration if constant is ever changed + fn updating_maximum_delegators_per_candidate_requires_configuring_required_migration() { + assert_eq!( + get!(pallet_parachain_staking, MaxTopDelegationsPerCandidate, u32), + 300 + ); + assert_eq!( + get!( + pallet_parachain_staking, + MaxBottomDelegationsPerCandidate, + u32 + ), + 50 + ); + } + + #[test] + fn test_proxy_type_can_be_decoded_from_valid_values() { + let test_cases = vec![ + // (input, expected) + (0u8, ProxyType::Any), + (1, ProxyType::NonTransfer), + (2, ProxyType::Governance), + (3, ProxyType::Staking), + (4, ProxyType::CancelProxy), + (5, ProxyType::Balances), + (6, ProxyType::AuthorMapping), + (7, ProxyType::IdentityJudgement), + ]; + + for (input, expected) in test_cases { + let actual = ProxyType::decode(&mut input.to_le_bytes().as_slice()); + assert_eq!( + Ok(expected), + actual, + "failed decoding ProxyType for value '{}'", + input + ); + } + } + + #[test] + fn configured_base_extrinsic_weight_is_evm_compatible() { + let min_ethereum_transaction_weight = WeightPerGas::get() * 21_000; + let base_extrinsic = ::BlockWeights::get() + .get(frame_support::dispatch::DispatchClass::Normal) + .base_extrinsic; + assert!(base_extrinsic.ref_time() <= min_ethereum_transaction_weight.ref_time()); + } + + #[test] + fn test_storage_growth_ratio_is_correct() { + let expected_storage_growth_ratio = BlockGasLimit::get() + .low_u64() + .saturating_div(BLOCK_STORAGE_LIMIT); + let actual_storage_growth_ratio = + ::GasLimitStorageGrowthRatio::get(); + assert_eq!( + expected_storage_growth_ratio, actual_storage_growth_ratio, + "Storage growth ratio is not correct" + ); + } +} diff --git a/tracing/2900/runtime/moonbase/src/migrations.rs b/tracing/2900/runtime/moonbase/src/migrations.rs new file mode 100644 index 00000000..693f813e --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/migrations.rs @@ -0,0 +1,43 @@ +// Copyright 2024 Moonbeam Foundation Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! # Moonbase specific Migrations + +use crate::Runtime; +use frame_support::{traits::OnRuntimeUpgrade, weights::Weight}; +use pallet_migrations::{GetMigrations, Migration}; +use pallet_parachain_staking::migrations::MultiplyRoundLenBy2; +use sp_std::{prelude::*, vec}; + +pub struct MoonbaseMigrations; + +impl GetMigrations for MoonbaseMigrations { + fn get_migrations() -> Vec> { + vec![Box::new(PalletStakingMultiplyRoundLenBy2)] + } +} + +// This migration should only be applied to runtimes with async backing enabled +pub struct PalletStakingMultiplyRoundLenBy2; +impl Migration for PalletStakingMultiplyRoundLenBy2 { + fn friendly_name(&self) -> &str { + "MM_MultiplyRoundLenBy2" + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + MultiplyRoundLenBy2::::on_runtime_upgrade() + } +} diff --git a/tracing/2900/runtime/moonbase/src/precompiles.rs b/tracing/2900/runtime/moonbase/src/precompiles.rs new file mode 100644 index 00000000..6ca3a0ae --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/precompiles.rs @@ -0,0 +1,312 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use crate::{ + asset_config::ForeignAssetInstance, xcm_config::XcmExecutorConfig, OpenTechCommitteeInstance, + TreasuryCouncilInstance, +}; +use crate::{AssetId, H160}; +use frame_support::parameter_types; +use pallet_evm_precompile_author_mapping::AuthorMappingPrecompile; +use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata}; +use pallet_evm_precompile_batch::BatchPrecompile; +use pallet_evm_precompile_blake2::Blake2F; +use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; +use pallet_evm_precompile_call_permit::CallPermitPrecompile; +use pallet_evm_precompile_collective::CollectivePrecompile; +use pallet_evm_precompile_conviction_voting::ConvictionVotingPrecompile; +use pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompile; +use pallet_evm_precompile_gmp::GmpPrecompile; +use pallet_evm_precompile_identity::IdentityPrecompile; +use pallet_evm_precompile_modexp::Modexp; +use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile; +use pallet_evm_precompile_preimage::PreimagePrecompile; +use pallet_evm_precompile_proxy::{OnlyIsProxyAndProxy, ProxyPrecompile}; +use pallet_evm_precompile_randomness::RandomnessPrecompile; +use pallet_evm_precompile_referenda::ReferendaPrecompile; +use pallet_evm_precompile_registry::PrecompileRegistry; +use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile; +use pallet_evm_precompile_relay_verifier::RelayDataVerifierPrecompile; +use pallet_evm_precompile_sha3fips::Sha3FIPS256; +use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use pallet_evm_precompile_storage_cleaner::StorageCleanerPrecompile; +use pallet_evm_precompile_xcm_transactor::{ + v1::XcmTransactorPrecompileV1, v2::XcmTransactorPrecompileV2, v3::XcmTransactorPrecompileV3, +}; +use pallet_evm_precompile_xcm_utils::{AllExceptXcmExecute, XcmUtilsPrecompile}; +use pallet_evm_precompile_xtokens::XtokensPrecompile; +use pallet_evm_precompileset_assets_erc20::{AccountIdAssetIdConversion, Erc20AssetsPrecompileSet}; +use precompile_utils::precompile_set::*; +use sp_std::prelude::*; + +/// ERC20 metadata for the native token. +pub struct NativeErc20Metadata; + +impl Erc20Metadata for NativeErc20Metadata { + /// Returns the name of the token. + fn name() -> &'static str { + "DEV token" + } + + /// Returns the symbol of the token. + fn symbol() -> &'static str { + "DEV" + } + + /// Returns the decimals places of the token. + fn decimals() -> u8 { + 18 + } + + /// Must return `true` only if it represents the main native currency of + /// the network. It must be the currency used in `pallet_evm`. + fn is_native_currency() -> bool { + true + } +} + +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as foreign +pub const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8; 4]; +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as local +pub const LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8, 255u8, 255u8, 254u8]; + +parameter_types! { + pub ForeignAssetPrefix: &'static [u8] = FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX; + pub LocalAssetPrefix: &'static [u8] = LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX; +} + +type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile); + +#[precompile_utils::precompile_name_from_address] +type MoonbasePrecompilesAt = ( + // Ethereum precompiles: + // We allow DELEGATECALL to stay compliant with Ethereum behavior. + PrecompileAt, ECRecover, EthereumPrecompilesChecks>, + PrecompileAt, Sha256, EthereumPrecompilesChecks>, + PrecompileAt, Ripemd160, EthereumPrecompilesChecks>, + PrecompileAt, Identity, EthereumPrecompilesChecks>, + PrecompileAt, Modexp, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Add, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>, + PrecompileAt, Blake2F, EthereumPrecompilesChecks>, + // Non-Moonbeam specific nor Ethereum precompiles : + PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>, + RemovedPrecompileAt>, // Dispatch + PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>, + PrecompileAt, StorageCleanerPrecompile, CallableByPrecompile>, + // Moonbeam specific precompiles: + PrecompileAt< + AddressU64<2048>, + ParachainStakingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2049>, + CrowdloanRewardsPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2050>, + Erc20BalancesPrecompile, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompileAt>, // DemocracyPrecompile + PrecompileAt< + AddressU64<2052>, + XtokensPrecompile, + ( + SubcallWithMaxNesting<1>, + CallableByContract, + CallableByPrecompile, + ), + >, + PrecompileAt< + AddressU64<2053>, + RelayEncoderPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2054>, + XcmTransactorPrecompileV1, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2055>, + AuthorMappingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2056>, + BatchPrecompile, + ( + SubcallWithMaxNesting<2>, + // Batch is the only precompile allowed to call Batch. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2057>, + RandomnessPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2058>, + CallPermitPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2059>, + ProxyPrecompile, + ( + CallableByContract>, + SubcallWithMaxNesting<0>, + // Batch is the only precompile allowed to call Proxy. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2060>, + XcmUtilsPrecompile, + CallableByContract>, + >, + PrecompileAt< + AddressU64<2061>, + XcmTransactorPrecompileV2, + (CallableByContract, CallableByPrecompile), + >, + // CouncilCollective precompile + RemovedPrecompileAt>, + // TechCommitteeCollective precompile + RemovedPrecompileAt>, + PrecompileAt< + AddressU64<2064>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2065>, + ReferendaPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2066>, + ConvictionVotingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2067>, + PreimagePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2068>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2069>, + PrecompileRegistry, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt, GmpPrecompile, SubcallWithMaxNesting<0>>, + PrecompileAt< + AddressU64<2071>, + XcmTransactorPrecompileV3, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2072>, + IdentityPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2073>, + RelayDataVerifierPrecompile, + (CallableByContract, CallableByPrecompile), + >, +); + +pub struct DisabledLocalAssets(sp_std::marker::PhantomData); + +impl sp_core::Get> for DisabledLocalAssets +where + Runtime: frame_system::Config, + Runtime::AccountId: Into, + Runtime: AccountIdAssetIdConversion, +{ + fn get() -> Vec { + vec![ + // https://moonbase.subscan.io/extrinsic/5245322-6?event=5245322-22 + 182085191673801920759598290391359780050u128, + // https://moonbase.subscan.io/extrinsic/3244752-4?event=3244752-9 + 282223684955665977914983262584256755878u128, + // https://moonbase.subscan.io/extrinsic/3158280-4?event=3158280-9 + 235962050501460763853961856666389569138u128, + // https://moonbase.subscan.io/block/3045900?tab=event&&event=3045900-4 + 45350527686064227409532032051821627910u128, + // https://moonbase.subscan.io/extrinsic/3024306-4?event=3024306-9 + 199439015574556113723291251263369885338u128, + // https://moonbase.subscan.io/extrinsic/2921640-4?event=2921640-9 + 236426850287284823323011839750645103615u128, + // https://moonbase.subscan.io/extrinsic/2748867-4?event=2748867-9 + 14626673838203901761839010613793775004u128, + // https://moonbase.subscan.io/extrinsic/2709788-4?event=2709788-9 + 95328064580428769161981851380106820590u128, + // https://moonbase.subscan.io/extrinsic/2670844-4?event=2670844-9 + 339028723712074529056817184013808486301u128, + // https://moonbase.subscan.io/extrinsic/2555083-4?event=2555083-9 + 100481493116602214283160747599845770751u128, + // https://moonbase.subscan.io/extrinsic/2473880-3?event=2473880-8 + 319515966007349957795820176952936446433u128, + // https://moonbase.subscan.io/extrinsic/2346438-3?event=2346438-6 + 337110116006454532607322340792629567158u128, + // https://moonbase.subscan.io/extrinsic/2239102-3?event=2239102-6 + 255225902946708983196362678630947296516u128, + // https://moonbase.subscan.io/extrinsic/2142964-4?event=2142964-12 + 3356866138193769031598374869367363824u128, + // https://moonbase.subscan.io/extrinsic/1967538-6?event=1967538-28 + 144992676743556815849525085098140609495u128, + ] + .iter() + .map(|id| Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, *id).into()) + .collect() + } +} + +/// The PrecompileSet installed in the Moonbase runtime. +/// We include the nine Istanbul precompiles +/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69) +/// The following distribution has been decided for the precompiles +/// 0-1023: Ethereum Mainnet Precompiles +/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither Moonbeam specific +/// 2048-4095 Moonbeam specific precompiles +pub type MoonbasePrecompiles = PrecompileSetBuilder< + R, + ( + // Skip precompiles if out of range. + PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), MoonbasePrecompilesAt>, + // Prefixed precompile sets (XC20) + PrecompileSetStartingWith< + ForeignAssetPrefix, + Erc20AssetsPrecompileSet, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompilesAt>, + ), +>; diff --git a/tracing/2900/runtime/moonbase/src/timestamp.rs b/tracing/2900/runtime/moonbase/src/timestamp.rs new file mode 100644 index 00000000..4df4eca6 --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/timestamp.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! A way to get a relyable timestamp + +use crate::Runtime; +use cumulus_pallet_parachain_system::{ + consensus_hook::UnincludedSegmentCapacity, + relay_state_snapshot::{self, ReadEntryErr}, + ConsensusHook, RelayChainStateProof, +}; +use frame_support::pallet_prelude::*; +use frame_support::storage::types::{StorageValue, ValueQuery}; +use frame_support::traits::{StorageInstance, Time}; +pub use moonbeam_core_primitives::well_known_relay_keys; + +/// Get the relay timestamp. +/// Noe that the relay timestamp is populated at the parachain system inherent. +/// If you fetch the timestamp before, you will get the timestamp of the parent block. +pub struct RelayTimestamp; +impl Time for RelayTimestamp { + type Moment = u64; + + fn now() -> Self::Moment { + RelayTimestampNow::get() + } +} + +/// A wrapper around the consensus hook to get the relay timlestmap from the relay storage proof +pub struct ConsensusHookWrapperForRelayTimestamp(core::marker::PhantomData); +impl ConsensusHook for ConsensusHookWrapperForRelayTimestamp { + fn on_state_proof(state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { + let relay_timestamp: u64 = + match state_proof.read_entry(well_known_relay_keys::TIMESTAMP_NOW, None) { + Ok(relay_timestamp) => relay_timestamp, + // Log the read entry error + Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Proof)) => { + log::error!("Invalid relay storage proof: fail to read key TIMESTAMP_NOW"); + panic!("Invalid realy storage proof: fail to read key TIMESTAMP_NOW"); + } + Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Decode)) => { + log::error!("Corrupted relay storage: fail to decode value TIMESTAMP_NOW"); + panic!("Corrupted relay storage: fail to decode value TIMESTAMP_NOW"); + } + Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Absent)) => { + log::error!("Corrupted relay storage: value TIMESTAMP_NOW is absent!"); + panic!("Corrupted relay storage: value TIMESTAMP_NOW is absent!"); + } + // Can't return another kind of error, the blokc is invalid anyway, so we should panic + _ => unreachable!(), + }; + + let wrapper_weight = ::DbWeight::get().writes(1); + + RelayTimestampNow::put(relay_timestamp); + + let (weight, capacity) = Inner::on_state_proof(state_proof); + + (weight.saturating_add(wrapper_weight), capacity) + } +} + +// Prefix for storage value RelayTimestampNow +struct RelayTimestampNowPrefix; +impl StorageInstance for RelayTimestampNowPrefix { + const STORAGE_PREFIX: &'static str = "RelayTimestampNow"; + + fn pallet_prefix() -> &'static str { + "runtime" + } +} + +// Storage type used to store the last current relay timestamp +type RelayTimestampNow = StorageValue; diff --git a/tracing/2900/runtime/moonbase/src/xcm_config.rs b/tracing/2900/runtime/moonbase/src/xcm_config.rs new file mode 100644 index 00000000..bd05759a --- /dev/null +++ b/tracing/2900/runtime/moonbase/src/xcm_config.rs @@ -0,0 +1,732 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM configuration for Moonbase. +//! + +use super::{ + governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, + EmergencyParaXcm, Erc20XcmBridge, MaintenanceMode, MessageQueue, ParachainInfo, + ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, + RuntimeOrigin, Treasury, XcmpQueue, +}; +use crate::OpenTechCommitteeInstance; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; +use sp_runtime::{ + traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf}, + DispatchErrorWithPostInfo, +}; + +use frame_support::{ + parameter_types, + traits::{EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin}, +}; + +use frame_system::{EnsureRoot, RawOrigin}; +use sp_core::{ConstU32, H160, H256}; +use sp_weights::Weight; +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, HashedDescription, + NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, +}; + +use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; + +use xcm::latest::prelude::{ + Asset, GlobalConsensus, InteriorLocation, Junction, Location, NetworkId, PalletInstance, + Parachain, +}; +use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry}; + +use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use orml_xcm_support::MultiNativeAsset; +use xcm_primitives::{ + AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType, + FirstAssetTrader, SignedToAccountId20, UtilityAvailableCalls, UtilityEncodeCall, XcmTransact, +}; + +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use sp_core::Get; +use sp_std::{ + convert::{From, Into, TryFrom}, + prelude::*, +}; + +use orml_traits::parameter_type_with_key; + +use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot}; + +parameter_types! { + // The network Id of the relay + pub const RelayNetwork: NetworkId = NetworkId::Westend; + // The relay chain Origin type + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + // The universal location within the global consensus system + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); + + + // Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a Location: (Self Balances pallet index) + // We use the RELATIVE multilocation + pub SelfReserve: Location = Location { + parents: 0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a Location of type AccountKey20, just generate a native account + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + HashedDescription>, +); + +/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`. +pub struct LocationToH160; +impl ConvertLocation for LocationToH160 { + fn convert_location(location: &Location) -> Option { + >::convert_location(location) + .map(Into::into) + } +} + +// The non-reserve fungible transactor type +// It will use pallet-assets, and the Id will be matched against AsAssetType +// This is intended to match FOREIGN ASSETS +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + super::Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId20 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +/// The transactor for our own chain currency. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + xcm_builder::IsConcrete, + // We can convert the MultiLocations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// We use all transactors +// These correspond to +// SelfReserve asset, both pre and post 0.9.16 +// Foreign assets +// We can remove the Old reanchor once +// we import https://github.com/open-web3-stack/open-runtime-module-library/pull/708 +pub type AssetTransactors = ( + LocalAssetTransactor, + ForeignFungiblesTransactor, + Erc20XcmBridge, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + // Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte- + // account local origin + SignedAccountKey20AsNative, +); + +parameter_types! { + /// Maximum number of instructions in a single XCM fragment. A sanity check against + /// weight caculations getting too crazy. + pub MaxInstructions: u32 = 100; +} + +/// Xcm Weigher shared between multiple Xcm-related configs. +pub type XcmWeigher = WeightInfoBounds< + moonbeam_xcm_benchmarks::weights::XcmWeight, + RuntimeCall, + MaxInstructions, +>; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +/// We do not burn anything because we want to mimic exactly what +/// the sovereign account has +pub type XcmFeesToAccount = xcm_primitives::XcmFeesToAccount< + super::Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +// Our implementation of the Moonbeam Call +// Attachs the right origin in case the call is made to pallet-ethereum-xcm +#[cfg(not(feature = "evm-tracing"))] +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +#[cfg(feature = "evm-tracing")] +moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!(); + +moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); + +pub struct SafeCallFilter; +impl frame_support::traits::Contains for SafeCallFilter { + fn contains(_call: &RuntimeCall) -> bool { + // TODO review + // This needs to be addressed at EVM level + true + } +} + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS; +} + +pub struct XcmExecutorConfig; +impl xcm_executor::Config for XcmExecutorConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + // Filter to the reserve withdraw operations + // Whenever the reserve matches the relative or absolute value + // of our chain, we always return the relative reserve + type IsReserve = MultiNativeAsset>; + type IsTeleporter = (); // No teleport + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = XcmWeigher; + // We use two traders + // When we receive the relative representation of the self-reserve asset, + // we use UsingComponents and the local way of handling fees + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + UsingComponents< + ::WeightToFee, + SelfReserve, + AccountId, + Balances, + DealWithFees, + >, + FirstAssetTrader, + ); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type PalletInstancesInfo = crate::AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = SafeCallFilter; + type Aliasers = Nothing; + type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor; +} + +// Converts a Signed Local Origin into a Location +pub type LocalOriginToLocation = SignedToAccountId20; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper< + XcmExecutorConfig, + xcm_executor::XcmExecutor, +>; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // TODO pallet-xcm weights + type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo; + type AdminOrigin = EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = sp_core::ConstU32<1_000>; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery< + cumulus_primitives_core::ParaId, + >; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +// TODO: This pallet can be removed after the lazy migration is done and +// event `Completed` is emitted. +// https://github.com/paritytech/polkadot-sdk/pull/1246 +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DmpSink = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_dmp_queue::weights::SubstrateWeight; +} + +parameter_types! { + /// The amount of weight (if any) which should be provided to the message queue for + /// servicing enqueued items. + /// + /// This may be legitimately `None` in the case that you will call + /// `ServiceQueues::service_queues` manually. + pub MessageQueueServiceWeight: Weight = + Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block; + /// The maximum number of stale pages (i.e. of overweight messages) allowed before culling + /// can happen. Once there are more stale pages than this, then historical pages may be + /// dropped, even if they contain unprocessed overweight messages. + pub const MessageQueueMaxStale: u32 = 8; + /// The size of the page; this implies the maximum message size which can be sent. + /// + /// A good value depends on the expected message sizes, their weights, the weight that is + /// available for processing them and the maximal needed message size. The maximal message + /// size is slightly lower than this as defined by [`MaxMessageLenOf`]. + pub const MessageQueueHeapSize: u32 = 128 * 1048; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = + xcm_builder::ProcessXcmMessage; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + // NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins + type QueuePausedQuery = EmergencyParaXcm; + type WeightInfo = pallet_message_queue::weights::SubstrateWeight; +} + +impl pallet_emergency_para_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CheckAssociatedRelayNumber = + cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type XcmpMessageHandler = XcmpQueue; + type PausedThreshold = ConstU32<300>; + type FastAuthorizeUpgradeOrigin = + pallet_collective::EnsureProportionAtLeast; + type PausedToNormalOrigin = + pallet_collective::EnsureProportionAtLeast; +} + +// Our AssetType. For now we only handle Xcm Assets +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum AssetType { + Xcm(xcm::v3::Location), +} +impl Default for AssetType { + fn default() -> Self { + Self::Xcm(xcm::v3::Location::here()) + } +} + +impl From for AssetType { + fn from(location: xcm::v3::Location) -> Self { + Self::Xcm(location) + } +} + +// This can be removed once we fully adopt xcm::v4 everywhere +impl TryFrom for AssetType { + type Error = (); + fn try_from(location: Location) -> Result { + Ok(Self::Xcm(location.try_into()?)) + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => Some(location), + } + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => xcm_builder::V4V3LocationConverter::convert_back(&location), + } + } +} + +// Implementation on how to retrieve the AssetId from an AssetType +// We take it +impl From for AssetId { + fn from(asset: AssetType) -> AssetId { + match asset { + AssetType::Xcm(id) => { + let mut result: [u8; 16] = [0u8; 16]; + let hash: H256 = id.using_encoded(::Hashing::hash); + result.copy_from_slice(&hash.as_fixed_bytes()[0..16]); + u128::from_le_bytes(result) + } + } + } +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + // Our native token + SelfReserve, + // Assets representing other chains native tokens + ForeignAsset(AssetId), + // Erc20 token + Erc20 { contract_address: H160 }, +} + +impl AccountIdToCurrencyId for Runtime { + fn account_to_currency_id(account: AccountId) -> Option { + Some(match account { + // the self-reserve currency is identified by the pallet-balances address + a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve, + // the rest of the currencies, by their corresponding erc20 address + _ => match Runtime::account_to_asset_id(account) { + // A foreign asset + Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id), + // If no known prefix is identified, we consider that it's a "real" erc20 token + // (i.e. managed by a real smart contract) + None => CurrencyId::Erc20 { + contract_address: account.into(), + }, + }, + }) + } +} + +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + CurrencyId::SelfReserve => { + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + CurrencyId::Erc20 { contract_address } => { + let mut location = Erc20XcmBridgePalletLocation::get(); + location + .push_interior(Junction::AccountKey20 { + key: contract_address.0, + network: None, + }) + .ok(); + Some(location) + } + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight + = Weight::from_parts(200_000_000u64, 0); + pub const MaxAssetsForTransfer: usize = 2; + // This is how we are going to detect whether the asset is a Reserve asset + // This however is the chain part only + pub SelfLocation: Location = Location::here(); + // We need this to be able to catch when someone is trying to execute a non- + // cross-chain transfer in xtokens through the absolute path way + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(ParachainInfo::parachain_id().into()) + ].into() + }; + +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + // AssetHub fee + (1, Some(Parachain(1001u32))) => Some(50_000_000u128), + _ => None, + } + }; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdConvert = CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = AbsoluteAndRelativeReserve; +} + +// 1 WND/ROC should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +// For now we only allow to transact in the relay, although this might change in the future +// Transactors just defines the chains in which we allow transactions to be issued through +// xcm +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum Transactors { + Relay, +} + +// Default for benchmarking +#[cfg(feature = "runtime-benchmarks")] +impl Default for Transactors { + fn default() -> Self { + Transactors::Relay + } +} + +impl TryFrom for Transactors { + type Error = (); + fn try_from(value: u8) -> Result { + match value { + 0u8 => Ok(Transactors::Relay), + _ => Err(()), + } + } +} + +impl UtilityEncodeCall for Transactors { + fn encode_call(self, call: UtilityAvailableCalls) -> Vec { + match self { + Transactors::Relay => pallet_xcm_transactor::Pallet::::encode_call( + pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::), + call, + ), + } + } +} + +impl XcmTransact for Transactors { + fn destination(self) -> Location { + match self { + Transactors::Relay => Location::parent(), + } + } +} + +pub type DerivativeAddressRegistrationOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = Transactors; + type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin; + type SovereignAccountDispatcherOrigin = EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdToLocation = CurrencyIdToLocation>; + type XcmSender = XcmRouter; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = AbsoluteAndRelativeReserve; + type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo; + type HrmpManipulatorOrigin = GeneralAdminOrRoot; + type HrmpOpenOrigin = FastGeneralAdminOrRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + // This is the relative view of erc20 assets. + // Identified by this prefix + AccountKey20(contractAddress) + // We use the RELATIVE multilocation + pub Erc20XcmBridgePalletLocation: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + + // To be able to support almost all erc20 implementations, + // we provide a sufficiently hight gas limit. + pub Erc20XcmBridgeTransferGasLimit: u64 = 200_000; +} + +impl pallet_erc20_xcm_bridge::Config for Runtime { + type AccountIdConverter = LocationToH160; + type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation; + type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit; + type EvmRunner = EvmRunnerPrecompileOrEthXcm; +} + +#[cfg(feature = "runtime-benchmarks")] +mod testing { + use super::*; + use xcm_builder::V4V3LocationConverter; + + /// This From exists for benchmarking purposes. It has the potential side-effect of calling + /// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code. + impl From for CurrencyId { + fn from(location: Location) -> CurrencyId { + use xcm_primitives::AssetTypeGetter; + + // If it does not exist, for benchmarking purposes, we create the association + let asset_id = if let Some(asset_id) = + AsAssetType::::convert_location(&location) + { + asset_id + } else { + let asset_type = AssetType::Xcm( + V4V3LocationConverter::convert(&location).expect("convert to v3"), + ); + let asset_id: AssetId = asset_type.clone().into(); + AssetManager::set_asset_type_asset_id(asset_type, asset_id); + asset_id + }; + + CurrencyId::ForeignAsset(asset_id) + } + } +} diff --git a/tracing/2900/runtime/moonbase/tests/common/mod.rs b/tracing/2900/runtime/moonbase/tests/common/mod.rs new file mode 100644 index 00000000..416e9a00 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/common/mod.rs @@ -0,0 +1,394 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![allow(dead_code)] + +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use fp_evm::GenesisAccount; +use frame_support::{ + assert_ok, + traits::{OnFinalize, OnInitialize}, +}; +use moonbase_runtime::{asset_config::AssetRegistrarMetadata, xcm_config::AssetType}; +pub use moonbase_runtime::{ + currency::{GIGAWEI, SUPPLY_FACTOR, UNIT, WEI}, + AccountId, AssetId, AssetManager, Assets, AsyncBacking, AuthorInherent, Balance, Balances, + CrowdloanRewards, Ethereum, Executive, Header, InflationInfo, ParachainStaking, + ParachainSystem, Range, Runtime, RuntimeCall, RuntimeEvent, System, TransactionConverter, + TransactionPaymentAsGasPrice, UncheckedExtrinsic, HOURS, WEEKS, +}; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use polkadot_parachain::primitives::HeadData; +use sp_consensus_slots::Slot; +use sp_core::{Encode, H160}; +use sp_runtime::{traits::Dispatchable, BuildStorage, Digest, DigestItem, Perbill, Percent}; + +use std::collections::BTreeMap; + +use fp_rpc::ConvertTransaction; +use pallet_transaction_payment::Multiplier; + +// A valid signed Alice transfer. +pub const VALID_ETH_TX: &str = + "02f86d8205018085174876e80085e8d4a5100082520894f24ff3a9cf04c71dbc94d0b566f7a27b9456\ + 6cac8080c001a0e1094e1a52520a75c0255db96132076dd0f1263089f838bea548cbdbfc64a4d19f031c\ + 92a8cb04e2d68d20a6158d542a07ac440cc8d07b6e36af02db046d92df"; + +// An invalid signed Alice transfer with a gas limit artifically set to 0. +pub const INVALID_ETH_TX: &str = + "f86180843b9aca00809412cb274aad8251c875c0bf6872b67d9983e53fdd01801ca00e28ba2dd3c5a\ + 3fd467d4afd7aefb4a34b373314fff470bb9db743a84d674a0aa06e5994f2d07eafe1c37b4ce5471ca\ + ecec29011f6f5bf0b1a552c55ea348df35f"; + +pub fn rpc_run_to_block(n: u32) { + while System::block_number() < n { + Ethereum::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Ethereum::on_initialize(System::block_number()); + } +} + +/// Utility function that advances the chain to the desired block number. +/// If an author is provided, that author information is injected to all the blocks in the meantime. +pub fn run_to_block(n: u32, author: Option) { + // Finalize the first block + Ethereum::on_finalize(System::block_number()); + while System::block_number() < n { + // Set the new block number and author + match author { + Some(ref author) => { + let pre_digest = Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + }; + System::reset_events(); + System::initialize( + &(System::block_number() + 1), + &System::parent_hash(), + &pre_digest, + ); + } + None => { + System::set_block_number(System::block_number() + 1); + } + } + + increase_last_relay_slot_number(1); + + // Initialize the new block + AuthorInherent::on_initialize(System::block_number()); + ParachainStaking::on_initialize(System::block_number()); + Ethereum::on_initialize(System::block_number()); + + // Finalize the block + Ethereum::on_finalize(System::block_number()); + ParachainStaking::on_finalize(System::block_number()); + } +} + +pub fn last_event() -> RuntimeEvent { + System::events().pop().expect("Event expected").event +} + +// Test struct with the purpose of initializing xcm assets +#[derive(Clone)] +pub struct XcmAssetInitialization { + pub asset_type: AssetType, + pub metadata: AssetRegistrarMetadata, + pub balances: Vec<(AccountId, Balance)>, + pub is_sufficient: bool, +} + +pub struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, + // [collator, amount] + collators: Vec<(AccountId, Balance)>, + // [delegator, collator, nomination_amount] + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + // per-round inflation config + inflation: InflationInfo, + // AuthorId -> AccoutId mappings + mappings: Vec<(NimbusId, AccountId)>, + // Crowdloan fund + crowdloan_fund: Balance, + // Chain id + chain_id: u64, + // EVM genesis accounts + evm_accounts: BTreeMap, + // [assettype, metadata, Vec] + xcm_assets: Vec, + safe_xcm_version: Option, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![], + delegations: vec![], + collators: vec![], + inflation: InflationInfo { + expect: Range { + min: 100_000 * UNIT, + ideal: 200_000 * UNIT, + max: 500_000 * UNIT, + }, + // not used + annual: Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50), + }, + // unrealistically high parameterization, only for testing + round: Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }, + }, + mappings: vec![], + crowdloan_fund: 0, + chain_id: CHAIN_ID, + evm_accounts: BTreeMap::new(), + xcm_assets: vec![], + safe_xcm_version: None, + } + } +} + +impl ExtBuilder { + pub fn with_evm_accounts(mut self, accounts: BTreeMap) -> Self { + self.evm_accounts = accounts; + self + } + + pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self { + self.collators = collators; + self + } + + pub fn with_delegations(mut self, delegations: Vec<(AccountId, AccountId, Balance)>) -> Self { + self.delegations = delegations + .into_iter() + .map(|d| (d.0, d.1, d.2, Percent::zero())) + .collect(); + self + } + + pub fn with_xcm_assets(mut self, xcm_assets: Vec) -> Self { + self.xcm_assets = xcm_assets; + self + } + + pub fn with_crowdloan_fund(mut self, crowdloan_fund: Balance) -> Self { + self.crowdloan_fund = crowdloan_fund; + self + } + + pub fn with_mappings(mut self, mappings: Vec<(NimbusId, AccountId)>) -> Self { + self.mappings = mappings; + self + } + + pub fn with_safe_xcm_version(mut self, safe_xcm_version: u32) -> Self { + self.safe_xcm_version = Some(safe_xcm_version); + self + } + + #[allow(dead_code)] + pub fn with_inflation(mut self, inflation: InflationInfo) -> Self { + self.inflation = inflation; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_parachain_staking::GenesisConfig:: { + candidates: self.collators, + delegations: self.delegations, + inflation_config: self.inflation, + collator_commission: Perbill::from_percent(20), + parachain_bond_reserve_percent: Percent::from_percent(30), + blocks_per_round: 2 * HOURS, + num_selected_candidates: 8, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_crowdloan_rewards::GenesisConfig:: { + funded_amount: self.crowdloan_fund, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_author_mapping::GenesisConfig:: { + mappings: self.mappings, + } + .assimilate_storage(&mut t) + .unwrap(); + + let genesis_config = pallet_evm_chain_id::GenesisConfig:: { + chain_id: self.chain_id, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: self.evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_ethereum::GenesisConfig:: { + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_xcm::GenesisConfig:: { + safe_xcm_version: self.safe_xcm_version, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_transaction_payment::GenesisConfig:: { + multiplier: Multiplier::from(8u128), + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + + let xcm_assets = self.xcm_assets.clone(); + + ext.execute_with(|| { + // If any xcm assets specified, we register them here + for xcm_asset_initialization in xcm_assets { + let asset_id: AssetId = xcm_asset_initialization.asset_type.clone().into(); + AssetManager::register_foreign_asset( + root_origin(), + xcm_asset_initialization.asset_type, + xcm_asset_initialization.metadata, + 1, + xcm_asset_initialization.is_sufficient, + ) + .unwrap(); + for (account, balance) in xcm_asset_initialization.balances { + Assets::mint( + origin_of(AssetManager::account_id()), + asset_id.into(), + account, + balance, + ) + .unwrap(); + } + } + System::set_block_number(1); + }); + ext + } +} + +pub const CHAIN_ID: u64 = 1281; +pub const ALICE: [u8; 20] = [4u8; 20]; +pub const ALICE_NIMBUS: [u8; 32] = [4u8; 32]; +pub const BOB: [u8; 20] = [5u8; 20]; +pub const CHARLIE: [u8; 20] = [6u8; 20]; +pub const DAVE: [u8; 20] = [7u8; 20]; +pub const EVM_CONTRACT: [u8; 20] = [8u8; 20]; + +pub fn origin_of(account_id: AccountId) -> ::RuntimeOrigin { + ::RuntimeOrigin::signed(account_id) +} + +pub fn inherent_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::none() +} + +pub fn root_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::root() +} + +pub fn unchecked_eth_tx(raw_hex_tx: &str) -> UncheckedExtrinsic { + let converter = TransactionConverter; + converter.convert_transaction(ethereum_transaction(raw_hex_tx)) +} + +pub fn ethereum_transaction(raw_hex_tx: &str) -> pallet_ethereum::Transaction { + let bytes = hex::decode(raw_hex_tx).expect("Transaction bytes."); + let transaction = ethereum::EnvelopedDecodable::decode(&bytes[..]); + assert!(transaction.is_ok()); + transaction.unwrap() +} + +/// Mock the inherent that sets validation data in ParachainSystem, which +/// contains the `relay_chain_block_number`, which is used in `author-filter` as a +/// source of randomness to filter valid authors at each block. +pub fn set_parachain_inherent_data() { + use cumulus_primitives_core::PersistedValidationData; + use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; + + let mut relay_sproof = RelayStateSproofBuilder::default(); + relay_sproof.para_id = 100u32.into(); + relay_sproof.included_para_head = Some(HeadData(vec![1, 2, 3])); + + let additional_key_values = vec![( + moonbeam_core_primitives::well_known_relay_keys::TIMESTAMP_NOW.to_vec(), + sp_timestamp::Timestamp::default().encode(), + )]; + + relay_sproof.additional_key_values = additional_key_values; + + let (relay_parent_storage_root, relay_chain_state) = relay_sproof.into_state_root_and_proof(); + + let vfp = PersistedValidationData { + relay_parent_number: 1u32, + relay_parent_storage_root, + ..Default::default() + }; + let parachain_inherent_data = ParachainInherentData { + validation_data: vfp, + relay_chain_state: relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + assert_ok!(RuntimeCall::ParachainSystem( + cumulus_pallet_parachain_system::Call::::set_validation_data { + data: parachain_inherent_data + } + ) + .dispatch(inherent_origin())); +} + +pub(crate) fn increase_last_relay_slot_number(amount: u64) { + let last_relay_slot = u64::from(AsyncBacking::slot_info().unwrap_or_default().0); + frame_support::storage::unhashed::put( + &frame_support::storage::storage_prefix(b"AsyncBacking", b"SlotInfo"), + &((Slot::from(last_relay_slot + amount), 0)), + ); +} diff --git a/tracing/2900/runtime/moonbase/tests/evm_tracing.rs b/tracing/2900/runtime/moonbase/tests/evm_tracing.rs new file mode 100644 index 00000000..6c8b4c79 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/evm_tracing.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbase EVM tracing Integration Tests + +mod common; + +#[cfg(test)] +#[cfg(feature = "evm-tracing")] +mod tests { + use super::common::*; + + use pallet_evm::AddressMapping; + use sp_core::H160; + + use moonbeam_rpc_primitives_debug::runtime_decl_for_debug_runtime_api::DebugRuntimeApi; + use std::str::FromStr; + + #[test] + fn debug_runtime_api_trace_transaction() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * UNIT), + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * UNIT, + } + .into(), + ); + let transaction = ethereum_transaction(VALID_ETH_TX); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_transaction( + vec![non_eth_uxt.clone(), eth_uxt, non_eth_uxt.clone()], + &transaction, + &block + ) + .is_ok()); + }); + } + + #[test] + fn debug_runtime_api_trace_block() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * UNIT), + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * UNIT, + } + .into(), + ); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let eth_tx = ethereum_transaction(VALID_ETH_TX); + let eth_extrinsic_hash = eth_tx.hash(); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_block( + vec![non_eth_uxt.clone(), eth_uxt.clone(), non_eth_uxt, eth_uxt], + vec![eth_extrinsic_hash, eth_extrinsic_hash], + &block + ) + .is_ok()); + }); + } +} diff --git a/tracing/2900/runtime/moonbase/tests/integration_test.rs b/tracing/2900/runtime/moonbase/tests/integration_test.rs new file mode 100644 index 00000000..8d0831c1 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/integration_test.rs @@ -0,0 +1,3004 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbase Runtime Integration Tests + +mod common; +use common::*; + +use precompile_utils::{ + precompile_set::{is_precompile_or_fail, IsActivePrecompile}, + prelude::*, + testing::*, +}; + +use fp_evm::{Context, IsPrecompileResult}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchClass, + traits::{ + fungible::Inspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo, + StorageInfoTrait, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + StorageHasher, Twox128, +}; +use moonbase_runtime::{ + asset_config::{AssetRegistrarMetadata, ForeignAssetInstance}, + xcm_config::{AssetType, SelfReserve}, + AccountId, AssetId, AssetManager, Assets, Balances, CrowdloanRewards, + OpenTechCommitteeCollective, ParachainStaking, PolkadotXcm, Precompiles, Runtime, + RuntimeBlockWeights, RuntimeCall, RuntimeEvent, System, TransactionPayment, + TreasuryCouncilCollective, XTokens, XcmTransactor, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; +use polkadot_parachain::primitives::Sibling; +use precompile_utils::testing::MockHandle; +use sp_runtime::{ + traits::{Convert as XcmConvert, Dispatchable}, + BuildStorage, +}; +use std::str::from_utf8; +use xcm_builder::{ParentIsPreset, SiblingParachainConvertsVia}; +use xcm_executor::traits::ConvertLocation; + +use moonbeam_xcm_benchmarks::weights::XcmWeight; +use nimbus_primitives::NimbusId; +use pallet_evm::PrecompileSet; +use pallet_evm_precompileset_assets_erc20::{ + AccountIdAssetIdConversion, SELECTOR_LOG_APPROVAL, SELECTOR_LOG_TRANSFER, +}; +use pallet_transaction_payment::Multiplier; +use pallet_xcm_transactor::{Currency, CurrencyPayment, HrmpOperation, TransactWeights}; +use parity_scale_codec::Encode; +use sha3::{Digest, Keccak256}; +use sp_core::{crypto::UncheckedFrom, ByteArray, Pair, H160, H256, U256}; +use sp_runtime::{DispatchError, ModuleError}; +use xcm::latest::prelude::*; + +type AuthorMappingPCall = + pallet_evm_precompile_author_mapping::AuthorMappingPrecompileCall; +type BatchPCall = pallet_evm_precompile_batch::BatchPrecompileCall; +type CrowdloanRewardsPCall = + pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompileCall; +type XcmUtilsPCall = pallet_evm_precompile_xcm_utils::XcmUtilsPrecompileCall< + Runtime, + moonbase_runtime::xcm_config::XcmExecutorConfig, +>; +type XtokensPCall = pallet_evm_precompile_xtokens::XtokensPrecompileCall; +type ForeignAssetsPCall = pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSetCall< + Runtime, + ForeignAssetInstance, +>; +type XcmTransactorV1PCall = + pallet_evm_precompile_xcm_transactor::v1::XcmTransactorPrecompileV1Call; +type XcmTransactorV2PCall = + pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call; + +// TODO: can we construct a const U256...? +const BASE_FEE_GENISIS: u128 = 10 * GIGAWEI; + +#[test] +fn xcmp_queue_controller_origin_is_root() { + // important for the XcmExecutionManager impl of PauseExecution which uses root origin + // to suspend/resume XCM execution in xcmp_queue::on_idle + assert_ok!( + ::ControllerOrigin::ensure_origin(root_origin()) + ); +} + +#[test] +fn verify_pallet_prefixes() { + fn is_pallet_prefix(name: &str) { + // Compares the unhashed pallet prefix in the `StorageInstance` implementation by every + // storage item in the pallet P. This pallet prefix is used in conjunction with the + // item name to get the unique storage key: hash(PalletPrefix) + hash(StorageName) + // https://github.com/paritytech/substrate/blob/master/frame/support/procedural/src/pallet/ + // expand/storage.rs#L389-L401 + assert_eq!( + ::PalletInfo::name::

(), + Some(name) + ); + } + // TODO: use StorageInfoTrait from https://github.com/paritytech/substrate/pull/9246 + // This is now available with polkadot-v0.9.9 dependencies + is_pallet_prefix::("System"); + is_pallet_prefix::("Utility"); + is_pallet_prefix::("ParachainSystem"); + is_pallet_prefix::("TransactionPayment"); + is_pallet_prefix::("ParachainInfo"); + is_pallet_prefix::("EthereumChainId"); + is_pallet_prefix::("EVM"); + is_pallet_prefix::("Ethereum"); + is_pallet_prefix::("ParachainStaking"); + is_pallet_prefix::("Scheduler"); + is_pallet_prefix::("Treasury"); + is_pallet_prefix::( + "OpenTechCommitteeCollective", + ); + is_pallet_prefix::("AuthorInherent"); + is_pallet_prefix::("AuthorFilter"); + is_pallet_prefix::("CrowdloanRewards"); + is_pallet_prefix::("AuthorMapping"); + is_pallet_prefix::("MaintenanceMode"); + is_pallet_prefix::("Identity"); + is_pallet_prefix::("XcmpQueue"); + is_pallet_prefix::("CumulusXcm"); + is_pallet_prefix::("DmpQueue"); + is_pallet_prefix::("PolkadotXcm"); + is_pallet_prefix::("Assets"); + is_pallet_prefix::("XTokens"); + is_pallet_prefix::("AssetManager"); + is_pallet_prefix::("Migrations"); + is_pallet_prefix::("XcmTransactor"); + is_pallet_prefix::("ProxyGenesisCompanion"); + is_pallet_prefix::("MoonbeamOrbiters"); + is_pallet_prefix::("EthereumXcm"); + is_pallet_prefix::("Randomness"); + is_pallet_prefix::("TreasuryCouncilCollective"); + is_pallet_prefix::("MoonbeamLazyMigrations"); + is_pallet_prefix::("RelayStorageRoots"); + + let prefix = |pallet_name, storage_name| { + let mut res = [0u8; 32]; + res[0..16].copy_from_slice(&Twox128::hash(pallet_name)); + res[16..32].copy_from_slice(&Twox128::hash(storage_name)); + res.to_vec() + }; + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"TotalIssuance".to_vec(), + prefix: prefix(b"Balances", b"TotalIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"InactiveIssuance".to_vec(), + prefix: prefix(b"Balances", b"InactiveIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Account".to_vec(), + prefix: prefix(b"Balances", b"Account"), + max_values: None, + max_size: Some(100), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Locks".to_vec(), + prefix: prefix(b"Balances", b"Locks"), + max_values: None, + max_size: Some(1287), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Reserves".to_vec(), + prefix: prefix(b"Balances", b"Reserves"), + max_values: None, + max_size: Some(1037), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Holds".to_vec(), + prefix: prefix(b"Balances", b"Holds"), + max_values: None, + max_size: Some(55), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Freezes".to_vec(), + prefix: prefix(b"Balances", b"Freezes"), + max_values: None, + max_size: Some(37), + }, + ] + ); + assert_eq!( + ::storage_info(), + vec![StorageInfo { + pallet_name: b"Sudo".to_vec(), + storage_name: b"Key".to_vec(), + prefix: prefix(b"Sudo", b"Key"), + max_values: Some(1), + max_size: Some(20), + }] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Proxies".to_vec(), + prefix: prefix(b"Proxy", b"Proxies"), + max_values: None, + max_size: Some(845), + }, + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Announcements".to_vec(), + prefix: prefix(b"Proxy", b"Announcements"), + max_values: None, + max_size: Some(1837), + } + ] + ); + assert_eq!( + ::storage_info(), + vec![StorageInfo { + pallet_name: b"MaintenanceMode".to_vec(), + storage_name: b"MaintenanceMode".to_vec(), + prefix: prefix(b"MaintenanceMode", b"MaintenanceMode"), + max_values: Some(1), + max_size: None, + },] + ); + + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRoot".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRoot"), + max_values: None, + max_size: Some(44), + }, + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRootKeys".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRootKeys"), + max_values: Some(1), + max_size: Some(121), + }, + ] + ); +} + +#[test] +fn test_collectives_storage_item_prefixes() { + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"TreasuryCouncilCollective".to_vec()); + } + + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"OpenTechCommitteeCollective".to_vec()); + } +} + +#[test] +fn collective_set_members_root_origin_works() { + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert_ok!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + // OpenTechCommitteeCollective + assert_ok!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + }); +} + +#[test] +fn collective_set_members_general_admin_origin_works() { + use moonbase_runtime::{ + governance::custom_origins::Origin as CustomOrigin, OriginCaller, Utility, + }; + + ExtBuilder::default().build().execute_with(|| { + let root_caller = ::RuntimeOrigin::root(); + let alice = AccountId::from(ALICE); + + // TreasuryCouncilCollective + let _ = Utility::dispatch_as( + root_caller.clone(), + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + // OpenTechCommitteeCollective + let _ = Utility::dispatch_as( + root_caller, + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + + assert_eq!( + System::events() + .into_iter() + .filter_map(|r| { + match r.event { + RuntimeEvent::Utility(pallet_utility::Event::DispatchedAs { result }) + if result.is_ok() => + { + Some(true) + } + _ => None, + } + }) + .collect::>() + .len(), + 2 + ) + }); +} + +#[test] +fn collective_set_members_signed_origin_does_not_work() { + let alice = AccountId::from(ALICE); + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + // OpenTechCommitteeCollective + assert!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + }); +} + +#[test] +fn verify_pallet_indices() { + fn is_pallet_index(index: usize) { + assert_eq!( + ::PalletInfo::index::

(), + Some(index) + ); + } + is_pallet_index::(0); + is_pallet_index::(1); + is_pallet_index::(3); + is_pallet_index::(4); + is_pallet_index::(6); + is_pallet_index::(7); + is_pallet_index::(8); + is_pallet_index::(9); + is_pallet_index::(10); + is_pallet_index::(11); + is_pallet_index::(12); + is_pallet_index::(13); + //is_pallet_index::(14); Removed + is_pallet_index::(17); + is_pallet_index::(18); + is_pallet_index::(19); + is_pallet_index::(20); + is_pallet_index::(21); + is_pallet_index::(22); + is_pallet_index::(23); + is_pallet_index::(24); + is_pallet_index::(25); + is_pallet_index::(26); + is_pallet_index::(27); + is_pallet_index::(28); + is_pallet_index::(29); + is_pallet_index::(30); + is_pallet_index::(31); + is_pallet_index::(32); + is_pallet_index::(33); + is_pallet_index::(34); + is_pallet_index::(37); + is_pallet_index::(38); + is_pallet_index::(39); + is_pallet_index::(40); + is_pallet_index::(46); + is_pallet_index::(51); +} + +#[test] +fn verify_reserved_indices() { + use frame_metadata::*; + let metadata = moonbase_runtime::Runtime::metadata(); + let metadata = match metadata.1 { + RuntimeMetadata::V14(metadata) => metadata, + _ => panic!("metadata has been bumped, test needs to be updated"), + }; + // 35: BaseFee + // 36: pallet_assets:: + let reserved = vec![35, 36]; + let existing = metadata + .pallets + .iter() + .map(|p| p.index) + .collect::>(); + assert!(reserved.iter().all(|index| !existing.contains(index))); +} + +#[test] +fn verify_proxy_type_indices() { + assert_eq!(moonbase_runtime::ProxyType::Any as u8, 0); + assert_eq!(moonbase_runtime::ProxyType::NonTransfer as u8, 1); + assert_eq!(moonbase_runtime::ProxyType::Governance as u8, 2); + assert_eq!(moonbase_runtime::ProxyType::Staking as u8, 3); + assert_eq!(moonbase_runtime::ProxyType::CancelProxy as u8, 4); + assert_eq!(moonbase_runtime::ProxyType::Balances as u8, 5); + assert_eq!(moonbase_runtime::ProxyType::AuthorMapping as u8, 6); + assert_eq!(moonbase_runtime::ProxyType::IdentityJudgement as u8, 7); +} + +#[test] +fn join_collator_candidates() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 2_000 * UNIT), + (AccountId::from(CHARLIE), 1_100 * UNIT), + (AccountId::from(DAVE), 1_000 * UNIT), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 1_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_delegations(vec![ + (AccountId::from(CHARLIE), AccountId::from(ALICE), 50 * UNIT), + (AccountId::from(CHARLIE), AccountId::from(BOB), 50 * UNIT), + ]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(ALICE)), + 1_000 * UNIT, + 2u32 + ), + pallet_parachain_staking::Error::::CandidateExists + ); + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 1_000 * UNIT, + 2u32 + ), + pallet_parachain_staking::Error::::DelegatorExists + ); + assert!(System::events().is_empty()); + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(DAVE)), + 1_000 * UNIT, + 2u32 + )); + assert_eq!( + last_event(), + RuntimeEvent::ParachainStaking( + pallet_parachain_staking::Event::JoinedCollatorCandidates { + account: AccountId::from(DAVE), + amount_locked: 1_000 * UNIT, + new_total_amt_locked: 3_100 * UNIT + } + ) + ); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(ALICE)); + assert_eq!(candidates.0[0].amount, 1_050 * UNIT); + assert_eq!(candidates.0[1].owner, AccountId::from(BOB)); + assert_eq!(candidates.0[1].amount, 1_050 * UNIT); + assert_eq!(candidates.0[2].owner, AccountId::from(DAVE)); + assert_eq!(candidates.0[2].amount, 1_000 * UNIT); + }); +} + +#[test] +fn transfer_through_evm_to_stake() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * UNIT)]) + .build() + .execute_with(|| { + // Charlie has no balance => fails to stake + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 1_000 * UNIT, + 0u32 + ), + DispatchError::Module(ModuleError { + index: 12, + error: [8, 0, 0, 0], + message: Some("InsufficientBalance") + }) + ); + + // Alice transfer from free balance 2000 UNIT to Bob + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 2_000 * UNIT, + )); + assert_eq!(Balances::free_balance(AccountId::from(BOB)), 2_000 * UNIT); + + let gas_limit = 100000u64; + // Bob transfers 1000 UNIT to Charlie via EVM + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(CHARLIE), + input: Vec::new(), + value: (1_000 * UNIT).into(), + gas_limit, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: None, + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + assert_eq!( + Balances::free_balance(AccountId::from(CHARLIE)), + 1_000 * UNIT, + ); + + // Charlie can stake now + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 1_000 * UNIT, + 0u32, + ),); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(CHARLIE)); + assert_eq!(candidates.0[0].amount, 1_000 * UNIT); + }); +} + +#[test] +fn reward_block_authors() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 100 extra tokens for her mapping deposit + (AccountId::from(ALICE), 2_100 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + increase_last_relay_slot_number(1); + // Just before round 3 + run_to_block(2399, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // no rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 1_100 * UNIT, + ); + assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 500 * UNIT,); + run_to_block(2401, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 1213666666584000000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 541333333292000000000, + ); + }); +} + +#[test] +fn reward_block_authors_with_parachain_bond_reserved() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 100 extra tokens for her mapping deposit + (AccountId::from(ALICE), 2_100 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + (AccountId::from(CHARLIE), UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + increase_last_relay_slot_number(1); + assert_ok!(ParachainStaking::set_parachain_bond_account( + root_origin(), + AccountId::from(CHARLIE), + ),); + + // Stop just before round 2 + run_to_block(1199, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + // no rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 1_100 * UNIT, + ); + assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 500 * UNIT,); + assert_eq!(Balances::usable_balance(AccountId::from(CHARLIE)), UNIT,); + + // Go to round 2 + run_to_block(1201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + // 30% reserved for parachain bond + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 47515000000000000000, + ); + + // Go to round 3 + run_to_block(2401, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 1182693333281650000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 525841666640825000000, + ); + // 30% again reserved for parachain bond + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 94727725000000000000, + ); + }); +} + +#[test] +fn initialize_crowdloan_addresses_with_batch_and_pay() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * UNIT); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * UNIT); + let expected = RuntimeEvent::Utility(pallet_utility::Event::BatchCompleted); + assert_eq!(last_event(), expected); + // This one should fail, as we already filled our data + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch { + calls: vec![RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![([4u8; 32].into(), Some(AccountId::from(ALICE)), 432000)] + } + )] + }) + .dispatch(root_origin()) + ); + let expected_fail = RuntimeEvent::Utility(pallet_utility::Event::BatchInterrupted { + index: 0, + error: DispatchError::Module(ModuleError { + index: 20, + error: [8, 0, 0, 0], + message: None, + }), + }); + assert_eq!(last_event(), expected_fail); + // Claim 1 block. + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(CHARLIE)))); + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(DAVE)))); + + let vesting_period = 4 * WEEKS as u128; + let per_block = (1_050_000 * UNIT) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (450_000 * UNIT) + per_block + ); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (450_000 * UNIT) + per_block + ); + // The total claimed reward should be equal to the account balance at this point. + assert_eq!( + Balances::balance(&AccountId::from(CHARLIE)), + (450_000 * UNIT) + per_block + ); + assert_eq!( + Balances::balance(&AccountId::from(DAVE)), + (450_000 * UNIT) + per_block + ); + assert_noop!( + CrowdloanRewards::claim(origin_of(AccountId::from(ALICE))), + pallet_crowdloan_rewards::Error::::NoAssociatedClaim + ); + }); +} + +#[test] +fn initialize_crowdloan_address_and_change_with_relay_key_sig() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + + let (pair1, _) = sp_core::sr25519::Pair::generate(); + let (pair2, _) = sp_core::sr25519::Pair::generate(); + + let public1 = pair1.public(); + let public2 = pair2.public(); + + // signature: + // WRAP_BYTES|| NetworkIdentifier|| new_account || previous_account || WRAP_BYTES + let mut message = pallet_crowdloan_rewards::WRAPPED_BYTES_PREFIX.to_vec(); + message.append(&mut b"moonbase-".to_vec()); + message.append(&mut AccountId::from(DAVE).encode()); + message.append(&mut AccountId::from(CHARLIE).encode()); + message.append(&mut pallet_crowdloan_rewards::WRAPPED_BYTES_POSTFIX.to_vec()); + + let signature1 = pair1.sign(&message); + let signature2 = pair2.sign(&message); + + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + // two relay accounts pointing at the same reward account + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public1.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public2.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 900_000 * UNIT); + + // this should fail, as we are only providing one signature + assert_noop!( + CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![(public1.into(), signature1.clone().into())] + ), + pallet_crowdloan_rewards::Error::::InsufficientNumberOfValidProofs + ); + + // this should be valid + assert_ok!(CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![ + (public1.into(), signature1.into()), + (public2.into(), signature2.into()) + ] + )); + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (900_000 * UNIT) + ); + }); +} + +#[test] +fn claim_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * UNIT); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * UNIT); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Alice uses the crowdloan precompile to claim through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENISIS.into(); + + // Construct the call data (selector, amount) + let mut call_data = Vec::::from([0u8; 4]); + call_data[0..4].copy_from_slice(&Keccak256::digest(b"claim()")[0..4]); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let vesting_period = 4 * WEEKS as u128; + let per_block = (1_050_000 * UNIT) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (450_000 * UNIT) + per_block + ); + }) +} + +#[test] +fn is_contributor_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Assert precompile reports Bob is not a contributor + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(BOB.into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(false); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(CHARLIE.into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(true); + }) +} + +#[test] +fn reward_info_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + let expected_total: U256 = (1_500_000 * UNIT).into(); + let expected_claimed: U256 = (450_000 * UNIT).into(); + + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::reward_info { + contributor: Address(AccountId::from(CHARLIE).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns((expected_total, expected_claimed)); + }) +} + +#[test] +fn update_reward_address_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * UNIT) + .build() + .execute_with(|| { + // set parachain inherent data + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * UNIT + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Charlie uses the crowdloan precompile to update address through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENISIS.into(); + + // Construct the input data to check if Bob is a contributor + let mut call_data = Vec::::from([0u8; 36]); + call_data[0..4] + .copy_from_slice(&Keccak256::digest(b"update_reward_address(address)")[0..4]); + call_data[16..36].copy_from_slice(&ALICE); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + assert!(CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)).is_none()); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(ALICE)) + .unwrap() + .claimed_reward, + (450_000 * UNIT) + ); + }) +} + +#[test] +fn asset_can_be_registered() { + ExtBuilder::default().build().execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonbase_runtime::AssetId = source_location.clone().into(); + let asset_metadata = AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }; + assert_ok!(AssetManager::register_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + source_location, + asset_metadata, + 1u128, + true, + )); + assert!(AssetManager::asset_id_type(source_id).is_some()); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_supply_and_balance() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * UNIT)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Assert the asset has been created with the correct supply + assert_eq!(Assets::total_supply(relay_asset_id), 1_000 * UNIT); + + // Access totalSupply through precompile. Important that the context is correct + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::total_supply {}, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * UNIT)); + + // Access balanceOf through precompile + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(ALICE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * UNIT)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * UNIT)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Transfer tokens from Alice to Bob, 400 UNIT. + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::transfer { + to: Address(BOB.into()), + value: { 400 * UNIT }.into(), + }, + ) + .expect_cost(24377) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * UNIT)), + )) + .execute_returns(true); + + // Make sure BOB has 400 UNIT + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(BOB.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * UNIT)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_approve() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * UNIT)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Aprove Bob for spending 400 UNIT from Alice + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::approve { + spender: Address(BOB.into()), + value: { 400 * UNIT }.into(), + }, + ) + .expect_cost(14429) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * UNIT)), + )) + .execute_returns(true); + + // Transfer tokens from Alice to Charlie by using BOB as origin + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::transfer_from { + from: Address(ALICE.into()), + to: Address(CHARLIE.into()), + value: { 400 * UNIT }.into(), + }, + ) + .expect_cost(29748) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + solidity::encode_event_data(U256::from(400 * UNIT)), + )) + .execute_returns(true); + + // Make sure CHARLIE has 400 UNIT + Precompiles::new() + .prepare_test( + CHARLIE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(CHARLIE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * UNIT)); + }); +} + +#[test] +fn xtokens_precompiles_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer { + currency_address: Address(asset_precompile_address.into()), + amount: 500_000_000_000_000u128.into(), + destination, + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()) + }) +} + +#[test] +fn xtokens_precompiles_transfer_multiasset() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // This time we transfer it through TransferMultiAsset + // Instead of the address, we encode directly the multilocation referencing the asset + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer_multiasset { + // We want to transfer the relay token + asset: Location::parent(), + amount: 500_000_000_000_000u128.into(), + destination, + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()); + }) +} + +#[test] +fn xtokens_precompiles_transfer_native() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // Its address is + let asset_precompile_address = H160::from_low_u64_be(2050); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer { + currency_address: Address(asset_precompile_address), + amount: { 500 * UNIT }.into(), + destination: destination.clone(), + weight: 4_000_000, + }, + ) + .expect_cost(16000) + .expect_no_logs() + .execute_returns(()); + }) +} + +fn run_with_system_weight(w: Weight, mut assertions: F) +where + F: FnMut() -> (), +{ + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); +} + +#[test] +#[rustfmt::skip] +fn length_fee_is_sensible() { + use sp_runtime::testing::TestXt; + + // tests that length fee is sensible for a few hypothetical transactions + ExtBuilder::default().build().execute_with(|| { + let call = frame_system::Call::remark:: { remark: vec![] }; + let uxt: TestXt<_, ()> = TestXt::new(call, Some((1u64, ()))); + + let calc_fee = |len: u32| -> Balance { + moonbase_runtime::TransactionPayment::query_fee_details(uxt.clone(), len) + .inclusion_fee + .expect("fee should be calculated") + .len_fee + }; + + // editorconfig-checker-disable + // left: cost of length fee, right: size in bytes + // /------------- proportional component: O(N * 1B) + // | /- exponential component: O(N ** 3) + // | | + assert_eq!( 1_000_000_001, calc_fee(1)); + assert_eq!( 10_000_001_000, calc_fee(10)); + assert_eq!( 100_001_000_000, calc_fee(100)); + assert_eq!( 1_001_000_000_000, calc_fee(1_000)); + assert_eq!( 11_000_000_000_000, calc_fee(10_000)); // inflection point + assert_eq!( 1_100_000_000_000_000, calc_fee(100_000)); + assert_eq!( 1_001_000_000_000_000_000, calc_fee(1_000_000)); // one UNIT, ~ 1MB + assert_eq!( 1_000_010_000_000_000_000_000, calc_fee(10_000_000)); + assert_eq!(1_000_000_100_000_000_000_000_000, calc_fee(100_000_000)); + // editorconfig-checker-enable + }); +} + +#[test] +fn multiplier_can_grow_from_zero() { + use frame_support::traits::Get; + + let minimum_multiplier = moonbase_runtime::MinimumMultiplier::get(); + let target = moonbase_runtime::TargetBlockFullness::get() + * RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = moonbase_runtime::FastAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) +} + +#[test] +fn ethereum_invalid_transaction() { + ExtBuilder::default().build().execute_with(|| { + // Ensure an extrinsic not containing enough gas limit to store the transaction + // on chain is rejected. + assert_eq!( + Executive::apply_extrinsic(unchecked_eth_tx(INVALID_ETH_TX)), + Err( + sp_runtime::transaction_validity::TransactionValidityError::Invalid( + sp_runtime::transaction_validity::InvalidTransaction::Custom(0u8) + ) + ) + ); + }); +} + +#[test] +fn transfer_ed_0_substrate() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), (1 * UNIT) + (1 * WEI)), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // Substrate transfer + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 1 * UNIT, + )); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI); + }); +} + +#[test] +fn initial_gas_fee_is_correct() { + use fp_evm::FeeCalculator; + + ExtBuilder::default().build().execute_with(|| { + let multiplier = TransactionPayment::next_fee_multiplier(); + assert_eq!(multiplier, Multiplier::from(8u128)); + + assert_eq!( + TransactionPaymentAsGasPrice::min_gas_price(), + ( + 10_000_000_000u128.into(), + Weight::from_parts(25_000_000u64, 0) + ) + ); + }); +} + +#[test] +fn transfer_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * UNIT) + (21_000 * BASE_FEE_GENISIS)) + (1 * WEI), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * UNIT).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI,); + }); +} + +#[test] +fn refund_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * UNIT) + (21_777 * BASE_FEE_GENISIS)), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer that zeroes ALICE + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * UNIT).into(), + gas_limit: 21_777u64, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // ALICE is refunded + assert_eq!( + Balances::free_balance(AccountId::from(ALICE)), + 777 * BASE_FEE_GENISIS, + ); + }); +} + +#[test] +fn author_does_not_receive_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * UNIT) + (21_000 * (500 * GIGAWEI)), + )]) + .build() + .execute_with(|| { + // Some block author as seen by pallet-evm. + let author = AccountId::from(>::find_author()); + // Currently the default impl of the evm uses `deposit_into_existing`. + // If we were to use this implementation, and for an author to receive eventual tips, + // the account needs to be somehow initialized, otherwise the deposit would fail. + Balances::make_free_balance_be(&author, 100 * UNIT); + + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * UNIT).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(300 * GIGAWEI), + max_priority_fee_per_gas: Some(U256::from(200 * GIGAWEI)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // Author free balance didn't change. + assert_eq!(Balances::free_balance(author), 100 * UNIT,); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_with_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * UNIT) + (21_000 * (2 * BASE_FEE_GENISIS)), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * UNIT).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(2 * BASE_FEE_GENISIS), + max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENISIS)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + // Fee is 1 * base_fee + tip. + let fee = ((2 * BASE_FEE_GENISIS) * 21_000) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonbase_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_without_priority_fee() { + use fp_evm::FeeCalculator; + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * UNIT) + (21_000 * (2 * BASE_FEE_GENISIS)), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * UNIT).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + // Fee is 1 GWEI base fee. + let base_fee = TransactionPaymentAsGasPrice::min_gas_price().0; + assert_eq!(base_fee.as_u128(), BASE_FEE_GENISIS); // hint in case following asserts fail + let fee = (base_fee.as_u128() * 21_000u128) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonbase_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn root_can_change_default_xcm_vers() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let source_id: moonbase_runtime::AssetId = source_location.clone().into(); + // Default XCM version is not set yet, so xtokens should fail because it does not + // know with which version to send + assert_noop!( + XTokens::transfer( + origin_of(AccountId::from(ALICE)), + moonbase_runtime::xcm_config::CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest.clone())), + WeightLimit::Limited(4000000000.into()) + ), + orml_xtokens::Error::::XcmExecutionFailed + ); + + // Root sets the defaultXcm + assert_ok!(PolkadotXcm::force_default_xcm_version( + root_origin(), + Some(2) + )); + + // Now transferring does not fail + assert_ok!(XTokens::transfer( + origin_of(AccountId::from(ALICE)), + moonbase_runtime::xcm_config::CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest)), + WeightLimit::Limited(4000000000.into()) + )); + }) +} + +#[test] +fn transactor_cannot_use_more_than_max_weight() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonbase_runtime::AssetId = source_location.clone().into(); + assert_ok!(XcmTransactor::register( + root_origin(), + AccountId::from(ALICE), + 0, + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + 1, + )); + + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonbase_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + vec![], + // 20000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonbase_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsCurrencyId( + moonbase_runtime::xcm_config::CurrencyId::ForeignAsset(source_id) + ), + fee_amount: None + }, + vec![], + // 20000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + }) +} + +#[test] +fn root_can_use_hrmp_manage() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .build() + .execute_with(|| { + // It fails sending, because the router does not work in test mode + // But all rest checks pass + assert_noop!( + XcmTransactor::hrmp_manage( + root_origin(), + HrmpOperation::Accept { + para_id: 2000u32.into() + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(10000) + }, + // 20000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: Some(Limited(20000.into())) + } + ), + pallet_xcm_transactor::Error::::ErrorValidating + ); + }) +} + +#[test] +fn transact_through_signed_precompile_works_v1() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::parent(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let xcm_transactor_v1_precompile_address = H160::from_low_u64_be(2054); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + Weight::from_parts(200_000, (xcm_primitives::DEFAULT_PROOF_SIZE) + 4000), + Some(4000.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + 1, + )); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v1_precompile_address, + XcmTransactorV1PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 15000, + call: bytes.into(), + }, + ) + .expect_cost(17559) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn transact_through_signed_precompile_works_v2() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::parent(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .expect_cost(17559) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn transact_through_signed_cannot_send_to_local_chain() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::here(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error:") + && from_utf8(&output).unwrap().contains("ErrorValidating") + }); + }); +} + +// Test to ensure we can use either in crowdloan rewards without worrying for migrations +#[test] +fn account_id_32_encodes_like_32_byte_u8_slice() { + let account_as_account_id_32: sp_runtime::AccountId32 = [1u8; 32].into(); + let account_as_slice = [1u8; 32]; + assert_eq!(account_as_account_id_32.encode(), account_as_slice.encode()); +} + +#[test] +fn author_mapping_precompile_associate_update_and_clear() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .build() + .execute_with(|| { + let author_mapping_precompile_address = H160::from_low_u64_be(2055); + let first_nimbus_id: NimbusId = + sp_core::sr25519::Public::unchecked_from([1u8; 32]).into(); + let first_vrf_id: session_keys_primitives::VrfId = + sp_core::sr25519::Public::unchecked_from([1u8; 32]).into(); + let second_nimbus_id: NimbusId = + sp_core::sr25519::Public::unchecked_from([2u8; 32]).into(); + let second_vrf_id: session_keys_primitives::VrfId = + sp_core::sr25519::Public::unchecked_from([2u8; 32]).into(); + + // Associate it + Precompiles::new() + .prepare_test( + ALICE, + author_mapping_precompile_address, + AuthorMappingPCall::add_association { + nimbus_id: [1u8; 32].into(), + }, + ) + .expect_cost(15144) + .expect_no_logs() + .execute_returns(()); + + let expected_associate_event = + RuntimeEvent::AuthorMapping(pallet_author_mapping::Event::KeysRegistered { + nimbus_id: first_nimbus_id.clone(), + account_id: AccountId::from(ALICE), + keys: first_vrf_id.clone(), + }); + assert_eq!(last_event(), expected_associate_event); + + // Update it + Precompiles::new() + .prepare_test( + ALICE, + author_mapping_precompile_address, + AuthorMappingPCall::update_association { + old_nimbus_id: [1u8; 32].into(), + new_nimbus_id: [2u8; 32].into(), + }, + ) + .expect_cost(14725) + .expect_no_logs() + .execute_returns(()); + + let expected_update_event = + RuntimeEvent::AuthorMapping(pallet_author_mapping::Event::KeysRotated { + new_nimbus_id: second_nimbus_id.clone(), + account_id: AccountId::from(ALICE), + new_keys: second_vrf_id.clone(), + }); + assert_eq!(last_event(), expected_update_event); + + // Clear it + Precompiles::new() + .prepare_test( + ALICE, + author_mapping_precompile_address, + AuthorMappingPCall::clear_association { + nimbus_id: [2u8; 32].into(), + }, + ) + .expect_cost(15172) + .expect_no_logs() + .execute_returns(()); + + let expected_clear_event = + RuntimeEvent::AuthorMapping(pallet_author_mapping::Event::KeysRemoved { + nimbus_id: second_nimbus_id, + account_id: AccountId::from(ALICE), + keys: second_vrf_id, + }); + assert_eq!(last_event(), expected_clear_event); + }); +} + +#[test] +fn author_mapping_register_and_set_keys() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .build() + .execute_with(|| { + let author_mapping_precompile_address = H160::from_low_u64_be(2055); + let first_nimbus_id: NimbusId = + sp_core::sr25519::Public::unchecked_from([1u8; 32]).into(); + let first_vrf_key: session_keys_primitives::VrfId = + sp_core::sr25519::Public::unchecked_from([3u8; 32]).into(); + let second_nimbus_id: NimbusId = + sp_core::sr25519::Public::unchecked_from([2u8; 32]).into(); + let second_vrf_key: session_keys_primitives::VrfId = + sp_core::sr25519::Public::unchecked_from([4u8; 32]).into(); + + // Associate it + Precompiles::new() + .prepare_test( + ALICE, + author_mapping_precompile_address, + AuthorMappingPCall::set_keys { + keys: solidity::encode_arguments(( + H256::from([1u8; 32]), + H256::from([3u8; 32]), + )) + .into(), + }, + ) + .expect_cost(16273) + .expect_no_logs() + .execute_returns(()); + + let expected_associate_event = + RuntimeEvent::AuthorMapping(pallet_author_mapping::Event::KeysRegistered { + nimbus_id: first_nimbus_id.clone(), + account_id: AccountId::from(ALICE), + keys: first_vrf_key.clone(), + }); + assert_eq!(last_event(), expected_associate_event); + + // Update it + Precompiles::new() + .prepare_test( + ALICE, + author_mapping_precompile_address, + AuthorMappingPCall::set_keys { + keys: solidity::encode_arguments(( + H256::from([2u8; 32]), + H256::from([4u8; 32]), + )) + .into(), + }, + ) + .expect_cost(16273) + .expect_no_logs() + .execute_returns(()); + + let expected_update_event = + RuntimeEvent::AuthorMapping(pallet_author_mapping::Event::KeysRotated { + new_nimbus_id: second_nimbus_id.clone(), + account_id: AccountId::from(ALICE), + new_keys: second_vrf_key.clone(), + }); + assert_eq!(last_event(), expected_update_event); + }); +} + +#[test] +fn test_xcm_utils_ml_tp_account() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_address_parent: H160 = + ParentIsPreset::::convert_location(&Location::parent()) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: Location::parent(), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parent)); + + let parachain_2000_multilocation = Location::new(1, [Parachain(2000)]); + let expected_address_parachain: H160 = + SiblingParachainConvertsVia::::convert_location( + ¶chain_2000_multilocation, + ) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: parachain_2000_multilocation, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parachain)); + + let alice_in_parachain_2000_multilocation = Location::new( + 1, + [ + Parachain(2000), + AccountKey20 { + network: None, + key: ALICE, + }, + ], + ); + let expected_address_alice_in_parachain_2000 = + xcm_builder::HashedDescription::< + AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&alice_in_parachain_2000_multilocation) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: alice_in_parachain_2000_multilocation, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_alice_in_parachain_2000)); + }); +} + +#[test] +fn test_xcm_utils_weight_message() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_weight = + XcmWeight::::clear_origin().ref_time(); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + let input = XcmUtilsPCall::weight_message { + message: message.into(), + }; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(0) + .expect_no_logs() + .execute_returns(expected_weight); + }); +} + +#[test] +fn test_xcm_utils_get_units_per_second() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let location = SelfReserve::get(); + + let input = XcmUtilsPCall::get_units_per_second { location }; + + let expected_units = + WEIGHT_REF_TIME_PER_SECOND as u128 * moonbase_runtime::currency::WEIGHT_FEE; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(expected_units); + }); +} + +#[test] +fn precompile_existence() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let precompile_addresses: std::collections::BTreeSet<_> = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 2048, 2049, 2050, 2051, 2052, 2053, + 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, + 2068, 2069, 2070, 2071, 2072, 2073, + ] + .into_iter() + .map(H160::from_low_u64_be) + .collect(); + + for i in 0..3000 { + let address = H160::from_low_u64_be(i); + + if precompile_addresses.contains(&address) { + assert!( + is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return true", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_some(), + "execute({},..) should return Some(_)", + i + ); + } else { + assert!( + !is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return false", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_none(), + "execute({},..) should return None", + i + ); + } + } + }); +} + +#[test] +fn removed_precompiles() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let removed_precompiles = [1025, 2051, 2062, 2063]; + + for i in 1..3000 { + let address = H160::from_low_u64_be(i); + + if !is_precompile_or_fail::(address, 100_000u64).expect("to be ok") { + continue; + } + + if !removed_precompiles.contains(&i) { + assert!( + match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} should be an active precompile" + ); + continue; + } + + assert!( + !match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} shouldn't be an active precompile" + ); + + precompiles + .prepare_test(Alice, address, []) + .execute_reverts(|out| out == b"Removed precompile"); + } + }) +} + +#[test] +fn substrate_based_fees_zero_txn_costs_only_base_extrinsic() { + use frame_support::dispatch::{DispatchInfo, Pays}; + use moonbase_runtime::{currency, EXTRINSIC_BASE_WEIGHT}; + + ExtBuilder::default().build().execute_with(|| { + let size_bytes = 0; + let tip = 0; + let dispatch_info = DispatchInfo { + weight: Weight::zero(), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + + assert_eq!( + TransactionPayment::compute_fee(size_bytes, &dispatch_info, tip), + EXTRINSIC_BASE_WEIGHT.ref_time() as u128 * currency::WEIGHT_FEE, + ); + }); +} + +#[test] +fn deal_with_fees_handles_tip() { + use frame_support::traits::OnUnbalanced; + use moonbase_runtime::{DealWithFees, Treasury}; + + ExtBuilder::default().build().execute_with(|| { + // This test checks the functionality of the `DealWithFees` trait implementation in the runtime. + // It simulates a scenario where a fee and a tip are issued to an account and ensures that the + // treasury receives the correct amount (20% of the total), and the rest is burned (80%). + // + // The test follows these steps: + // 1. It issues a fee of 100 and a tip of 1000. + // 2. It checks the total supply before the fee and tip are dealt with, which should be 1_100. + // 3. It checks that the treasury's balance is initially 0. + // 4. It calls `DealWithFees::on_unbalanceds` with the fee and tip. + // 5. It checks that the treasury's balance is now 220 (20% of the fee and tip). + // 6. It checks that the total supply has decreased by 880 (80% of the fee and tip), indicating + // that this amount was burned. + let fee = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(100); + let tip = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(1000); + + let total_supply_before = Balances::total_issuance(); + assert_eq!(total_supply_before, 1_100); + assert_eq!(Balances::free_balance(&Treasury::account_id()), 0); + + DealWithFees::on_unbalanceds(vec![fee, tip].into_iter()); + + // treasury should have received 20% + assert_eq!(Balances::free_balance(&Treasury::account_id()), 220); + + // verify 80% burned + let total_supply_after = Balances::total_issuance(); + assert_eq!(total_supply_before - total_supply_after, 880); + }); +} + +#[test] +fn evm_revert_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + // Batch a transfer followed by an invalid call to batch. + // Thus BatchAll will revert the transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + input: BatchPCall::batch_all { + to: vec![Address(BOB.into()), Address(batch_precompile_address)].into(), + value: vec![U256::from(1 * UNIT), U256::zero()].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 0, "there should be no transfer event"); + }); +} + +#[test] +fn evm_success_keeps_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + input: BatchPCall::batch_all { + to: vec![Address(BOB.into())].into(), + value: vec![U256::from(1 * UNIT)].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: U256::from(BASE_FEE_GENISIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 1, "there should be 1 transfer event"); + }); +} + +#[test] +fn validate_transaction_fails_on_filtered_call() { + use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidityError, + }; + use sp_transaction_pool::runtime_api::runtime_decl_for_tagged_transaction_queue::TaggedTransactionQueueV3; // editorconfig-checker-disable-line + + ExtBuilder::default().build().execute_with(|| { + let xt = UncheckedExtrinsic::new_unsigned( + pallet_evm::Call::::call { + source: Default::default(), + target: H160::default(), + input: Vec::new(), + value: Default::default(), + gas_limit: Default::default(), + max_fee_per_gas: Default::default(), + max_priority_fee_per_gas: Default::default(), + nonce: Default::default(), + access_list: Default::default(), + } + .into(), + ); + + assert_eq!( + Runtime::validate_transaction(TransactionSource::External, xt, Default::default(),), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); + }); +} + +#[cfg(test)] +mod fee_tests { + use super::*; + use fp_evm::FeeCalculator; + use frame_support::{ + traits::{ConstU128, OnFinalize}, + weights::{ConstantMultiplier, WeightToFee}, + }; + use moonbase_runtime::{ + currency, BlockWeights, FastAdjustingFeeUpdate, LengthToFee, MinimumMultiplier, + TargetBlockFullness, NORMAL_WEIGHT, WEIGHT_PER_GAS, + }; + use sp_runtime::{BuildStorage, FixedPointNumber, Perbill}; + + fn run_with_system_weight(w: Weight, mut assertions: F) + where + F: FnMut() -> (), + { + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); + } + + #[test] + fn test_multiplier_can_grow_from_zero() { + let minimum_multiplier = MinimumMultiplier::get(); + let target = TargetBlockFullness::get() + * BlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = FastAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) + } + + #[test] + fn test_fee_calculation() { + let base_extrinsic = BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let multiplier = sp_runtime::FixedU128::from_float(0.999000000000000000); + let extrinsic_len = 100u32; + let extrinsic_weight = 5_000u64; + let tip = 42u128; + type WeightToFeeImpl = + ConstantMultiplier>; + type LengthToFeeImpl = LengthToFee; + + // base_fee + (multiplier * extrinsic_weight_fee) + extrinsic_length_fee + tip + let expected_fee = + WeightToFeeImpl::weight_to_fee(&base_extrinsic) + + multiplier.saturating_mul_int(WeightToFeeImpl::weight_to_fee( + &Weight::from_parts(extrinsic_weight, 1), + )) + LengthToFeeImpl::weight_to_fee(&Weight::from_parts(extrinsic_len as u64, 1)) + + tip; + + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier); + let actual_fee = TransactionPayment::compute_fee( + extrinsic_len, + &frame_support::dispatch::DispatchInfo { + class: DispatchClass::Normal, + pays_fee: frame_support::dispatch::Pays::Yes, + weight: Weight::from_parts(extrinsic_weight, 1), + }, + tip, + ); + + assert_eq!( + expected_fee, + actual_fee, + "The actual fee did not match the expected fee, diff {}", + actual_fee - expected_fee + ); + }); + } + + #[test] + fn test_min_gas_price_is_deterministic() { + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + let multiplier = sp_runtime::FixedU128::from_u32(1); + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier); + let actual = TransactionPaymentAsGasPrice::min_gas_price().0; + let expected: U256 = multiplier + .saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128)) + .into(); + + assert_eq!(expected, actual); + }); + } + + #[test] + fn test_min_gas_price_has_no_precision_loss_from_saturating_mul_int() { + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + let multiplier_1 = sp_runtime::FixedU128::from_float(0.999593900000000000); + let multiplier_2 = sp_runtime::FixedU128::from_float(0.999593200000000000); + + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier_1); + let a = TransactionPaymentAsGasPrice::min_gas_price(); + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier_2); + let b = TransactionPaymentAsGasPrice::min_gas_price(); + + assert_ne!( + a, b, + "both gas prices were equal, unexpected precision loss incurred" + ); + }); + } + + #[test] + fn test_fee_scenarios() { + use sp_runtime::FixedU128; + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + let weight_fee_per_gas = currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128); + let sim = |start_gas_price: u128, fullness: Perbill, num_blocks: u64| -> U256 { + let start_multiplier = + FixedU128::from_rational(start_gas_price, weight_fee_per_gas); + pallet_transaction_payment::NextFeeMultiplier::::set(start_multiplier); + + let block_weight = NORMAL_WEIGHT * fullness; + + for i in 0..num_blocks { + System::set_block_number(i as u32); + System::set_block_consumed_resources(block_weight, 0); + TransactionPayment::on_finalize(i as u32); + } + + TransactionPaymentAsGasPrice::min_gas_price().0 + }; + + // The expected values are the ones observed during test execution, + // they are expected to change when parameters that influence + // the fee calculation are changed, and should be updated accordingly. + // If a test fails when nothing specific to fees has changed, + // it may indicate an unexpected collateral effect and should be investigated + + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(0), 1), + U256::from(998_002_000), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(25), 1), + U256::from(999_000_500), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(50), 1), + U256::from(1_000_000_000), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(100), 1), + U256::from(1_002_002_000), + ); + + // 1 "real" hour (at 12-second blocks) + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(0), 300), + U256::from(548_811_855), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(25), 300), + U256::from(740_818_257), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(50), 300), + U256::from(1_000_000_000), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(100), 300), + U256::from(1_822_118_072u128), + ); + + // 1 "real" day (at 12-second blocks) + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(0), 7200), + U256::from(125_000_000), // lower bound enforced + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(25), 7200), + U256::from(125_000_000), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(50), 7200), + U256::from(1_000_000_000u128), + ); + assert_eq!( + sim(1_000_000_000, Perbill::from_percent(100), 7200), + U256::from(125_000_000_000_000u128), // upper bound enforced + ); + }); + } +} diff --git a/tracing/2900/runtime/moonbase/tests/runtime_apis.rs b/tracing/2900/runtime/moonbase/tests/runtime_apis.rs new file mode 100644 index 00000000..9ed29655 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/runtime_apis.rs @@ -0,0 +1,392 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbase Runtime Api Integration Tests + +mod common; +use common::*; + +use fp_evm::GenesisAccount; +use frame_support::assert_ok; +use nimbus_primitives::NimbusId; +use pallet_evm::{Account as EVMAccount, AddressMapping, FeeCalculator}; +use sp_core::{ByteArray, H160, H256, U256}; + +use fp_rpc::runtime_decl_for_ethereum_runtime_rpc_api::EthereumRuntimeRPCApi; +use moonbeam_rpc_primitives_txpool::runtime_decl_for_tx_pool_runtime_api::TxPoolRuntimeApi; +use nimbus_primitives::runtime_decl_for_nimbus_api::NimbusApi; +use std::{collections::BTreeMap, str::FromStr}; + +#[test] +fn ethereum_runtime_rpc_api_chain_id() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Runtime::chain_id(), CHAIN_ID); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_basic() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * UNIT)]) + .build() + .execute_with(|| { + assert_eq!( + Runtime::account_basic(H160::from(ALICE)), + EVMAccount { + balance: U256::from(2_000 * UNIT), + nonce: U256::zero() + } + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_gas_price() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!( + Runtime::gas_price(), + TransactionPaymentAsGasPrice::min_gas_price().0 + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_code_at() { + let address = H160::from(EVM_CONTRACT); + let code: Vec = vec![1, 2, 3, 4, 5]; + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: code.clone(), + nonce: Default::default(), + storage: Default::default(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::account_code_at(address), code); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_author() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .build() + .execute_with(|| { + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + assert_eq!(Runtime::author(), H160::from(ALICE)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_storage_at() { + let address = H160::from(EVM_CONTRACT); + let mut key = [0u8; 32]; + key[31..32].copy_from_slice(&[6u8][..]); + let mut value = [0u8; 32]; + value[31..32].copy_from_slice(&[7u8][..]); + let item = H256::from_slice(&key[..]); + let mut storage: BTreeMap = BTreeMap::new(); + storage.insert(H256::from_slice(&key[..]), item); + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: Vec::new(), + nonce: Default::default(), + storage: storage.clone(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::storage_at(address, U256::from(6)), item); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_call() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 2_000 * UNIT), + ]) + .build() + .execute_with(|| { + let execution_result = Runtime::call( + H160::from(ALICE), // from + H160::from(BOB), // to + Vec::new(), // data + U256::from(1000u64), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_create() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * UNIT)]) + .build() + .execute_with(|| { + let execution_result = Runtime::create( + H160::from(ALICE), // from + vec![0, 1, 1, 0], // data + U256::zero(), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_transaction_statuses() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 2_000 * UNIT), + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .build() + .execute_with(|| { + let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)); + rpc_run_to_block(2); + let statuses = + Runtime::current_transaction_statuses().expect("Transaction statuses result."); + assert_eq!(statuses.len(), 1); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_block() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .build() + .execute_with(|| { + rpc_run_to_block(2); + let block = Runtime::current_block().expect("Block result."); + assert_eq!(block.header.number, U256::from(1u8)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_receipts() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 2_000 * UNIT), + (AccountId::from(ALICE), 2_000 * UNIT), + (AccountId::from(BOB), 1_000 * UNIT), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * UNIT, + )]) + .build() + .execute_with(|| { + let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)); + rpc_run_to_block(2); + let receipts = Runtime::current_receipts().expect("Receipts result."); + assert_eq!(receipts.len(), 1); + }); +} + +#[test] +fn txpool_runtime_api_extrinsic_filter() { + ExtBuilder::default().build().execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * UNIT, + } + .into(), + ); + + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let txpool = >::extrinsic_filter( + vec![eth_uxt.clone(), non_eth_uxt.clone()], + vec![unchecked_eth_tx(VALID_ETH_TX), non_eth_uxt], + ); + assert_eq!(txpool.ready.len(), 1); + assert_eq!(txpool.future.len(), 1); + }); +} + +#[test] +fn can_author_when_selected_is_empty() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 20_000_000 * UNIT), + (AccountId::from(BOB), 10_000_000 * UNIT), + ]) + .with_collators(vec![(AccountId::from(ALICE), 2_000_000 * UNIT)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }; + + // Base case: ALICE can author blocks when she is the only candidate + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Remove ALICE from candidate pool, leaving the candidate_pool empty + assert_ok!(ParachainStaking::go_offline(origin_of(AccountId::from( + ALICE + )))); + + // Need to fast forward to right before the next session, which is when selected candidates + // will be updated. We want to test the creation of the first block of the next session. + run_to_block(1799, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1799, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Check that it works as expected after session update + run_to_block(1800, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1800, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + }); +} + +// Some Priority-related test ideas +// 1. Eth balance transfer with various gas prices. Priority == gas price +// 2. Eth contract call with various gas prices. Priority == gas price +// 3. System remark with no tip -> calculate expected priority from gas weight mapping +// 4. System remark with tip. +// 5. Operational dispatch has higher priority than normal for otherwise same transactions diff --git a/tracing/2900/runtime/moonbase/tests/xcm_mock/mod.rs b/tracing/2900/runtime/moonbase/tests/xcm_mock/mod.rs new file mode 100644 index 00000000..f556bc47 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/xcm_mock/mod.rs @@ -0,0 +1,272 @@ +// Copyright 2021 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 . + +pub mod parachain; +pub mod relay_chain; +pub mod statemint_like; +use cumulus_primitives_core::ParaId; +use pallet_xcm_transactor::relay_indices::*; +use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{AccountId32, BuildStorage}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +use polkadot_runtime_parachains::configuration::{ + GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, +}; +use polkadot_runtime_parachains::paras::{ + GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, +}; + +use sp_core::{H160, U256}; +use std::{collections::BTreeMap, str::FromStr}; + +pub const PARAALICE: [u8; 20] = [1u8; 20]; +pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); + +pub fn para_a_account() -> AccountId32 { + ParaId::from(1).into_account_truncating() +} + +pub fn para_b_account() -> AccountId32 { + ParaId::from(2).into_account_truncating() +} + +pub fn para_a_account_20() -> parachain::AccountId { + ParaId::from(1).into_account_truncating() +} + +pub fn evm_account() -> H160 { + H160::from_str("1000000000000000000000000000000000000001").unwrap() +} + +pub fn mock_para_genesis_info() -> ParaGenesisArgs { + ParaGenesisArgs { + genesis_head: vec![1u8].into(), + validation_code: vec![1u8].into(), + para_kind: ParaKind::Parachain, + } +} + +pub fn mock_relay_config() -> HostConfiguration { + HostConfiguration:: { + hrmp_channel_max_capacity: u32::MAX, + hrmp_channel_max_total_size: u32::MAX, + hrmp_max_parachain_inbound_channels: 10, + hrmp_max_parachain_outbound_channels: 10, + hrmp_channel_max_message_size: u32::MAX, + // Changed to avoid aritmetic errors within hrmp_close + max_downward_message_size: 100_000u32, + ..Default::default() + } +} + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_parachain! { + pub struct ParaC { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(3), + } +} + +decl_test_parachain! { + pub struct Statemint { + Runtime = statemint_like::Runtime, + XcmpMessageHandler = statemint_like::MsgQueue, + DmpMessageHandler = statemint_like::MsgQueue, + new_ext = statemint_ext(4), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(vec![1, 2, 3, 4]), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + (3, ParaC), + (4, Statemint), + ], + } +} + +pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; + +pub const INITIAL_EVM_BALANCE: u128 = 0; +pub const INITIAL_EVM_NONCE: u32 = 1; + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(PARAALICE.into(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm_transactor::GenesisConfig:: { + // match relay runtime construct_runtime order in xcm_mock::relay_chain + relay_indices: RelayChainIndices { + hrmp: 6u8, + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + ..Default::default() + }, + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // EVM accounts are self-sufficient. + let mut evm_accounts = BTreeMap::new(); + evm_accounts.insert( + evm_account(), + fp_evm::GenesisAccount { + nonce: U256::from(INITIAL_EVM_NONCE), + balance: U256::from(INITIAL_EVM_BALANCE), + storage: Default::default(), + code: vec![ + 0x00, // STOP + ], + }, + ); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn statemint_ext(para_id: u32) -> sp_io::TestExternalities { + use statemint_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (RELAYALICE.into(), INITIAL_BALANCE), + (RELAYBOB.into(), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext(paras: Vec) -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(RELAYALICE, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras + .iter() + .map(|¶_id| (para_id.into(), mock_para_genesis_info())) + .collect(); + + let genesis_config = ConfigurationGenesisConfig:: { + config: mock_relay_config(), + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = ParasGenesisConfig:: { + paras: para_genesis, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type Hrmp = polkadot_runtime_parachains::hrmp::Pallet; + +pub type StatemintBalances = pallet_balances::Pallet; +pub type StatemintChainPalletXcm = pallet_xcm::Pallet; +pub type StatemintAssets = pallet_assets::Pallet; + +pub type ParachainPalletXcm = pallet_xcm::Pallet; +pub type Assets = pallet_assets::Pallet; + +pub type Treasury = pallet_treasury::Pallet; +pub type AssetManager = pallet_asset_manager::Pallet; +pub type XTokens = orml_xtokens::Pallet; +pub type RelayBalances = pallet_balances::Pallet; +pub type ParaBalances = pallet_balances::Pallet; +pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/tracing/2900/runtime/moonbase/tests/xcm_mock/parachain.rs b/tracing/2900/runtime/moonbase/tests/xcm_mock/parachain.rs new file mode 100644 index 00000000..20c6d4d4 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/xcm_mock/parachain.rs @@ -0,0 +1,1129 @@ +// Copyright 2021 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 . + +//! Parachain runtime mock. + +use frame_support::{ + construct_runtime, + dispatch::GetDispatchInfo, + ensure, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU32, Everything, Get, InstanceFilter, Nothing, PalletInfoAccess, + }, + weights::Weight, + PalletId, +}; + +use frame_system::{pallet_prelude::BlockNumberFor, EnsureNever, EnsureRoot}; +use pallet_xcm::migration::v1::VersionUncheckedMigrateToV1; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, + Permill, +}; +use sp_std::{convert::TryFrom, prelude::*}; +use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; + +use cumulus_primitives_core::relay_chain::HrmpChannelId; +use orml_traits::parameter_type_with_key; +use pallet_ethereum::PostLogContent; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use xcm::latest::{ + AssetId as XcmAssetId, Error as XcmError, ExecuteXcm, + Junction::{PalletInstance, Parachain}, + Location, NetworkId, Outcome, Xcm, +}; +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, + FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, + NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; + +pub use moonbase_runtime::xcm_config::AssetType; +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; +use scale_info::TypeInfo; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; + +pub type AccountId = moonbeam_core_primitives::AccountId; +pub type Balance = u128; +pub type AssetId = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 0; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +pub type ForeignAssetInstance = (); +pub type LocalAssetInstance = pallet_assets::Instance1; + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 10; // Does not really matter as this will be only called by root + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + AccountKey20Aliases, + // The rest of multilocations convert via hashing it + xcm_builder::HashedDescription< + AccountId, + xcm_builder::DescribeFamily, + >, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + SignedAccountKey20AsNative, +); + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); + pub MaxInstructions: u32 = 100; +} + +// Instructing how incoming xcm assets will be handled +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId32 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +/// The transactor for our own chain currency. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + IsConcrete, + // We can convert the Locations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// These will be our transactors +// We use both transactors +pub type AssetTransactors = (LocalAssetTransactor, ForeignFungiblesTransactor); + +pub type XcmRouter = super::ParachainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +pub type XcmFeesToAccount_ = xcm_primitives::XcmFeesToAccount< + Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +parameter_types! { + // We cannot skip the native trader for some specific tests, so we will have to work with + // a native trader that charges same number of units as weight + // We use both the old and new anchoring logics + pub ParaTokensPerSecond: (XcmAssetId, u128, u128) = ( + AssetId(SelfReserve::get()), + 1000000000000, + 0, + ); +} + +parameter_types! { + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + + // New Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a Location: (Self Balances pallet index) + pub SelfReserve: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +use frame_system::RawOrigin; +use sp_runtime::traits::PostDispatchInfoOf; +use sp_runtime::DispatchErrorWithPostInfo; +use xcm_executor::traits::CallDispatcher; +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + // We use three traders + // When we receive either representation of the self-reserve asset, + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + FixedRateOfFungible, + xcm_primitives::FirstAssetTrader, + ); + + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + SelfReserve, + ForeignAsset(AssetId), +} + +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + CurrencyId::SelfReserve => { + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxAssetsForTransfer: usize = 2; + pub SelfLocation: Location = Location::here(); + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(MsgQueue::parachain_id().into()) + ].into() + }; +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + (1, Some(Parachain(4u32))) => Some(50u128), + _ => None, + } + }; +} + +// The XCM message wrapper wrapper +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdConvert = + CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: Balance = 0; + pub const SpendPeriod: u32 = 0; + pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); + pub const MaxApprovals: u32 = 100; + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + type ApproveOrigin = EnsureRoot; + type RejectOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = MaxApprovals; + type WeightInfo = (); + type SpendFunds = (); + type ProposalBondMaximum = (); + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<0>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ArgumentsBenchmarkHelper; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} + +// Pallet to provide the version, used to test runtime upgrade version changes +#[frame_support::pallet] +pub mod mock_version_changer { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_version)] + pub(super) type CurrentVersion = StorageValue<_, XcmVersion, ValueQuery>; + + impl Get for Pallet { + fn get() -> XcmVersion { + Self::current_version() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + VersionChanged(XcmVersion), + } + + impl Pallet { + pub fn set_version(version: XcmVersion) { + CurrentVersion::::put(version); + Self::deposit_event(Event::VersionChanged(version)); + } + } +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl mock_version_changer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +pub type LocalOriginToLocation = + xcm_primitives::SignedToAccountId20; + +parameter_types! { + pub MatcherLocation: Location = Location::here(); +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = frame_support::traits::Nothing; + type XcmExecutor = XcmExecutor; + // Do not allow teleports + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // We use a custom one to test runtime ugprades + type AdvertisedXcmVersion = XcmVersioner; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::pallet_prelude::DispatchResult; +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset, + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset, + metadata.name, + metadata.symbol, + metadata.decimals, + false, + ) + } + + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into())?; + + Ok(()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, +} + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetMetadata; + type ForeignAssetType = AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = EnsureRoot; + type WeightInfo = (); +} + +// 1 ROC/WND should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = MockTransactors; + type DerivativeAddressRegistrationOrigin = EnsureRoot; + type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdToLocation = + CurrencyIdToLocation>; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type XcmSender = XcmRouter; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; + type WeightInfo = (); + type HrmpManipulatorOrigin = EnsureRoot; + type HrmpOpenOrigin = EnsureRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 1000; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +use sp_core::U256; + +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; +/// Block storage limit in bytes. Set to 40 KB. +const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(u64::MAX); + pub WeightPerGas: Weight = Weight::from_parts(1, 0); + pub GasLimitPovSizeRatio: u64 = { + let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); + block_gas_limit.saturating_div(MAX_POV_SIZE) + }; + pub GasLimitStorageGrowthRatio: u64 = + BlockGasLimit::get().min(u64::MAX.into()).low_u64().saturating_div(BLOCK_STORAGE_LIMIT); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + + type CallOrigin = pallet_evm::EnsureAddressRoot; + type WithdrawOrigin = pallet_evm::EnsureAddressNever; + + type AddressMapping = pallet_evm::IdentityAddressMapping; + type Currency = Balances; + type Runner = pallet_evm::runner::stack::Runner; + + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = (); + type PrecompilesValue = (); + type ChainId = (); + type BlockGasLimit = BlockGasLimit; + type OnChargeTransaction = (); + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = Timestamp; + type WeightInfo = pallet_evm::weights::SubstrateWeight; +} + +pub struct NormalFilter; +impl frame_support::traits::Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + _ => true, + } + } +} + +// We need to use the encoding from the relay mock runtime +#[derive(Encode, Decode)] +pub enum RelayCall { + #[codec(index = 5u8)] + // the index should match the position of the module in `construct_runtime!` + Utility(UtilityCall), + #[codec(index = 6u8)] + // the index should match the position of the module in `construct_runtime!` + Hrmp(HrmpCall), +} + +#[derive(Encode, Decode)] +pub enum UtilityCall { + #[codec(index = 1u8)] + AsDerivative(u16), +} + +// HRMP call encoding, needed for xcm transactor pallet +#[derive(Encode, Decode)] +pub enum HrmpCall { + #[codec(index = 0u8)] + InitOpenChannel(ParaId, u32, u32), + #[codec(index = 1u8)] + AcceptOpenChannel(ParaId), + #[codec(index = 2u8)] + CloseChannel(HrmpChannelId), + #[codec(index = 6u8)] + CancelOpenRequest(HrmpChannelId, u32), +} + +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum MockTransactors { + Relay, +} + +impl xcm_primitives::XcmTransact for MockTransactors { + fn destination(self) -> Location { + match self { + MockTransactors::Relay => Location::parent(), + } + } +} + +impl xcm_primitives::UtilityEncodeCall for MockTransactors { + fn encode_call(self, call: xcm_primitives::UtilityAvailableCalls) -> Vec { + match self { + MockTransactors::Relay => match call { + xcm_primitives::UtilityAvailableCalls::AsDerivative(a, b) => { + let mut call = + RelayCall::Utility(UtilityCall::AsDerivative(a.clone())).encode(); + call.append(&mut b.clone()); + call + } + }, + } + } +} + +pub struct MockHrmpEncoder; +impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { + fn hrmp_encode_call( + call: xcm_primitives::HrmpAvailableCalls, + ) -> Result, xcm::latest::Error> { + match call { + xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( + HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), + ) + .encode()), + xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { + Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) + } + } + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} +parameter_types! { + pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); +} + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +pub enum ProxyType { + NotAllowed = 0, + Any = 1, +} + +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} + +impl InstanceFilter for ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + match self { + ProxyType::NotAllowed => false, + ProxyType::Any => true, + } + } + fn is_superset(&self, _o: &Self) -> bool { + false + } +} + +impl Default for ProxyType { + fn default() -> Self { + Self::NotAllowed + } +} + +parameter_types! { + pub const ProxyCost: u64 = 1; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyCost; + type ProxyDepositFactor = ProxyCost; + type MaxProxies = ConstU32<32>; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ProxyCost; + type AnnouncementDepositFactor = ProxyCost; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will iMmediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +type Block = frame_system::mocking::MockBlockU32; + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_msg_queue, + XcmVersioner: mock_version_changer, + + PolkadotXcm: pallet_xcm, + Assets: pallet_assets, + CumulusXcm: cumulus_pallet_xcm, + XTokens: orml_xtokens, + AssetManager: pallet_asset_manager, + XcmTransactor: pallet_xcm_transactor, + Treasury: pallet_treasury, + LocalAssets: pallet_assets::, + Proxy: pallet_proxy, + + Timestamp: pallet_timestamp, + EVM: pallet_evm, + Ethereum: pallet_ethereum, + EthereumXcm: pallet_ethereum_xcm, + } +); + +pub(crate) fn para_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::tokens::{PayFromAccount, UnityAssetBalanceConversion}; +use frame_support::traits::{OnFinalize, OnInitialize, OnRuntimeUpgrade}; +pub(crate) fn on_runtime_upgrade() { + VersionUncheckedMigrateToV1::::on_runtime_upgrade(); +} + +pub(crate) fn para_roll_to(n: BlockNumber) { + while System::block_number() < n { + PolkadotXcm::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + PolkadotXcm::on_initialize(System::block_number()); + } +} diff --git a/tracing/2900/runtime/moonbase/tests/xcm_mock/relay_chain.rs b/tracing/2900/runtime/moonbase/tests/xcm_mock/relay_chain.rs new file mode 100644 index 00000000..40bff5e1 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/xcm_mock/relay_chain.rs @@ -0,0 +1,412 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, IdentityLookup}, + AccountId32, +}; + +use frame_support::weights::{Weight, WeightMeter}; +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{ + configuration, dmp, hrmp, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, paras, shared, +}; +use sp_runtime::transaction_validity::TransactionPriority; +use sp_runtime::Permill; +use xcm::latest::prelude::*; +use xcm_builder::{ + Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, + FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + WithComputedOrigin, +}; +use xcm_executor::{Config, XcmExecutor}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); + type PalletsOrigin = OriginCaller; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub KsmLocation: Location = Here.into(); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: Option = None; + pub UniversalLocation: InteriorLocation = Here; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, + // Not enabled in the relay per se, but we enable it to test + // the transact_through_signed extrinsic + Account32Hash, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); + pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub MatcherLocation: Location = Location::here(); +} + +pub type XcmRouter = super::RelayChainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); + pub Statemine: Location = Parachain(4).into(); + pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); +} + +pub type TrustedTeleporters = xcm_builder::Case; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + +/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this +/// is more to satisfy type requirements rather than to test anything. +pub struct TestNextSessionRotation; + +impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { + fn average_session_length() -> u32 { + 10 + } + + fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } + + fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } +} + +impl paras::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = TestNextSessionRotation; + type QueueFootprinter = (); + type OnNewHead = (); + type AssignCoretime = (); +} + +impl dmp::Config for Runtime {} + +impl hrmp::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type WeightInfo = TestHrmpWeightInfo; + type ChannelManager = frame_system::EnsureRoot; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlockU32; + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + ProcessXcmMessage::, RuntimeCall>::process_message( + message, + Junction::Parachain(para.into()), + meter, + id, + ) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); + type QueuePausedQuery = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + MessageQueue: pallet_message_queue, + XcmPallet: pallet_xcm, + Utility: pallet_utility, + Hrmp: hrmp, + Dmp: dmp, + Paras: paras, + Configuration: configuration, + } +); + +pub(crate) fn relay_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::{OnFinalize, OnInitialize}; +pub(crate) fn relay_roll_to(n: BlockNumber) { + while System::block_number() < n { + XcmPallet::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + XcmPallet::on_initialize(System::block_number()); + } +} + +/// A weight info that is only suitable for testing. +pub struct TestHrmpWeightInfo; + +impl hrmp::WeightInfo for TestHrmpWeightInfo { + fn hrmp_accept_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn force_clean_hrmp(_: u32, _: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_close(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_open(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_cancel_open_request(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_close_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_init_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn clean_open_channel_requests(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_open_hrmp_channel(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn establish_system_channel() -> Weight { + Weight::from_parts(1, 0) + } + + fn poke_channel_deposits() -> Weight { + Weight::from_parts(1, 0) + } +} diff --git a/tracing/2900/runtime/moonbase/tests/xcm_mock/statemint_like.rs b/tracing/2900/runtime/moonbase/tests/xcm_mock/statemint_like.rs new file mode 100644 index 00000000..a3da2c21 --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/xcm_mock/statemint_like.rs @@ -0,0 +1,575 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + weights::Weight, +}; +use frame_system::{EnsureRoot, EnsureSigned}; + +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, Hash, IdentityLookup}, + AccountId32, +}; + +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_parachain::primitives::Sibling; +use sp_std::convert::TryFrom; +use xcm::latest::prelude::*; +use xcm::VersionedXcm; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type AssetId = u128; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) + // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const ExecutiveBody: BodyId = BodyId::Executive; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub Local: Location = Here.into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = + (AssetId(KsmLocation::get()), 1, 1); +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// Means for transacting assets besides the native currency on this chain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ConvertedConcreteId< + AssetId, + Balance, + AsPrefixedGeneralIndex, + JustTry, + >, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only want to allow teleports of known assets. We use non-zero issuance as an indication + // that this asset is known. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `RuntimeOrigin::signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxInstructions: u32 = 100; +} + +pub struct ParentOrParentsExecutivePlurality; +impl Contains for ParentOrParentsExecutivePlurality { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (1, []) + | ( + 1, + [Plurality { + id: BodyId::Executive, + .. + }] + ) + ) + } +} + +pub struct ParentOrSiblings; +impl Contains for ParentOrSiblings { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (1, []) | (1, [_])) + } +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution + AllowUnpaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, +); + +parameter_types! { + pub MatcherLocation: Location = Location::here(); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = + orml_xcm_support::MultiNativeAsset; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +pub type XcmRouter = super::ParachainXcmRouter; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 +#[frame_support::pallet] +pub mod mock_statemint_prefix { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_prefix)] + pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; + + impl Get for Pallet { + fn get() -> Location { + Self::current_prefix() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // Changed Prefix + PrefixChanged(Location), + } + + impl Pallet { + pub fn set_prefix(prefix: Location) { + CurrentPrefix::::put(&prefix); + Self::deposit_event(Event::PrefixChanged(prefix)); + } + } +} + +impl mock_statemint_prefix::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +type Block = frame_system::mocking::MockBlockU32; +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + PolkadotXcm: pallet_xcm, + CumulusXcm: cumulus_pallet_xcm, + MsgQueue: mock_msg_queue, + Assets: pallet_assets, + PrefixChanger: mock_statemint_prefix, + + } +); diff --git a/tracing/2900/runtime/moonbase/tests/xcm_tests.rs b/tracing/2900/runtime/moonbase/tests/xcm_tests.rs new file mode 100644 index 00000000..f11ebf2c --- /dev/null +++ b/tracing/2900/runtime/moonbase/tests/xcm_tests.rs @@ -0,0 +1,3970 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbase Runtime Xcm Tests + +mod xcm_mock; +use frame_support::{ + assert_ok, + traits::{ConstU32, PalletInfo, PalletInfoAccess}, + weights::constants::WEIGHT_REF_TIME_PER_SECOND, + weights::Weight, + BoundedVec, +}; +use pallet_xcm_transactor::{ + Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, +}; +use sp_runtime::traits::MaybeEquivalence; +use sp_std::boxed::Box; +use xcm::latest::prelude::{ + AccountId32, AccountKey20, All, BuyExecution, ClearOrigin, DepositAsset, GeneralIndex, + Junction, Junctions, Limited, Location, OriginKind, PalletInstance, Parachain, QueryResponse, + Reanchorable, Response, WeightLimit, WithdrawAsset, Xcm, +}; +use xcm::{VersionedLocation, WrapVersion}; +use xcm_executor::traits::ConvertLocation; +use xcm_mock::*; +use xcm_primitives::{UtilityEncodeCall, DEFAULT_PROOF_SIZE}; +use xcm_simulator::TestExt; +mod common; +use cumulus_primitives_core::relay_chain::HrmpChannelId; +// Send a relay asset (like DOT) to a parachain A +#[test] +fn receive_relay_asset_from_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + // Verify that parachain received the asset + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +// Send relay asset (like DOT) back from Parachain A to relaychain +#[test] +fn send_relay_asset_to_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register relay asset in paraA + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // Free execution + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // First send relay chain asset to Parachain like in previous test + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Lets gather the balance before sending back money + let mut balance_before_sending = 0; + Relay::execute_with(|| { + balance_before_sending = RelayBalances::free_balance(&RELAYALICE); + }); + + // We now send back some money to the relay + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: RELAYALICE.into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 123, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The balances in paraAlice should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); + + // Balances in the relay should have been received + Relay::execute_with(|| { + // Free execution,x full amount received + assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); + }); +} + +#[test] +fn send_relay_asset_to_para_b() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register asset in paraA. Free execution + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // First send relay chain asset to Parachain A like in previous test + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Now send relay asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 23); + }); + + // Para B balances should have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_to_para_b() { + MockNet::reset(); + + // this represents the asset in paraA + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Native token is substracted in paraA + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Asset is minted in paraB + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_from_para_b_to_para_c() { + MockNet::reset(); + + // Represents para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in parachain B. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register para A asset in parachain C. Free execution + ParaC::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send para A asset from para B to para C + let dest = Location { + parents: 1, + interior: [ + Parachain(3), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The message passed through parachainA so we needed to pay since its the native token + ParaC::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 96); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a() { + MockNet::reset(); + + // Para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in para B + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send back para A asset to para A + let dest = Location { + parents: 1, + interior: [ + Parachain(1), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a_with_new_reanchoring() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A asset has been credited + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // This time we will force the new reanchoring by manually sending the + // Message through polkadotXCM pallet + + let dest = Location { + parents: 1, + interior: [Parachain(1)].into(), + }; + + let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); + + let message = xcm::VersionedXcm::<()>::V4(Xcm(vec![ + WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), + ClearOrigin, + BuyExecution { + fees: (reanchored_para_a_balances, 100).into(), + weight_limit: Limited(80.into()), + }, + DepositAsset { + assets: All.into(), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: PARAALICE, + }], + ), + }, + ])); + ParaB::execute_with(|| { + // Send a message to the sovereign account in ParaA to withdraw + // and deposit asset + assert_ok!(ParachainPalletXcm::send( + parachain::RuntimeOrigin::root(), + Box::new(dest.into()), + Box::new(message), + )); + }); + + ParaA::execute_with(|| { + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn receive_relay_asset_with_trader() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // We are sending 100 tokens from relay. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Therefore with no refund, we should receive 10 tokens less + // Native trader fails for this, and we use the asset trader + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 100).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // non-free execution, not full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // In destination chain, we only need 4 weight + // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // We are sending 100 tokens from para A. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Since we set 10 weight in destination chain, 25 will be charged upfront + // 15 of those will be refunded, while 10 will go to treasury as the true weight used + // will be 4 + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader_and_fee() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // With these units per second, 80K weight convrets to 1 asset unit + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 12500000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // we use transfer_with_fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // 100 tokens transferred plus 1 taken from fees + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 - 1 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received because trully the xcm instruction does not cost + // what it is specified + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 101); + }); +} + +#[test] +fn error_when_not_paying_enough() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + // We are sending 100 tokens from relay. + // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 + // Therefore with no refund, we should receive 10 tokens less + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 5).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // amount not received as it is not paying enough + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); +} + +#[test] +fn transact_through_derivative_multilocation() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000003000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + false + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn transact_through_sovereign() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000003000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000009000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A and set XCM version to 1 + ParaA::execute_with(|| { + parachain::XcmVersioner::set_version(1); + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let response = Response::Version(2); + let querier: Location = [].into(); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: Weight::zero(), + querier: Some(querier), + }]); + // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force + // it directly here + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + Relay::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(RelayChainPalletXcm::force_default_xcm_version( + relay_chain::RuntimeOrigin::root(), + Some(2) + )); + + // Wrap version, which sets VersionedStorage + // This is necessary because the mock router does not use wrap_version, but + // this is not necessary in prod + assert_ok!(::wrap_version( + &Parachain(1).into(), + mock_message + )); + + // Transfer assets. Since it is an unknown destination, it will query for version + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 123).into()), + 0, + )); + + // Let's advance the relay. This should trigger the subscription message + relay_chain::relay_roll_to(2); + + // queries should have been updated + assert!(RelayChainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 1, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the version change + assert!(relay_chain::relay_events().contains(&expected_supported_version)); + }); + + // ParaA changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaA::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().iter().any(|e| matches!( + e, + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { + result: 2, + .. + }) + ))); + }); + + // This event should have been seen in the relay + let expected_supported_version_2: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 2, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(relay_chain::relay_events().contains(&expected_supported_version_2)); + }); +} + +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_para_b() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + let response = Response::Version(2); + let querier: Location = [].into(); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: Weight::zero(), + querier: Some(querier), + }]); + + ParaA::execute_with(|| { + // advertised version + parachain::XcmVersioner::set_version(2); + }); + + ParaB::execute_with(|| { + // Let's try with v0 + parachain::XcmVersioner::set_version(0); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + ParaA::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(ParachainPalletXcm::force_default_xcm_version( + parachain::RuntimeOrigin::root(), + Some(2) + )); + // Wrap version, which sets VersionedStorage + assert_ok!(::wrap_version( + &Location::new(1, [Parachain(2)]).into(), + mock_message + )); + + parachain::para_roll_to(2); + + // queries should have been updated + assert!(ParachainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: parachain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 1, + interior: [Parachain(2)].into(), + }, + version: 0, + } + .into(); + + ParaA::execute_with(|| { + // Assert that the events vector contains the version change + assert!(parachain::para_events().contains(&expected_supported_version)); + }); + + // Let's ensure talking in v0 works + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + } + .into(); + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // ParaB changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaB::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().iter().any(|e| matches!( + e, + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { + result: 2, + .. + }) + ))); + }); + + // This event should have been seen in para A + let expected_supported_version_2: parachain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 1, + interior: [Parachain(2)].into(), + }, + version: 2, + } + .into(); + + // Para A should have received the version change + ParaA::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(parachain::para_events().contains(&expected_supported_version_2)); + }); +} + +#[test] +fn receive_asset_with_no_sufficients_not_possible_if_non_existent_account() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + // parachain should not have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 0); + }); + + // Send native token to fresh_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + fresh_account.into(), + 100 + )); + }); + + // Re-send tokens + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { + MockNet::reset(); + + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + // Evm account is self sufficient + ParaA::execute_with(|| { + assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + // Evm account sufficient ref count increased by 1. + ParaA::execute_with(|| { + // TODO: since the suicided logic was introduced the data of the smart contract is not + // removed, it will have to be updated in a future release when there is the ability to + // remove contract data + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); + }); + + ParaA::execute_with(|| { + // Remove the account from the evm context. + parachain::EVM::remove_account(&evm_account()); + // Evm account sufficient ref count decreased by 1. + // TODO: since the suicided logic was introduced the data of the smart contract is not + // removed, it will have to be updated in a future release when there is the ability to + // remove contract data + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); +} + +#[test] +fn empty_account_should_not_be_reset() { + MockNet::reset(); + + // Test account has nonce 1 on genesis. + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send native token to evm_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + evm_account_id, + 100 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([], 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Empty the assets from the account. + // As this makes the account go below the `min_balance`, the account is considered dead + // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. + assert_ok!(parachain::Assets::transfer( + parachain::RuntimeOrigin::signed(evm_account_id), + source_id, + PARAALICE.into(), + 123 + )); + // Verify account asset balance is Zero. + assert_eq!( + parachain::Assets::balance(source_id, &evm_account_id.into()), + 0 + ); + // Because we no longer have consumer references, we can set the balance to Zero. + // This would reset the account if our ED were to be > than Zero. + assert_ok!(ParaBalances::force_set_balance( + parachain::RuntimeOrigin::root(), + evm_account_id, + 0, + )); + // Verify account native balance is Zero. + assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); + // Remove the account from the evm context. + // This decreases the sufficients reference by 1 and now is Zero. + parachain::EVM::remove_account(&evm_account()); + // Verify reference count. + let account = parachain::System::account(evm_account_id); + // TODO: after introducing the suicided fix the value for account.sufficients will remain 1 + // until the storage is not completely removed, it will have to be decreased to 0 once the + // storage can be fully removed + assert_eq!(account.sufficients, 1); + assert_eq!(account.consumers, 0); + assert_eq!(account.providers, 1); + // We expect the account to be alive in a Zero ED context. + // TODO: after introducing the suicided fix the nonce is increased by 1 + // until the storage is not completely removed, it will have to be decreased to 1 once the + // storage can be fully removed + assert_eq!(parachain::System::account_nonce(evm_account_id), 2); + }); +} + +#[test] +fn test_statemint_like() { + MockNet::reset(); + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + let statemint_asset_a_balances = Location::new( + 1, + [ + Parachain(4), + PalletInstance(5), + xcm::latest::prelude::GeneralIndex(0u128), + ], + ); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemint_asset_a_balances) + .expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"StatemintToken".to_vec(), + symbol: b"StatemintToken".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + Statemint::execute_with(|| { + // Set new prefix + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 300000000000000 + )); + + // This is needed, since the asset is created as non-sufficient + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send asset with previous prefix + assert_ok!(StatemintChainPalletXcm::reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + xcm::latest::prelude::GeneralIndex(0), + ], + 123 + ) + .into() + ), + 0, + )); + }); + + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +#[test] +fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send relay chain asset to Alice in Parachain A + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_relay) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + )); + }); + + Statemint::execute_with(|| { + // Set new prefix + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Send some native statemint tokens to sovereign for fees. + // We can't pay fees with USDC as the asset is minted as non-sufficient. + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Send statemint USDC asset to Alice in Parachain A + let parachain_beneficiary_from_statemint: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send with new prefix + assert_ok!(StatemintChainPalletXcm::reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_statemint) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + )); + }); + + let statemint_beneficiary = Location { + parents: 1, + interior: [ + Parachain(4), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + + // Alice has received 200 Relay assets + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + Statemint::execute_with(|| { + // Check that BOB's balance is empty before the transfer + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![]); + }); + + // Transfer USDC from Parachain A to Statemint using Relay asset as fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(statemint_beneficiary)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + }); + + ParaA::execute_with(|| { + // Alice has 100 USDC less + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 25 + ); + + // Alice has 100 relay asset less + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that BOB received 100 USDC on statemint + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); +} + +#[test] +fn transact_through_signed_multilocation() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4000.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000004000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // 100 transferred + assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); + + // 4000005186 refunded + assert_eq!(RelayBalances::free_balance(&derived), 4000005186); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_multilocation + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + assert!(ParaBalances::free_balance(&derived) == 0); + + assert!(ParaBalances::free_balance(¶_a_account_20()) == 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_multilocation + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: Some(overall_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + }); + + ParaB::execute_with(|| { + // Check the derived account was refunded + assert_eq!(ParaBalances::free_balance(&derived), 8993); + + // Check the transfer was executed + assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_multilocation + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact { + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer went through + assert!( + ParaBalances::free_balance(&PARAALICE.into()) + == parachain_b_alice_balances_before + 100 + ); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_multilocation + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer wasn't executed + assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_multilocation + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + let transfer_recipient = evm_account(); + let mut transfer_recipient_balance_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); + + // Add proxy ALICE -> derived + let _ = parachain::Proxy::add_proxy_delegate( + &PARAALICE.into(), + derived, + parachain::ProxyType::Any, + 0, + ); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer was executed + assert!( + ParaBalances::free_balance(&transfer_recipient.into()) + == transfer_recipient_balance_before + 100 + ); + }); +} + +#[test] +fn hrmp_init_accept_through_root() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_b_account(), + 1000u128 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp init channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: 2u32.into(), + proposed_max_capacity: 1, + proposed_max_message_size: 1 + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { + sender: 1u32.into(), + recipient: 2u32.into(), + proposed_max_capacity: 1u32, + proposed_max_message_size: 1u32, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); + ParaB::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp accept channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: 1u32.into() + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { + sender: 1u32.into(), + recipient: 2u32.into(), + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +#[test] +fn hrmp_close_works() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(Hrmp::force_open_hrmp_channel( + relay_chain::RuntimeOrigin::root(), + 1u32.into(), + 2u32.into(), + 1u32, + 1u32 + )); + assert_ok!(Hrmp::force_process_hrmp_open( + relay_chain::RuntimeOrigin::root(), + 1u32 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Close(HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into() + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { + by_parachain: 1u32.into(), + channel_id: HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into(), + }, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +use parity_scale_codec::{Decode, Encode}; +use sp_io::hashing::blake2_256; + +// Helper to derive accountIds +pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { + let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); + sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") +} diff --git a/tracing/2900/runtime/moonbeam/Cargo.toml b/tracing/2900/runtime/moonbeam/Cargo.toml new file mode 100644 index 00000000..96cb372b --- /dev/null +++ b/tracing/2900/runtime/moonbeam/Cargo.toml @@ -0,0 +1,401 @@ +[package] +authors = { workspace = true } +build = "build.rs" +description = "Moonbeam Runtime" +edition = "2021" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +name = "moonbeam-runtime" +version = "0.8.4" + +[dependencies] +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +num_enum = { workspace = true } +rlp = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +sha3 = { workspace = true, optional = true } +smallvec = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } + +# Moonbeam +account = { workspace = true } +moonbeam-core-primitives = { workspace = true } +moonbeam-relay-encoder = { workspace = true } +moonbeam-runtime-common = { workspace = true } +precompile-utils = { workspace = true } +session-keys-primitives = { workspace = true } +xcm-primitives = { workspace = true } + +# Moonbeam pallets +moonbeam-xcm-benchmarks = { workspace = true } +pallet-asset-manager = { workspace = true } +pallet-author-mapping = { workspace = true } +pallet-crowdloan-rewards = { workspace = true } +pallet-erc20-xcm-bridge = { workspace = true } +pallet-ethereum-xcm = { workspace = true } +pallet-evm-chain-id = { workspace = true } +pallet-maintenance-mode = { workspace = true, features = ["xcm-support"] } +pallet-migrations = { workspace = true } +pallet-moonbeam-lazy-migrations = { workspace = true } +pallet-moonbeam-orbiters = { workspace = true } +pallet-parachain-staking = { workspace = true } +pallet-precompile-benchmarks = { workspace = true } +pallet-proxy-genesis-companion = { workspace = true } +pallet-randomness = { workspace = true } +pallet-xcm-transactor = { workspace = true } + +# Moonbeam precompiles +pallet-evm-precompile-author-mapping = { workspace = true } +pallet-evm-precompile-balances-erc20 = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } +pallet-evm-precompile-call-permit = { workspace = true } +pallet-evm-precompile-collective = { workspace = true } +pallet-evm-precompile-conviction-voting = { workspace = true } +pallet-evm-precompile-crowdloan-rewards = { workspace = true } +pallet-evm-precompile-gmp = { workspace = true } +pallet-evm-precompile-identity = { workspace = true } +pallet-evm-precompile-parachain-staking = { workspace = true } +pallet-evm-precompile-preimage = { workspace = true } +pallet-evm-precompile-proxy = { workspace = true } +pallet-evm-precompile-randomness = { workspace = true } +pallet-evm-precompile-referenda = { workspace = true } +pallet-evm-precompile-registry = { workspace = true } +pallet-evm-precompile-relay-encoder = { workspace = true } +pallet-evm-precompile-relay-verifier = { workspace = true } +pallet-evm-precompile-xcm-transactor = { workspace = true } +pallet-evm-precompile-xcm-utils = { workspace = true } +pallet-evm-precompile-xtokens = { workspace = true } +pallet-evm-precompileset-assets-erc20 = { workspace = true } + +# Moonbeam tracing +evm-tracing-events = { workspace = true, optional = true } +moonbeam-evm-tracer = { workspace = true, optional = true } +moonbeam-rpc-primitives-debug = { workspace = true } +moonbeam-rpc-primitives-txpool = { workspace = true } + +# Substrate +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true, features = ["insecure_zero_ed"] } +pallet-collective = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-identity = { workspace = true } +pallet-multisig = { workspace = true } +pallet-preimage = { workspace = true } +pallet-proxy = { workspace = true } +pallet-referenda = { workspace = true } +pallet-root-testing = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-society = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } +pallet-treasury = { workspace = true } +pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } +parity-scale-codec = { workspace = true, features = [ + "derive", + "max-encoded-len", + "chain-error", +] } +scale-info = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-inherents = { workspace = true } +sp-io = { workspace = true, features = ["improved_panic_error_reporting"] } +sp-offchain = { workspace = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-version = { workspace = true } +sp-weights = { workspace = true } +sp-genesis-builder = { workspace = true } + +# Frontier +fp-evm = { workspace = true } +fp-rpc = { workspace = true } +fp-self-contained = { workspace = true, features = ["serde"] } +pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm-precompile-blake2 = { workspace = true } +pallet-evm-precompile-bn128 = { workspace = true } +pallet-evm-precompile-dispatch = { workspace = true } +pallet-evm-precompile-modexp = { workspace = true } +pallet-evm-precompile-sha3fips = { workspace = true } +pallet-evm-precompile-simple = { workspace = true } + +# Polkadot / XCM +orml-traits = { workspace = true } +orml-xcm-support = { workspace = true } +orml-xtokens = { workspace = true } +pallet-xcm = { workspace = true } +pallet-xcm-benchmarks = { workspace = true, optional = true } +pallet-message-queue = { workspace = true } +polkadot-core-primitives = { workspace = true } +polkadot-runtime-common = { workspace = true } +polkadot-parachain = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +# Cumulus +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +parachain-info = { workspace = true } +parachains-common = { workspace = true } + +# Moonkit +async-backing-primitives = { workspace = true } +moonkit-xcm-primitives = { workspace = true } +nimbus-primitives = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-author-slot-filter = { workspace = true } +pallet-relay-storage-roots = { workspace = true } + +# Benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } +frame-try-runtime = { workspace = true, optional = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +default = ["std", "evm-tracing"] +std = [ + "account/std", + "async-backing-primitives/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "evm-tracing-events/std", + "fp-evm/std", + "fp-rpc/std", + "fp-self-contained/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "moonbeam-core-primitives/std", + "moonbeam-evm-tracer/std", + "moonbeam-relay-encoder/std", + "moonbeam-rpc-primitives-debug/std", + "moonbeam-rpc-primitives-txpool/std", + "moonbeam-runtime-common/std", + "moonbeam-xcm-benchmarks/std", + "moonkit-xcm-primitives/std", + "nimbus-primitives/std", + "orml-xtokens/std", + "pallet-asset-manager/std", + "pallet-assets/std", + "pallet-author-inherent/std", + "pallet-author-mapping/std", + "pallet-author-slot-filter/std", + "pallet-balances/std", + "pallet-collective/std", + "pallet-conviction-voting/std", + "pallet-crowdloan-rewards/std", + "pallet-erc20-xcm-bridge/std", + "pallet-evm-chain-id/std", + "pallet-ethereum-xcm/std", + "pallet-ethereum/std", + "pallet-evm-precompile-author-mapping/std", + "pallet-evm-precompile-balances-erc20/std", + "pallet-evm-precompile-batch/std", + "pallet-evm-precompile-call-permit/std", + "pallet-evm-precompile-collective/std", + "pallet-evm-precompile-conviction-voting/std", + "pallet-evm-precompile-parachain-staking/std", + "pallet-evm-precompile-preimage/std", + "pallet-evm-precompile-randomness/std", + "pallet-evm-precompile-referenda/std", + "pallet-evm-precompile-relay-encoder/std", + "pallet-evm-precompile-relay-verifier/std", + "pallet-evm-precompile-xcm-transactor/std", + "pallet-evm-precompile-xcm-utils/std", + "pallet-evm-precompile-xtokens/std", + "pallet-evm-precompileset-assets-erc20/std", + "pallet-evm/std", + "pallet-identity/std", + "pallet-maintenance-mode/std", + "pallet-migrations/std", + "pallet-moonbeam-lazy-migrations/std", + "pallet-moonbeam-orbiters/std", + "pallet-multisig/std", + "pallet-parachain-staking/std", + "pallet-precompile-benchmarks/std", + "pallet-preimage/std", + "pallet-proxy-genesis-companion/std", + "pallet-proxy/std", + "pallet-randomness/std", + "pallet-referenda/std", + "pallet-relay-storage-roots/std", + "pallet-root-testing/std", + "pallet-scheduler/std", + "pallet-society/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-treasury/std", + "pallet-utility/std", + "pallet-whitelist/std", + "pallet-xcm-transactor/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parity-scale-codec/std", + "precompile-utils/std", + "scale-info/std", + "session-keys-primitives/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "sp-genesis-builder/std", + "strum/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-primitives/std", + "xcm/std", +] +evm-tracing = ["evm-tracing-events", "moonbeam-evm-tracer", "rlp", "sha3"] + +# Will be enabled by the `wasm-builder` when building the runtime for WASM. +runtime-wasm = [] + +# A feature that should be enabled when the runtime should be build for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller like logging for example. +on-chain-release-build = ["sp-api/disable-logging"] + +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "moonbeam-relay-encoder/runtime-benchmarks", + "moonbeam-runtime-common/runtime-benchmarks", + "moonbeam-xcm-benchmarks/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "pallet-asset-manager/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-author-mapping/runtime-benchmarks", + "pallet-author-slot-filter/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-crowdloan-rewards/runtime-benchmarks", + "pallet-ethereum-xcm/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-identity/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", + "pallet-moonbeam-lazy-migrations/runtime-benchmarks", + "pallet-moonbeam-orbiters/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-parachain-staking/runtime-benchmarks", + "pallet-precompile-benchmarks/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-randomness/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-relay-storage-roots/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-society/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-treasury/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-transactor/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "session-keys-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] + +try-runtime = [ + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-dmp-queue/try-runtime", + "fp-self-contained/try-runtime", + "frame-executive/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime", + "moonbeam-runtime-common/try-runtime", + "pallet-asset-manager/try-runtime", + "pallet-author-mapping/try-runtime", + "pallet-author-slot-filter/try-runtime", + "pallet-balances/try-runtime", + "pallet-collective/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-maintenance-mode/try-runtime", + "pallet-migrations/try-runtime", + "pallet-moonbeam-lazy-migrations/try-runtime", + "pallet-parachain-staking/try-runtime", + "pallet-precompile-benchmarks/try-runtime", + "pallet-preimage/try-runtime", + "pallet-referenda/try-runtime", + "pallet-relay-storage-roots/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-society/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-whitelist/try-runtime", + "pallet-xcm/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-utility/try-runtime", + "pallet-transaction-payment/try-runtime", + "parachain-info/try-runtime", + "pallet-evm-chain-id/try-runtime", + "parachain-info/try-runtime", + "pallet-evm/try-runtime", + "pallet-ethereum/try-runtime", + "pallet-treasury/try-runtime", + "pallet-author-inherent/try-runtime", + "pallet-crowdloan-rewards/try-runtime", + "pallet-proxy/try-runtime", + "pallet-identity/try-runtime", + "orml-xtokens/try-runtime", + "pallet-assets/try-runtime", + "pallet-xcm-transactor/try-runtime", + "pallet-proxy-genesis-companion/try-runtime", + "pallet-moonbeam-orbiters/try-runtime", + "pallet-ethereum-xcm/try-runtime", + "pallet-randomness/try-runtime", + "pallet-whitelist/try-runtime", + "pallet-erc20-xcm-bridge/try-runtime", + "pallet-multisig/try-runtime", +] diff --git a/tracing/2900/runtime/moonbeam/build.rs b/tracing/2900/runtime/moonbeam/build.rs new file mode 100644 index 00000000..3934b9c5 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/tracing/2900/runtime/moonbeam/src/asset_config.rs b/tracing/2900/runtime/moonbeam/src/asset_config.rs new file mode 100644 index 00000000..439b7c05 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/asset_config.rs @@ -0,0 +1,219 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset configuration for Moonbase. +//! + +use super::{ + currency, governance, xcm_config, AccountId, AssetId, AssetManager, Assets, Balance, Balances, + OpenTechCommitteeInstance, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +use frame_support::{ + dispatch::GetDispatchInfo, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse}, + weights::Weight, +}; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; + +use frame_system::{EnsureNever, EnsureRoot}; +use sp_core::H160; + +use parity_scale_codec::{Compact, Decode, Encode}; +use scale_info::TypeInfo; + +use sp_std::{ + convert::{From, Into}, + prelude::*, +}; + +// Number of items that can be destroyed with our configured max extrinsic proof size. +// x = (a - b) / c where: +// a: maxExtrinsic proof size +// b: base proof size for destroy_accounts in pallet_assets weights +// c: proof size for each item +// 656.87 = (3_407_872 - 8232) / 5180 +const REMOVE_ITEMS_LIMIT: u32 = 656; + +// Not to disrupt the previous asset instance, we assign () to Foreign +pub type ForeignAssetInstance = (); + +// For foreign assets, these parameters dont matter much +// as this will only be called by root with the forced arguments +// No deposit is substracted with those methods +parameter_types! { + pub const AssetDeposit: Balance = 100 * currency::GLMR * currency::SUPPLY_FACTOR; + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = currency::deposit(1,68); + pub const MetadataDepositPerByte: Balance = currency::deposit(0, 1); +} + +/// We allow Root and General Admin to execute privileged asset operations. +pub type AssetsForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +// Foreign assets +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = ConstU128<{ currency::deposit(1, 18) }>; + type WeightInfo = moonbeam_weights::pallet_assets::WeightInfo; + type RemoveItemsLimit = ConstU32<{ REMOVE_ITEMS_LIMIT }>; + type AssetIdParameter = Compact; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::{pallet_prelude::DispatchResult, transactional}; + +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + #[transactional] + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetRegistrarMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset.into(), + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + // Lastly, the metadata + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset.into(), + metadata.name, + metadata.symbol, + metadata.decimals, + metadata.is_frozen, + ) + } + + #[transactional] + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + // For us both of them (Foreign and Local) have the same annotated weight for a given + // witness + // We need to take the dispatch info from the destroy call, which is already annotated in + // the assets pallet + + // This is the dispatch info of destroy + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetRegistrarMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, + pub is_frozen: bool, +} +pub type ForeignAssetModifierOrigin = EitherOfDiverse< + EnsureRoot, + EitherOfDiverse< + pallet_collective::EnsureProportionMoreThan, + governance::custom_origins::GeneralAdmin, + >, +>; + +pub type LocalAssetModifierOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetRegistrarMetadata; + type ForeignAssetType = xcm_config::AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = ForeignAssetModifierOrigin; + type WeightInfo = moonbeam_weights::pallet_asset_manager::WeightInfo; +} + +// Instruct how to go from an H160 to an AssetID +// We just take the lowest 128 bits +impl AccountIdAssetIdConversion for Runtime { + /// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF + /// and by taking the lowest 128 bits as the assetId + fn account_to_asset_id(account: AccountId) -> Option<(Vec, AssetId)> { + let h160_account: H160 = account.into(); + let mut data = [0u8; 16]; + let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(4); + if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX { + data.copy_from_slice(id_part); + let asset_id: AssetId = u128::from_be_bytes(data).into(); + Some((prefix_part.to_vec(), asset_id)) + } else { + None + } + } + + // The opposite conversion + fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId { + let mut data = [0u8; 20]; + data[0..4].copy_from_slice(prefix); + data[4..20].copy_from_slice(&asset_id.to_be_bytes()); + AccountId::from(data) + } +} diff --git a/tracing/2900/runtime/moonbeam/src/governance/councils.rs b/tracing/2900/runtime/moonbeam/src/governance/councils.rs new file mode 100644 index 00000000..c8cc1ca3 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/governance/councils.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2023 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; + +pub type TreasuryCouncilInstance = pallet_collective::Instance3; +pub type OpenTechCommitteeInstance = pallet_collective::Instance4; + +parameter_types! { + // TODO: Check value of this parameter + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for treasury council members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 3 * DAYS }>; + /// The maximum number of proposals that can be open in the treasury council at once. + type MaxProposals = ConstU32<20>; + /// The maximum number of treasury council members. + type MaxMembers = ConstU32<9>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for technical committee members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 14 * DAYS }>; + /// The maximum number of proposals that can be open in the technical committee at once. + type MaxProposals = ConstU32<100>; + /// The maximum number of technical committee members. + type MaxMembers = ConstU32<100>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} diff --git a/tracing/2900/runtime/moonbeam/src/governance/mod.rs b/tracing/2900/runtime/moonbeam/src/governance/mod.rs new file mode 100644 index 00000000..bfe3def3 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/governance/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2023 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Governance configurations + +pub mod councils; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{ + custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller, +}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/tracing/2900/runtime/moonbeam/src/governance/origins.rs b/tracing/2900/runtime/moonbeam/src/governance/origins.rs new file mode 100644 index 00000000..48154cd1 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/governance/origins.rs @@ -0,0 +1,85 @@ +// Copyright 2019-2023 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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. + +//! Custom origins for governance interventions. +#![cfg_attr(not(feature = "std"), no_std)] + +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive( + PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString, + )] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + /// Fast General Admin + FastGeneralAdmin, + } + + 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!( + ReferendumCanceller, + ReferendumKiller, + WhitelistedCaller, + GeneralAdmin, + FastGeneralAdmin, + ); +} diff --git a/tracing/2900/runtime/moonbeam/src/governance/referenda.rs b/tracing/2900/runtime/moonbeam/src/governance/referenda.rs new file mode 100644 index 00000000..62720a0d --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/governance/referenda.rs @@ -0,0 +1,100 @@ +// Copyright 2019-2023 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use crate::currency::{GLMR, SUPPLY_FACTOR}; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_conviction_voting::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub const SubmissionDeposit: Balance = 10 * GLMR * SUPPLY_FACTOR; + pub const UndecidingTimeout: BlockNumber = 21 * DAYS; +} + +// Origin for general admin or root +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; +// The policy allows for Root or FastGeneralAdmin. +pub type FastGeneralAdminOrRoot = EitherOf, origins::FastGeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_whitelist::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast< + Self::AccountId, + OpenTechCommitteeInstance, + 5, + 9, + >, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +impl pallet_referenda::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_referenda::WeightInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + 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; +} diff --git a/tracing/2900/runtime/moonbeam/src/governance/tracks.rs b/tracing/2900/runtime/moonbeam/src/governance/tracks.rs new file mode 100644 index 00000000..a69cd645 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/governance/tracks.rs @@ -0,0 +1,193 @@ +// Copyright 2022 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 crate::currency::{GLMR, KILOGLMR, SUPPLY_FACTOR}; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +use pallet_referenda::Curve; +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 6] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 20 * KILOGLMR * SUPPLY_FACTOR, + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 2 * KILOGLMR * SUPPLY_FACTOR, + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 100 * GLMR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 2 * KILOGLMR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 4 * KILOGLMR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 5, + pallet_referenda::TrackInfo { + name: "fast_general_admin", + max_deciding: 10, + decision_deposit: 100 * GLMR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(5, 14, percent(1), percent(0), percent(50)), + }, + ), +]; + +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(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks() + .into_iter() + .find(|(_, track)| track.name == "root") + { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in TRACKS_DATA { + assert!( + ::VoteLockingPeriod::get() + >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in TRACKS_DATA { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/tracing/2900/runtime/moonbeam/src/lib.rs b/tracing/2900/runtime/moonbeam/src/lib.rs new file mode 100644 index 00000000..5f79e4a4 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/lib.rs @@ -0,0 +1,1788 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! The Moonbeam Runtime. +//! +//! Primary features of this runtime include: +//! * Ethereum compatibility +//! * Moonbeam tokenomics + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use account::AccountId20; +use cumulus_pallet_parachain_system::{RelayChainStateProof, RelaychainDataProvider}; +use fp_rpc::TransactionStatus; + +use cumulus_primitives_core::{relay_chain, AggregateMessageOrigin}; +#[cfg(feature = "std")] +pub use fp_evm::GenesisAccount; +pub use frame_support::traits::Get; +use frame_support::{ + construct_runtime, + dispatch::{DispatchClass, GetDispatchInfo, PostDispatchInfo}, + ensure, + pallet_prelude::DispatchResult, + parameter_types, + traits::{ + fungible::{Balanced, Credit, HoldConsideration, Inspect}, + tokens::imbalance::ResolveTo, + tokens::{PayFromAccount, UnityAssetBalanceConversion}, + ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Contains, EitherOfDiverse, + EqualPrivilegeOnly, Imbalance, InstanceFilter, LinearStoragePrice, OnFinalize, + OnUnbalanced, + }, + weights::{ + constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + PalletId, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +pub use moonbeam_core_primitives::{ + AccountId, AccountIndex, Address, AssetId, Balance, BlockNumber, DigestItem, Hash, Header, + Index, Signature, +}; +use moonbeam_rpc_primitives_txpool::TxPoolResponse; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_ethereum::Call::transact; +use pallet_ethereum::{PostLogContent, Transaction as EthereumTransaction}; +use pallet_evm::{ + Account as EVMAccount, EVMFungibleAdapter, EnsureAddressNever, EnsureAddressRoot, + FeeCalculator, GasWeightMapping, IdentityAddressMapping, + OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner, +}; +pub use pallet_parachain_staking::{weights::WeightInfo, InflationInfo, Range}; +use pallet_transaction_payment::{FungibleAdapter, Multiplier, TargetedFeeAdjustment}; +use pallet_treasury::TreasuryAccountId; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use smallvec::smallvec; +use sp_api::impl_runtime_apis; +use sp_consensus_slots::Slot; +use sp_core::{OpaqueMetadata, H160, H256, U256}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{ + BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentityLookup, + PostDispatchInfoOf, UniqueSaturatedInto, Zero, + }, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + }, + ApplyExtrinsicResult, DispatchErrorWithPostInfo, FixedPointNumber, Perbill, Permill, + Perquintill, SaturatedConversion, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use nimbus_primitives::CanAuthor; + +mod precompiles; +pub use precompiles::{ + MoonbeamPrecompiles, PrecompileName, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +pub type Precompiles = MoonbeamPrecompiles; + +pub mod asset_config; +pub mod governance; +pub mod xcm_config; +use governance::councils::*; + +/// GLMR, the native token, uses 18 decimals of precision. +pub mod currency { + use super::Balance; + + // Provide a common factor between runtimes based on a supply of 10_000_000 tokens. + pub const SUPPLY_FACTOR: Balance = 100; + + pub const WEI: Balance = 1; + pub const KILOWEI: Balance = 1_000; + pub const MEGAWEI: Balance = 1_000_000; + pub const GIGAWEI: Balance = 1_000_000_000; + pub const MICROGLMR: Balance = 1_000_000_000_000; + pub const MILLIGLMR: Balance = 1_000_000_000_000_000; + pub const GLMR: Balance = 1_000_000_000_000_000_000; + pub const KILOGLMR: Balance = 1_000_000_000_000_000_000_000; + + pub const TRANSACTION_BYTE_FEE: Balance = 1 * GIGAWEI * SUPPLY_FACTOR; + pub const STORAGE_BYTE_FEE: Balance = 100 * MICROGLMR * SUPPLY_FACTOR; + pub const WEIGHT_FEE: Balance = 50 * KILOWEI * SUPPLY_FACTOR; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 100 * MILLIGLMR * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE + } +} + +/// Maximum weight per block +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX) + .saturating_div(2) + .set_proof_size(relay_chain::MAX_POV_SIZE as u64); + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const WEEKS: BlockNumber = DAYS * 7; +pub const MONTHS: BlockNumber = DAYS * 30; +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core datastructures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + pub type Block = generic::Block; + + impl_opaque_keys! { + pub struct SessionKeys { + pub nimbus: AuthorInherent, + pub vrf: session_keys_primitives::VrfSessionKey, + } + } +} + +/// This runtime version. +/// The spec_version is composed of 2x2 digits. The first 2 digits represent major changes +/// that can't be skipped, such as data migration upgrades. The last 2 digits represent minor +/// changes which can be skipped. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("moonbeam"), + impl_name: create_runtime_str!("moonbeam"), + authoring_version: 3, + spec_version: 2900, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 2, + state_version: 0, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const NORMAL_WEIGHT: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_mul(3).saturating_div(4); +// Here we assume Ethereum's base fee of 21000 gas and convert to weight, but we +// subtract roughly the cost of a balance transfer from it (about 1/3 the cost) +// and some cost to account for per-byte-fee. +// TODO: we should use benchmarking's overhead feature to measure this +pub const EXTRINSIC_BASE_WEIGHT: Weight = Weight::from_parts(10000 * WEIGHT_PER_GAS, 0); + +pub struct RuntimeBlockWeights; +impl Get for RuntimeBlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .for_class(DispatchClass::Normal, |weights| { + weights.base_extrinsic = EXTRINSIC_BASE_WEIGHT; + weights.max_total = NORMAL_WEIGHT.into(); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = MAXIMUM_BLOCK_WEIGHT.into(); + weights.reserved = (MAXIMUM_BLOCK_WEIGHT - NORMAL_WEIGHT).into(); + }) + .avg_block_initialization(Perbill::from_percent(10)) + .build() + .expect("Provided BlockWeight definitions are valid, qed") + } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + /// We allow for 5 MB blocks. + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); +} + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Index; + /// The index type for blocks. + type Block = Block; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// The aggregated RuntimeTask type. + type RuntimeTask = RuntimeTask; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = ConstU32<256>; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type BlockWeights = RuntimeBlockWeights; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type BlockLength = BlockLength; + /// Runtime version. + type Version = Version; + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = RocksDbWeight; + type BaseCallFilter = MaintenanceMode; + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = ConstU16<1284>; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = moonbeam_weights::pallet_utility::WeightInfo; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<6000>; + type WeightInfo = moonbeam_weights::pallet_timestamp::WeightInfo; +} + +impl pallet_balances::Config for Runtime { + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 4]; + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<0>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = moonbeam_weights::pallet_balances::WeightInfo; +} + +pub struct DealWithFees(sp_std::marker::PhantomData); +impl OnUnbalanced>> for DealWithFees +where + R: pallet_balances::Config + pallet_treasury::Config, +{ + // this seems to be called for substrate-based transactions + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>>, + ) { + if let Some(fees) = fees_then_tips.next() { + // for fees, 80% are burned, 20% to the treasury + let (_, to_treasury) = fees.ration(80, 20); + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + + // handle tip if there is one + if let Some(tip) = fees_then_tips.next() { + // for now we use the same burn/treasury strategy used for regular fees + let (_, to_treasury) = tip.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + } + } + } + + // this is called from pallet_evm for Ethereum-based transactions + // (technically, it calls on_unbalanced, which calls this when non-zero) + fn on_nonzero_unbalanced(amount: Credit>) { + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + let (_, to_treasury) = amount.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced(to_treasury); + } +} + +pub struct LengthToFee; +impl WeightToFeePolynomial for LengthToFee { + type Balance = Balance; + + fn polynomial() -> WeightToFeeCoefficients { + smallvec![ + WeightToFeeCoefficient { + degree: 1, + coeff_frac: Perbill::zero(), + coeff_integer: currency::TRANSACTION_BYTE_FEE, + negative: false, + }, + WeightToFeeCoefficient { + degree: 3, + coeff_frac: Perbill::zero(), + coeff_integer: 1 * currency::SUPPLY_FACTOR, + negative: false, + }, + ] + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = FungibleAdapter>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = ConstantMultiplier>; + type LengthToFee = LengthToFee; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; +} + +impl pallet_evm_chain_id::Config for Runtime {} + +/// Current approximation of the gas/s consumption considering +/// EVM execution over compiled WASM (on 4.4Ghz CPU). +/// Given the 500ms Weight, from which 75% only are used for transactions, +/// the total EVM execution gas limit is: GAS_PER_SECOND * 0.500 * 0.75 ~= 15_000_000. +pub const GAS_PER_SECOND: u64 = 40_000_000; + +/// Approximate ratio of the amount of Weight per Gas. +/// u64 works for approximations because Weight is a very small unit compared to gas. +pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND; + +parameter_types! { + pub BlockGasLimit: U256 + = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); + /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less + /// than this will decrease the weight and more will increase. + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(50); + /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to + /// change the fees more rapidly. This low value causes changes to occur slowly over time. + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(4, 1_000); + /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure + /// that combined with `AdjustmentVariable`, we can recover from the minimum. + /// See `multiplier_can_grow_from_zero` in integration_tests.rs. + /// This value is currently only used by pallet-transaction-payment as an assertion that the + /// next multiplier is always > min value. + pub MinimumMultiplier: Multiplier = Multiplier::from(1u128); + /// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act + /// as a safety net. + pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128); + pub PrecompilesValue: MoonbeamPrecompiles = MoonbeamPrecompiles::<_>::new(); + pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); + /// The amount of gas per pov. A ratio of 4 if we convert ref_time to gas and we compare + /// it with the pov_size for a block. E.g. + /// ceil( + /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS + /// ) + pub const GasLimitPovSizeRatio: u64 = 4; + /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT + /// The current definition of BLOCK_STORAGE_LIMIT is 40 KB, resulting in a value of 366. + pub GasLimitStorageGrowthRatio: u64 = 366; +} + +pub struct TransactionPaymentAsGasPrice; +impl FeeCalculator for TransactionPaymentAsGasPrice { + fn min_gas_price() -> (U256, Weight) { + // note: transaction-payment differs from EIP-1559 in that its tip and length fees are not + // scaled by the multiplier, which means its multiplier will be overstated when + // applied to an ethereum transaction + // note: transaction-payment uses both a congestion modifier (next_fee_multiplier, which is + // updated once per block in on_finalize) and a 'WeightToFee' implementation. Our + // runtime implements this as a 'ConstantModifier', so we can get away with a simple + // multiplication here. + // It is imperative that `saturating_mul_int` be performed as late as possible in the + // expression since it involves fixed point multiplication with a division by a fixed + // divisor. This leads to truncation and subsequent precision loss if performed too early. + // This can lead to min_gas_price being same across blocks even if the multiplier changes. + // There's still some precision loss when the final `gas_price` (used_gas * min_gas_price) + // is computed in frontier, but that's currently unavoidable. + let min_gas_price = TransactionPayment::next_fee_multiplier() + .saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128)); + ( + min_gas_price.into(), + ::DbWeight::get().reads(1), + ) + } +} + +/// Parameterized slow adjusting fee updated based on +/// https://w3f-research.readthedocs.io/en/latest/polkadot/overview/2-token-economics.html#-2.-slow-adjusting-mechanism // editorconfig-checker-disable-line +/// +/// The adjustment algorithm boils down to: +/// +/// diff = (previous_block_weight - target) / maximum_block_weight +/// next_multiplier = prev_multiplier * (1 + (v * diff) + ((v * diff)^2 / 2)) +/// assert(next_multiplier > min) +/// where: v is AdjustmentVariable +/// target is TargetBlockFullness +/// min is MinimumMultiplier +pub type SlowAdjustingFeeUpdate = TargetedFeeAdjustment< + R, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, +>; + +use frame_support::traits::FindAuthor; +//TODO It feels like this shold be able to work for any T: H160, but I tried for +// embarassingly long and couldn't figure that out. + +/// The author inherent provides a AccountId20, but pallet evm needs an H160. +/// This simple adapter makes the conversion. +pub struct FindAuthorAdapter(sp_std::marker::PhantomData); + +impl FindAuthor for FindAuthorAdapter +where + Inner: FindAuthor, +{ + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + Inner::find_author(digests).map(Into::into) + } +} + +moonbeam_runtime_common::impl_on_charge_evm_transaction!(); + +impl pallet_evm::Config for Runtime { + type FeeCalculator = TransactionPaymentAsGasPrice; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = MoonbeamPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = EthereumChainId; + type OnChargeTransaction = OnChargeEVMTransaction>; + type BlockGasLimit = BlockGasLimit; + type FindAuthor = FindAuthorAdapter; + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = Timestamp; + type WeightInfo = moonbeam_weights::pallet_evm::WeightInfo; +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = NORMAL_DISPATCH_RATIO * RuntimeBlockWeights::get().max_block; +} + +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<50>; + type WeightInfo = moonbeam_weights::pallet_scheduler::WeightInfo; + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +parameter_types! { + pub const PreimageBaseDeposit: Balance = 5 * currency::GLMR * currency::SUPPLY_FACTOR ; + pub const PreimageByteDeposit: Balance = currency::STORAGE_BYTE_FEE; + pub const PreimageHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + +impl pallet_preimage::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_preimage::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const TreasuryId: PalletId = PalletId(*b"py/trsry"); + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +type TreasuryApproveOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +type TreasuryRejectOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionMoreThan, +>; + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + // At least three-fifths majority of the council is required (or root) to approve a proposal + type ApproveOrigin = TreasuryApproveOrigin; + // More than half of the council is required (or root) to reject a proposal + type RejectOrigin = TreasuryRejectOrigin; + type RuntimeEvent = RuntimeEvent; + // If spending proposal rejected, transfer proposer bond to treasury + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ConstU128<{ 1 * currency::GLMR * currency::SUPPLY_FACTOR }>; + type SpendPeriod = ConstU32<{ 6 * DAYS }>; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = ConstU32<100>; + type WeightInfo = moonbeam_weights::pallet_treasury::WeightInfo; + type SpendFunds = (); + type ProposalBondMaximum = (); + #[cfg(not(feature = "runtime-benchmarks"))] + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Disabled, no spending + #[cfg(feature = "runtime-benchmarks")] + type SpendOrigin = + frame_system::EnsureWithSuccess, AccountId, benches::MaxBalance>; + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<{ 30 * DAYS }>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +parameter_types! { + pub const MaxSubAccounts: u32 = 100; + pub const MaxAdditionalFields: u32 = 100; + pub const MaxRegistrars: u32 = 20; + pub const PendingUsernameExpiration: u32 = 7 * DAYS; + pub const MaxSuffixLength: u32 = 7; + pub const MaxUsernameLength: u32 = 32; +} + +type IdentityForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type IdentityRegistrarOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_identity::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + // Add one item in storage and take 258 bytes + type BasicDeposit = ConstU128<{ currency::deposit(1, 258) }>; + // Does not add any item to the storage but takes 1 bytes + type ByteDeposit = ConstU128<{ currency::deposit(0, 1) }>; + // Add one item in storage and take 53 bytes + type SubAccountDeposit = ConstU128<{ currency::deposit(1, 53) }>; + type MaxSubAccounts = MaxSubAccounts; + type IdentityInformation = pallet_identity::legacy::IdentityInfo; + type MaxRegistrars = MaxRegistrars; + type Slashed = Treasury; + type ForceOrigin = IdentityForceOrigin; + type RegistrarOrigin = IdentityRegistrarOrigin; + type OffchainSignature = Signature; + type SigningPublicKey = ::Signer; + type UsernameAuthorityOrigin = EnsureRoot; + type PendingUsernameExpiration = PendingUsernameExpiration; + type MaxSuffixLength = MaxSuffixLength; + type MaxUsernameLength = MaxUsernameLength; + type WeightInfo = moonbeam_weights::pallet_identity::WeightInfo; +} + +pub struct TransactionConverter; + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = ParachainInfo; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; + type ConsensusHook = cumulus_pallet_parachain_system::ExpectParentIncluded; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_parachain_system::weights::SubstrateWeight; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will immediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl parachain_info::Config for Runtime {} + +pub struct OnNewRound; +impl pallet_parachain_staking::OnNewRound for OnNewRound { + fn on_new_round(round_index: pallet_parachain_staking::RoundIndex) -> Weight { + MoonbeamOrbiters::on_new_round(round_index) + } +} +pub struct PayoutCollatorOrOrbiterReward; +impl pallet_parachain_staking::PayoutCollatorReward for PayoutCollatorOrOrbiterReward { + fn payout_collator_reward( + for_round: pallet_parachain_staking::RoundIndex, + collator_id: AccountId, + amount: Balance, + ) -> Weight { + let extra_weight = + if MoonbeamOrbiters::is_collator_pool_with_active_orbiter(for_round, collator_id) { + MoonbeamOrbiters::distribute_rewards(for_round, collator_id, amount) + } else { + ParachainStaking::mint_collator_reward(for_round, collator_id, amount) + }; + + ::DbWeight::get() + .reads(1) + .saturating_add(extra_weight) + } +} + +pub struct OnInactiveCollator; +impl pallet_parachain_staking::OnInactiveCollator for OnInactiveCollator { + fn on_inactive_collator( + collator_id: AccountId, + round: pallet_parachain_staking::RoundIndex, + ) -> Result> { + let extra_weight = if !MoonbeamOrbiters::is_collator_pool_with_active_orbiter( + round, + collator_id.clone(), + ) { + ParachainStaking::go_offline_inner(collator_id)?; + ::WeightInfo::go_offline( + pallet_parachain_staking::MAX_CANDIDATES, + ) + } else { + Weight::zero() + }; + + Ok(::DbWeight::get() + .reads(1) + .saturating_add(extra_weight)) + } +} +type MonetaryGovernanceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +/// TODO: +/// Temporary type that we should replace by RelayChainSlotProvider once async backing is enabled. +pub struct StakingRoundSlotProvider; +impl Get for StakingRoundSlotProvider { + fn get() -> Slot { + let block_number: u64 = frame_system::pallet::Pallet::::block_number().into(); + Slot::from(block_number) + } +} + +impl pallet_parachain_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MonetaryGovernanceOrigin = MonetaryGovernanceOrigin; + /// Minimum round length is 2 minutes (10 * 12 second block times) + type MinBlocksPerRound = ConstU32<10>; + /// If a collator doesn't produce any block on this number of rounds, it is notified as inactive + type MaxOfflineRounds = ConstU32<1>; + /// Rounds before the collator leaving the candidates request can be executed + type LeaveCandidatesDelay = ConstU32<{ 4 * 7 }>; + /// Rounds before the candidate bond increase/decrease can be executed + type CandidateBondLessDelay = ConstU32<{ 4 * 7 }>; + /// Rounds before the delegator exit can be executed + type LeaveDelegatorsDelay = ConstU32<{ 4 * 7 }>; + /// Rounds before the delegator revocation can be executed + type RevokeDelegationDelay = ConstU32<{ 4 * 7 }>; + /// Rounds before the delegator bond increase/decrease can be executed + type DelegationBondLessDelay = ConstU32<{ 4 * 7 }>; + /// Rounds before the reward is paid + type RewardPaymentDelay = ConstU32<2>; + /// Minimum collators selected per round, default at genesis and minimum forever after + type MinSelectedCandidates = ConstU32<8>; + /// Maximum top delegations per candidate + type MaxTopDelegationsPerCandidate = ConstU32<300>; + /// Maximum bottom delegations per candidate + type MaxBottomDelegationsPerCandidate = ConstU32<50>; + /// Maximum delegations per delegator + type MaxDelegationsPerDelegator = ConstU32<100>; + /// Minimum stake required to be reserved to be a candidate + type MinCandidateStk = ConstU128<{ 20_000 * currency::GLMR * currency::SUPPLY_FACTOR }>; + /// Minimum stake required to be reserved to be a delegator + type MinDelegation = ConstU128<{ 500 * currency::MILLIGLMR * currency::SUPPLY_FACTOR }>; + type BlockAuthor = AuthorInherent; + type OnCollatorPayout = (); + type PayoutCollatorReward = PayoutCollatorOrOrbiterReward; + type OnInactiveCollator = OnInactiveCollator; + type OnNewRound = OnNewRound; + type SlotProvider = StakingRoundSlotProvider; + type WeightInfo = moonbeam_weights::pallet_parachain_staking::WeightInfo; + type MaxCandidates = ConstU32<200>; + type SlotDuration = ConstU64<12_000>; + type BlockTime = ConstU64<12_000>; +} + +impl pallet_author_inherent::Config for Runtime { + type SlotBeacon = RelaychainDataProvider; + type AccountLookup = MoonbeamOrbiters; + type CanAuthor = AuthorFilter; + type AuthorId = AccountId; + type WeightInfo = moonbeam_weights::pallet_author_inherent::WeightInfo; +} + +impl pallet_author_slot_filter::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RandomnessSource = Randomness; + type PotentialAuthors = ParachainStaking; + type WeightInfo = moonbeam_weights::pallet_author_slot_filter::WeightInfo; +} + +parameter_types! { + pub const InitializationPayment: Perbill = Perbill::from_percent(30); + pub const RelaySignaturesThreshold: Perbill = Perbill::from_percent(100); + pub const SignatureNetworkIdentifier: &'static [u8] = b"moonbeam-"; +} + +impl pallet_crowdloan_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Initialized = ConstBool; + type InitializationPayment = InitializationPayment; + type MaxInitContributors = ConstU32<500>; + type MinimumReward = ConstU128<0>; + type RewardCurrency = Balances; + type RelayChainAccountId = [u8; 32]; + type RewardAddressAssociateOrigin = EnsureSigned; + type RewardAddressChangeOrigin = EnsureSigned; + type RewardAddressRelayVoteThreshold = RelaySignaturesThreshold; + type SignatureNetworkIdentifier = SignatureNetworkIdentifier; + type VestingBlockNumber = relay_chain::BlockNumber; + type VestingBlockProvider = RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_crowdloan_rewards::WeightInfo; +} + +// This is a simple session key manager. It should probably either work with, or be replaced +// entirely by pallet sessions +impl pallet_author_mapping::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DepositCurrency = Balances; + type DepositAmount = ConstU128<{ 100 * currency::GLMR * currency::SUPPLY_FACTOR }>; + type Keys = session_keys_primitives::VrfId; + type WeightInfo = moonbeam_weights::pallet_author_mapping::WeightInfo; +} + +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, +)] +pub enum ProxyType { + /// All calls can be proxied. This is the trivial/most permissive filter. + Any = 0, + /// Only extrinsics that do not transfer funds. + NonTransfer = 1, + /// Only extrinsics related to governance (democracy and collectives). + Governance = 2, + /// Only extrinsics related to staking. + Staking = 3, + /// Allow to veto an announced proxy call. + CancelProxy = 4, + /// Allow extrinsic related to Balances. + Balances = 5, + /// Allow extrinsic related to AuthorMapping. + AuthorMapping = 6, + /// Allow extrinsic related to IdentityJudgement. + IdentityJudgement = 7, +} + +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +fn is_governance_precompile(precompile_name: &precompiles::PrecompileName) -> bool { + matches!( + precompile_name, + PrecompileName::ConvictionVotingPrecompile + | PrecompileName::PreimagePrecompile + | PrecompileName::ReferendaPrecompile + | PrecompileName::OpenTechCommitteeInstance + | PrecompileName::TreasuryCouncilInstance + ) +} + +// Be careful: Each time this filter is modified, the substrate filter must also be modified +// consistently. +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType { + fn is_evm_proxy_call_allowed( + &self, + call: &pallet_evm_precompile_proxy::EvmSubCall, + recipient_has_code: bool, + gas: u64, + ) -> precompile_utils::EvmResult { + Ok(match self { + ProxyType::Any => { + match PrecompileName::from_address(call.to.0) { + // Any precompile that can execute a subcall should be forbidden here, + // to ensure that unauthorized smart contract can't be called + // indirectly. + // To be safe, we only allow the precompiles we need. + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile, + ) => true, + Some(ref precompile) if is_governance_precompile(precompile) => true, + // All non-whitelisted precompiles are forbidden + Some(_) => false, + // Allow evm transfer to "simple" account (no code nor precompile) + // For the moment, no smart contract other than precompiles is allowed. + // In the future, we may create a dynamic whitelist to authorize some audited + // smart contracts through governance. + None => { + // If the address is not recognized, allow only evm transfert to "simple" + // accounts (no code nor precompile). + // Note: Checking the presence of the code is not enough because some + // precompiles have no code. + !recipient_has_code + && precompile_utils::precompile_set::is_precompile_or_fail::( + call.to.0, gas, + )? + } + } + } + ProxyType::NonTransfer => { + call.value == U256::zero() + && match PrecompileName::from_address(call.to.0) { + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile, + ) => true, + Some(ref precompile) if is_governance_precompile(precompile) => true, + _ => false, + } + } + ProxyType::Governance => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(ref precompile) if is_governance_precompile(precompile) + ) + } + ProxyType::Staking => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile + ) + ) + } + // The proxy precompile does not contain method cancel_proxy + ProxyType::CancelProxy => false, + ProxyType::Balances => { + // Allow only "simple" accounts as recipient (no code nor precompile). + // Note: Checking the presence of the code is not enough because some precompiles + // have no code. + !recipient_has_code + && !precompile_utils::precompile_set::is_precompile_or_fail::( + call.to.0, gas, + )? + } + ProxyType::AuthorMapping => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(PrecompileName::AuthorMappingPrecompile) + ) + } + // There is no identity precompile + ProxyType::IdentityJudgement => false, + }) + } +} + +// Be careful: Each time this filter is modified, the EVM filter must also be modified consistently. +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + matches!( + c, + RuntimeCall::System(..) + | RuntimeCall::ParachainSystem(..) + | RuntimeCall::Timestamp(..) + | RuntimeCall::ParachainStaking(..) + | RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Identity(..) + | RuntimeCall::Utility(..) + | RuntimeCall::Proxy(..) | RuntimeCall::AuthorMapping(..) + | RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::claim { .. } + ) + ) + } + ProxyType::Governance => matches!( + c, + RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Utility(..) + ), + ProxyType::Staking => matches!( + c, + RuntimeCall::ParachainStaking(..) + | RuntimeCall::Utility(..) + | RuntimeCall::AuthorMapping(..) + | RuntimeCall::MoonbeamOrbiters(..) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) + ), + ProxyType::Balances => { + matches!(c, RuntimeCall::Balances(..) | RuntimeCall::Utility(..)) + } + ProxyType::AuthorMapping => matches!(c, RuntimeCall::AuthorMapping(..)), + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) + | RuntimeCall::Utility(..) + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + _ => false, + } + } +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + // One storage item; key size 32, value size 8 + type ProxyDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 21 bytes (20 bytes AccountId + 1 byte sizeof(ProxyType)). + type ProxyDepositFactor = ConstU128<{ currency::deposit(0, 21) }>; + type MaxProxies = ConstU32<32>; + type WeightInfo = moonbeam_weights::pallet_proxy::WeightInfo; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 56 bytes: + // - 20 bytes AccountId + // - 32 bytes Hasher (Blake2256) + // - 4 bytes BlockNumber (u32) + type AnnouncementDepositFactor = ConstU128<{ currency::deposit(0, 56) }>; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MigrationsList = (moonbeam_runtime_common::migrations::CommonMigrations,); + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_moonbeam_lazy_migrations::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_moonbeam_lazy_migrations::WeightInfo; +} + +/// Maintenance mode Call filter +pub struct MaintenanceFilter; +impl Contains for MaintenanceFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(_) => false, + RuntimeCall::Balances(_) => false, + RuntimeCall::CrowdloanRewards(_) => false, + RuntimeCall::Ethereum(_) => false, + RuntimeCall::EVM(_) => false, + RuntimeCall::Identity(_) => false, + RuntimeCall::XTokens(_) => false, + RuntimeCall::ParachainStaking(_) => false, + RuntimeCall::MoonbeamOrbiters(_) => false, + RuntimeCall::PolkadotXcm(_) => false, + RuntimeCall::Treasury(_) => false, + RuntimeCall::XcmTransactor(_) => false, + RuntimeCall::EthereumXcm(_) => false, + _ => true, + } + } +} + +/// Normal Call Filter +/// We dont allow to create nor mint assets, this for now is disabled +/// We only allow transfers. For now creation of assets will go through +/// asset-manager, while minting/burning only happens through xcm messages +/// This can change in the future +pub struct NormalFilter; +impl Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(method) => match method { + pallet_assets::Call::transfer { .. } => true, + pallet_assets::Call::transfer_keep_alive { .. } => true, + pallet_assets::Call::approve_transfer { .. } => true, + pallet_assets::Call::transfer_approved { .. } => true, + pallet_assets::Call::cancel_approval { .. } => true, + pallet_assets::Call::destroy_accounts { .. } => true, + pallet_assets::Call::destroy_approvals { .. } => true, + pallet_assets::Call::finish_destroy { .. } => true, + _ => false, + }, + // We just want to enable this in case of live chains, since the default version + // is populated at genesis + RuntimeCall::PolkadotXcm(method) => match method { + pallet_xcm::Call::force_default_xcm_version { .. } => true, + _ => false, + }, + // We filter anonymous proxy as they make "reserve" inconsistent + // See: https://github.com/paritytech/substrate/blob/37cca710eed3dadd4ed5364c7686608f5175cce1/frame/proxy/src/lib.rs#L270 // editorconfig-checker-disable-line + RuntimeCall::Proxy(method) => match method { + pallet_proxy::Call::create_pure { .. } => false, + pallet_proxy::Call::kill_pure { .. } => false, + pallet_proxy::Call::proxy { real, .. } => { + !pallet_evm::AccountCodes::::contains_key(H160::from(*real)) + } + _ => true, + }, + // Filtering the EVM prevents possible re-entrancy from the precompiles which could + // lead to unexpected scenarios. + // See https://github.com/PureStake/sr-moonbeam/issues/30 + // Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so + // this can be seen as an additional security + RuntimeCall::EVM(_) => false, + RuntimeCall::Treasury( + pallet_treasury::Call::spend { .. } + | pallet_treasury::Call::payout { .. } + | pallet_treasury::Call::check_status { .. } + | pallet_treasury::Call::void_spend { .. }, + ) => false, + _ => true, + } + } +} + +pub struct XcmExecutionManager; +impl moonkit_xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} + +impl pallet_maintenance_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NormalCallFilter = NormalFilter; + type MaintenanceCallFilter = MaintenanceFilter; + type MaintenanceOrigin = + pallet_collective::EnsureProportionAtLeast; + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_proxy_genesis_companion::Config for Runtime { + type ProxyType = ProxyType; +} + +parameter_types! { + pub OrbiterReserveIdentifier: [u8; 4] = [b'o', b'r', b'b', b'i']; +} + +type AddCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type DelCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_moonbeam_orbiters::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AccountLookup = AuthorMapping; + type AddCollatorOrigin = AddCollatorOrigin; + type Currency = Balances; + type DelCollatorOrigin = DelCollatorOrigin; + /// Maximum number of orbiters per collator + type MaxPoolSize = ConstU32<8>; + /// Maximum number of round to keep on storage + type MaxRoundArchive = ConstU32<4>; + type OrbiterReserveIdentifier = OrbiterReserveIdentifier; + type RotatePeriod = ConstU32<1>; + /// Round index type. + type RoundIndex = pallet_parachain_staking::RoundIndex; + type WeightInfo = moonbeam_weights::pallet_moonbeam_orbiters::WeightInfo; +} + +/// Only callable after `set_validation_data` is called which forms this proof the same way +fn relay_chain_state_proof() -> RelayChainStateProof { + let relay_storage_root = ParachainSystem::validation_data() + .expect("set in `set_validation_data`") + .relay_parent_storage_root; + let relay_chain_state = + ParachainSystem::relay_state_proof().expect("set in `set_validation_data`"); + RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) + .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") +} + +pub struct BabeDataGetter; +impl pallet_randomness::GetBabeData> for BabeDataGetter { + // Tolerate panic here because only ever called in inherent (so can be omitted) + fn get_epoch_index() -> u64 { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + const BENCHMARKING_NEW_EPOCH: u64 = 10u64; + return BENCHMARKING_NEW_EPOCH; + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::EPOCH_INDEX) + .ok() + .flatten() + .expect("expected to be able to read epoch index from relay chain state proof") + } + fn get_epoch_randomness() -> Option { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + let benchmarking_babe_output = Hash::default(); + return Some(benchmarking_babe_output); + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) + .ok() + .flatten() + } +} + +impl pallet_randomness::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddressMapping = sp_runtime::traits::ConvertInto; + type Currency = Balances; + type BabeDataGetter = BabeDataGetter; + type VrfKeyLookup = AuthorMapping; + type Deposit = ConstU128<{ 1 * currency::GLMR * currency::SUPPLY_FACTOR }>; + type MaxRandomWords = ConstU8<100>; + type MinBlockDelay = ConstU32<2>; + type MaxBlockDelay = ConstU32<2_000>; + type BlockExpirationDelay = ConstU32<10_000>; + type EpochExpirationDelay = ConstU64<10_000>; + type WeightInfo = moonbeam_weights::pallet_randomness::WeightInfo; +} + +impl pallet_root_testing::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +parameter_types! { + // One storage item; key size is 32 + 20; value is size 4+4+16+20 bytes = 44 bytes. + pub const DepositBase: Balance = currency::deposit(1, 96); + // Additional storage item size of 20 bytes. + pub const DepositFactor: Balance = currency::deposit(0, 20); + pub const MaxSignatories: u32 = 100; +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = MaxSignatories; + type WeightInfo = moonbeam_weights::pallet_multisig::WeightInfo; +} + +impl pallet_relay_storage_roots::Config for Runtime { + type MaxStorageRoots = ConstU32<30>; + type RelaychainStateProvider = cumulus_pallet_parachain_system::RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_relay_storage_roots::WeightInfo; +} + +impl pallet_precompile_benchmarks::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_precompile_benchmarks::WeightInfo; +} + +construct_runtime! { + pub enum Runtime + { + // System support stuff. + System: frame_system::{Pallet, Call, Storage, Config, Event} = 0, + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 1, + // Previously 2: pallet_randomness_collective_flip + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 3, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 4, + RootTesting: pallet_root_testing::{Pallet, Call, Storage, Event} = 5, + + // Monetary stuff. + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Config, Event} = 11, + + // Consensus support. + ParachainStaking: pallet_parachain_staking::{Pallet, Call, Storage, Event, Config} = 20, + AuthorInherent: pallet_author_inherent::{Pallet, Call, Storage, Inherent} = 21, + AuthorFilter: pallet_author_slot_filter::{Pallet, Call, Storage, Event, Config} = 22, + AuthorMapping: pallet_author_mapping::{Pallet, Call, Config, Storage, Event} = 23, + MoonbeamOrbiters: pallet_moonbeam_orbiters::{Pallet, Call, Storage, Event} = 24, + + // Handy utilities. + Utility: pallet_utility::{Pallet, Call, Event} = 30, + Proxy: pallet_proxy::{Pallet, Call, Storage, Event} = 31, + MaintenanceMode: pallet_maintenance_mode::{Pallet, Call, Config, Storage, Event} = 32, + Identity: pallet_identity::{Pallet, Call, Storage, Event} = 33, + Migrations: pallet_migrations::{Pallet, Storage, Config, Event} = 34, + ProxyGenesisCompanion: pallet_proxy_genesis_companion::{Pallet, Config} = 35, + Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 36, + MoonbeamLazyMigrations: pallet_moonbeam_lazy_migrations::{Pallet, Call, Storage} = 37, + + // Has been permanently removed for safety reasons. + // Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event} = 40, + + // Ethereum compatibility. + EthereumChainId: pallet_evm_chain_id::{Pallet, Storage, Config} = 50, + EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 51, + Ethereum: pallet_ethereum::{Pallet, Call, Storage, Event, Origin, Config} = 52, + + // Governance stuff. + Scheduler: pallet_scheduler::{Pallet, Storage, Event, Call} = 60, + // Democracy: 61, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event, HoldReason} = 62, + ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event} = 63, + Referenda: pallet_referenda::{Pallet, Call, Storage, Event} = 64, + Origins: governance::custom_origins::{Origin} = 65, + Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event} = 66, + + // Council stuff. + // CouncilCollective: 70 + // TechCommitteeCollective: 71 + TreasuryCouncilCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 72, + OpenTechCommitteeCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 73, + + // Treasury stuff. + Treasury: pallet_treasury::{Pallet, Storage, Config, Event, Call} = 80, + + // Crowdloan stuff. + CrowdloanRewards: pallet_crowdloan_rewards::{Pallet, Call, Config, Storage, Event} = 90, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Storage, Event} = 100, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 101, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 102, + PolkadotXcm: pallet_xcm::{Pallet, Storage, Call, Event, Origin, Config} = 103, + Assets: pallet_assets::{Pallet, Call, Storage, Event} = 104, + AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event} = 105, + XTokens: orml_xtokens::{Pallet, Call, Storage, Event} = 106, + XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event} = 107, + // Previously 108: pallet_assets:: + EthereumXcm: pallet_ethereum_xcm::{Pallet, Call, Storage, Origin} = 109, + Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 110, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 111, + RelayStorageRoots: pallet_relay_storage_roots::{Pallet, Storage} = 112, + PrecompileBenchmarks: pallet_precompile_benchmarks::{Pallet} = 113, + + // Randomness + Randomness: pallet_randomness::{Pallet, Call, Storage, Event, Inherent} = 120, + } +} + +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_support::parameter_types! { + pub const MaxBalance: crate::Balance = crate::Balance::max_value(); + } + + frame_benchmarking::define_benchmarks!( + [pallet_utility, Utility] + [pallet_timestamp, Timestamp] + [pallet_balances, Balances] + [pallet_evm, EVM] + [pallet_assets, Assets] + [pallet_parachain_staking, ParachainStaking] + [pallet_scheduler, Scheduler] + [pallet_treasury, Treasury] + [pallet_author_inherent, AuthorInherent] + [pallet_author_slot_filter, AuthorFilter] + [pallet_crowdloan_rewards, CrowdloanRewards] + [pallet_author_mapping, AuthorMapping] + [pallet_proxy, Proxy] + [pallet_identity, Identity] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_asset_manager, AssetManager] + [pallet_xcm_transactor, XcmTransactor] + [pallet_moonbeam_orbiters, MoonbeamOrbiters] + [pallet_randomness, Randomness] + [pallet_conviction_voting, ConvictionVoting] + [pallet_referenda, Referenda] + [pallet_preimage, Preimage] + [pallet_whitelist, Whitelist] + [pallet_multisig, Multisig] + [pallet_moonbeam_lazy_migrations, MoonbeamLazyMigrations] + [pallet_relay_storage_roots, RelayStorageRoots] + ); +} + +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + fp_self_contained::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = + fp_self_contained::CheckedExtrinsic; +/// Executive: handles dispatch to the various pallets. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +// All of our runtimes share most of their Runtime API implementations. +// We use a macro to implement this common part and add runtime-specific additional implementations. +// This macro expands to : +// ``` +// impl_runtime_apis! { +// // All impl blocks shared between all runtimes. +// +// // Specific impls provided to the `impl_runtime_apis_plus_common!` macro. +// } +// ``` +moonbeam_runtime_common::impl_runtime_apis_plus_common! { + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + xt: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + // Filtered calls should not enter the tx pool as they'll fail if inserted. + // If this call is not allowed, we return early. + if !::BaseCallFilter::contains(&xt.0.function) { + return InvalidTransaction::Call.into(); + } + + // This runtime uses Substrate's pallet transaction payment. This + // makes the chain feel like a standard Substrate chain when submitting + // frame transactions and using Substrate ecosystem tools. It has the downside that + // transaction are not prioritized by gas_price. The following code reprioritizes + // transactions to overcome this. + // + // A more elegant, ethereum-first solution is + // a pallet that replaces pallet transaction payment, and allows users + // to directly specify a gas price rather than computing an effective one. + // #HopefullySomeday + + // First we pass the transactions to the standard FRAME executive. This calculates all the + // necessary tags, longevity and other properties that we will leave unchanged. + // This also assigns some priority that we don't care about and will overwrite next. + let mut intermediate_valid = Executive::validate_transaction(source, xt.clone(), block_hash)?; + + let dispatch_info = xt.get_dispatch_info(); + + // If this is a pallet ethereum transaction, then its priority is already set + // according to gas price from pallet ethereum. If it is any other kind of transaction, + // we modify its priority. + Ok(match &xt.0.function { + RuntimeCall::Ethereum(transact { .. }) => intermediate_valid, + _ if dispatch_info.class != DispatchClass::Normal => intermediate_valid, + _ => { + let tip = match xt.0.signature { + None => 0, + Some((_, _, ref signed_extra)) => { + // Yuck, this depends on the index of charge transaction in Signed Extra + let charge_transaction = &signed_extra.7; + charge_transaction.tip() + } + }; + + // Calculate the fee that will be taken by pallet transaction payment + let fee: u64 = TransactionPayment::compute_fee( + xt.encode().len() as u32, + &dispatch_info, + tip, + ).saturated_into(); + + // Calculate how much gas this effectively uses according to the existing mapping + let effective_gas = + ::GasWeightMapping::weight_to_gas( + dispatch_info.weight + ); + + // Here we calculate an ethereum-style effective gas price using the + // current fee of the transaction. Because the weight -> gas conversion is + // lossy, we have to handle the case where a very low weight maps to zero gas. + let effective_gas_price = if effective_gas > 0 { + fee / effective_gas + } else { + // If the effective gas was zero, we just act like it was 1. + fee + }; + + // Overwrite the original prioritization with this ethereum one + intermediate_valid.priority = effective_gas_price; + intermediate_valid + } + }) + } + } + + impl async_backing_primitives::UnincludedSegmentApi for Runtime { + fn can_build_upon( + _included_hash: ::Hash, + _slot: async_backing_primitives::Slot, + ) -> bool { + // This runtime API can be called only when asynchronous backing is enabled client-side + // We return false here to force the client to not use async backing in moonbeam. + false + } + } +} + +struct CheckInherents; + +// Parity has decided to depreciate this trait, but does not offer a satisfactory replacement, +// see issue: https://github.com/paritytech/polkadot-sdk/issues/2841 +#[allow(deprecated)] +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + inherent_data.check_extrinsics(block) + } +} + +// Nimbus's Executive wrapper allows relay validators to verify the seal digest +cumulus_pallet_parachain_system::register_validate_block!( + Runtime = Runtime, + BlockExecutor = pallet_author_inherent::BlockExecutor::, + CheckInherents = CheckInherents, +); + +moonbeam_runtime_common::impl_self_contained_call!(); + +// Shorthand for a Get field of a pallet Config. +#[macro_export] +macro_rules! get { + ($pallet:ident, $name:ident, $type:ty) => { + <<$crate::Runtime as $pallet::Config>::$name as $crate::Get<$type>>::get() + }; +} + +#[cfg(test)] +mod tests { + use super::{currency::*, *}; + + #[test] + // Helps us to identify a Pallet Call in case it exceeds the 1kb limit. + // Hint: this should be a rare case. If that happens, one or more of the dispatchable arguments + // need to be Boxed. + fn call_max_size() { + const CALL_ALIGN: u32 = 1024; + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + } + + #[test] + fn currency_constants_are_correct() { + assert_eq!(SUPPLY_FACTOR, 100); + + // txn fees + assert_eq!(TRANSACTION_BYTE_FEE, Balance::from(100 * GIGAWEI)); + assert_eq!( + get!(pallet_transaction_payment, OperationalFeeMultiplier, u8), + 5_u8 + ); + assert_eq!(STORAGE_BYTE_FEE, Balance::from(10 * MILLIGLMR)); + + // treasury minimums + assert_eq!( + get!(pallet_treasury, ProposalBondMinimum, u128), + Balance::from(100 * GLMR) + ); + + // pallet_identity deposits + assert_eq!( + get!(pallet_identity, BasicDeposit, u128), + Balance::from(10 * GLMR + 2580 * MILLIGLMR) + ); + assert_eq!( + get!(pallet_identity, ByteDeposit, u128), + Balance::from(10 * MILLIGLMR) + ); + assert_eq!( + get!(pallet_identity, SubAccountDeposit, u128), + Balance::from(10 * GLMR + 530 * MILLIGLMR) + ); + + // staking minimums + assert_eq!( + get!(pallet_parachain_staking, MinCandidateStk, u128), + Balance::from(2_000_000 * GLMR) + ); + assert_eq!( + get!(pallet_parachain_staking, MinDelegation, u128), + Balance::from(50 * GLMR) + ); + + // crowdloan min reward + assert_eq!( + get!(pallet_crowdloan_rewards, MinimumReward, u128), + Balance::from(0u128) + ); + + // deposit for AuthorMapping + assert_eq!( + get!(pallet_author_mapping, DepositAmount, u128), + Balance::from(10 * KILOGLMR) + ); + + // proxy deposits + assert_eq!( + get!(pallet_proxy, ProxyDepositBase, u128), + Balance::from(10 * GLMR + 80 * MILLIGLMR) + ); + assert_eq!( + get!(pallet_proxy, ProxyDepositFactor, u128), + Balance::from(210 * MILLIGLMR) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositBase, u128), + Balance::from(10 * GLMR + 80 * MILLIGLMR) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositFactor, u128), + Balance::from(560 * MILLIGLMR) + ); + } + + #[test] + fn max_offline_rounds_lower_or_eq_than_reward_payment_delay() { + assert!( + get!(pallet_parachain_staking, MaxOfflineRounds, u32) + <= get!(pallet_parachain_staking, RewardPaymentDelay, u32) + ); + } + + #[test] + // Required migration is + // pallet_parachain_staking::migrations::IncreaseMaxTopDelegationsPerCandidate + // Purpose of this test is to remind of required migration if constant is ever changed + fn updating_maximum_delegators_per_candidate_requires_configuring_required_migration() { + assert_eq!( + get!(pallet_parachain_staking, MaxTopDelegationsPerCandidate, u32), + 300 + ); + assert_eq!( + get!( + pallet_parachain_staking, + MaxBottomDelegationsPerCandidate, + u32 + ), + 50 + ); + } + + #[test] + fn configured_base_extrinsic_weight_is_evm_compatible() { + let min_ethereum_transaction_weight = WeightPerGas::get() * 21_000; + let base_extrinsic = ::BlockWeights::get() + .get(frame_support::dispatch::DispatchClass::Normal) + .base_extrinsic; + assert!(base_extrinsic.ref_time() <= min_ethereum_transaction_weight.ref_time()); + } + + #[test] + fn test_storage_growth_ratio_is_correct() { + // This is the highest amount of new storage that can be created in a block 40 KB + let block_storage_limit = 40 * 1024; + let expected_storage_growth_ratio = BlockGasLimit::get() + .low_u64() + .saturating_div(block_storage_limit); + let actual_storage_growth_ratio = + ::GasLimitStorageGrowthRatio::get(); + assert_eq!( + expected_storage_growth_ratio, actual_storage_growth_ratio, + "Storage growth ratio is not correct" + ); + } +} diff --git a/tracing/2900/runtime/moonbeam/src/precompiles.rs b/tracing/2900/runtime/moonbeam/src/precompiles.rs new file mode 100644 index 00000000..d0098d0d --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/precompiles.rs @@ -0,0 +1,285 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use crate::{ + asset_config::ForeignAssetInstance, xcm_config::XcmExecutorConfig, OpenTechCommitteeInstance, + TreasuryCouncilInstance, +}; +use crate::{AssetId, H160}; +use frame_support::parameter_types; +use pallet_evm_precompile_author_mapping::AuthorMappingPrecompile; +use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata}; +use pallet_evm_precompile_batch::BatchPrecompile; +use pallet_evm_precompile_blake2::Blake2F; +use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; +use pallet_evm_precompile_call_permit::CallPermitPrecompile; +use pallet_evm_precompile_collective::CollectivePrecompile; +use pallet_evm_precompile_conviction_voting::ConvictionVotingPrecompile; +use pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompile; +use pallet_evm_precompile_gmp::GmpPrecompile; +use pallet_evm_precompile_identity::IdentityPrecompile; +use pallet_evm_precompile_modexp::Modexp; +use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile; +use pallet_evm_precompile_preimage::PreimagePrecompile; +use pallet_evm_precompile_proxy::{OnlyIsProxyAndProxy, ProxyPrecompile}; +use pallet_evm_precompile_randomness::RandomnessPrecompile; +use pallet_evm_precompile_referenda::ReferendaPrecompile; +use pallet_evm_precompile_registry::PrecompileRegistry; +use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile; +use pallet_evm_precompile_relay_verifier::RelayDataVerifierPrecompile; +use pallet_evm_precompile_sha3fips::Sha3FIPS256; +use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use pallet_evm_precompile_xcm_transactor::{ + v1::XcmTransactorPrecompileV1, v2::XcmTransactorPrecompileV2, v3::XcmTransactorPrecompileV3, +}; +use pallet_evm_precompile_xcm_utils::XcmUtilsPrecompile; +use pallet_evm_precompile_xtokens::XtokensPrecompile; +use pallet_evm_precompileset_assets_erc20::{AccountIdAssetIdConversion, Erc20AssetsPrecompileSet}; +use precompile_utils::precompile_set::*; +use sp_std::prelude::*; + +pub struct NativeErc20Metadata; + +/// ERC20 metadata for the native token. +impl Erc20Metadata for NativeErc20Metadata { + /// Returns the name of the token. + fn name() -> &'static str { + "GLMR token" + } + + /// Returns the symbol of the token. + fn symbol() -> &'static str { + "GLMR" + } + + /// Returns the decimals places of the token. + fn decimals() -> u8 { + 18 + } + + /// Must return `true` only if it represents the main native currency of + /// the network. It must be the currency used in `pallet_evm`. + fn is_native_currency() -> bool { + true + } +} + +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as foreign +pub const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8; 4]; +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as local +pub const LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8, 255u8, 255u8, 254u8]; + +parameter_types! { + pub ForeignAssetPrefix: &'static [u8] = FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX; + pub LocalAssetPrefix: &'static [u8] = LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX; +} + +type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile); + +#[precompile_utils::precompile_name_from_address] +type MoonbeamPrecompilesAt = ( + // Ethereum precompiles: + // We allow DELEGATECALL to stay compliant with Ethereum behavior. + PrecompileAt, ECRecover, EthereumPrecompilesChecks>, + PrecompileAt, Sha256, EthereumPrecompilesChecks>, + PrecompileAt, Ripemd160, EthereumPrecompilesChecks>, + PrecompileAt, Identity, EthereumPrecompilesChecks>, + PrecompileAt, Modexp, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Add, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>, + PrecompileAt, Blake2F, EthereumPrecompilesChecks>, + // Non-Moonbeam specific nor Ethereum precompiles : + PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>, + RemovedPrecompileAt>, // Dispatch + PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>, + // Moonbeam specific precompiles: + PrecompileAt< + AddressU64<2048>, + ParachainStakingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2049>, + CrowdloanRewardsPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2050>, + Erc20BalancesPrecompile, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompileAt>, // Democracy + PrecompileAt< + AddressU64<2052>, + XtokensPrecompile, + ( + SubcallWithMaxNesting<1>, + CallableByContract, + CallableByPrecompile, + ), + >, + PrecompileAt< + AddressU64<2053>, + RelayEncoderPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2054>, + XcmTransactorPrecompileV1, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2055>, + AuthorMappingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2056>, + BatchPrecompile, + ( + SubcallWithMaxNesting<2>, + // Batch is the only precompile allowed to call Batch. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2057>, + RandomnessPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2058>, + CallPermitPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2059>, + ProxyPrecompile, + ( + CallableByContract>, + SubcallWithMaxNesting<0>, + // Batch is the only precompile allowed to call Proxy. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2060>, + XcmUtilsPrecompile, + CallableByContract< + pallet_evm_precompile_xcm_utils::AllExceptXcmExecute, + >, + >, + PrecompileAt< + AddressU64<2061>, + XcmTransactorPrecompileV2, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompileAt>, //CouncilInstance + RemovedPrecompileAt>, // TechCommitteeInstance + PrecompileAt< + AddressU64<2064>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2065>, + ReferendaPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2066>, + ConvictionVotingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2067>, + PreimagePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2068>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2069>, + PrecompileRegistry, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt, GmpPrecompile, SubcallWithMaxNesting<0>>, + PrecompileAt< + AddressU64<2071>, + XcmTransactorPrecompileV3, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2072>, + IdentityPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2073>, + RelayDataVerifierPrecompile, + (CallableByContract, CallableByPrecompile), + >, +); + +pub struct DisabledLocalAssets(sp_std::marker::PhantomData); + +impl sp_core::Get> for DisabledLocalAssets +where + Runtime: frame_system::Config, + Runtime::AccountId: Into, + Runtime: AccountIdAssetIdConversion, +{ + fn get() -> Vec { + vec![ + 337110116006454532607322340792629567158u128, + 278750993613512357835566279094880339619, + 228256396637196286254896220398224702687, + 270195117769614861929703564202131636628, + ] + .iter() + .map(|id| Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, *id).into()) + .collect() + } +} + +/// The PrecompileSet installed in the Moonbeam runtime. +/// We include the nine Istanbul precompiles +/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69) +/// as well as a special precompile for dispatching Substrate extrinsics +/// The following distribution has been decided for the precompiles +/// 0-1023: Ethereum Mainnet Precompiles +/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither Moonbeam specific +/// 2048-4095 Moonbeam specific precompiles +pub type MoonbeamPrecompiles = PrecompileSetBuilder< + R, + ( + // Skip precompiles if out of range. + PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), MoonbeamPrecompilesAt>, + // Prefixed precompile sets (XC20) + PrecompileSetStartingWith< + ForeignAssetPrefix, + Erc20AssetsPrecompileSet, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompilesAt>, + ), +>; diff --git a/tracing/2900/runtime/moonbeam/src/xcm_config.rs b/tracing/2900/runtime/moonbeam/src/xcm_config.rs new file mode 100644 index 00000000..7042ac8f --- /dev/null +++ b/tracing/2900/runtime/moonbeam/src/xcm_config.rs @@ -0,0 +1,709 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM configuration for Moonbase. +//! + +use super::{ + governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, Erc20XcmBridge, + MaintenanceMode, MessageQueue, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, + RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, +}; + +use frame_support::{ + parameter_types, + traits::{EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin}, +}; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; +use sp_runtime::{ + traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf}, + DispatchErrorWithPostInfo, +}; +use sp_weights::Weight; + +use frame_system::{EnsureRoot, RawOrigin}; +use sp_core::{ConstU32, H160, H256}; + +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, HashedDescription, + NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, +}; + +use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; +use xcm::latest::prelude::{ + Asset, GlobalConsensus, InteriorLocation, Junction, Location, NetworkId, PalletInstance, + Parachain, +}; +use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry}; + +use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use orml_xcm_support::MultiNativeAsset; +use xcm_primitives::{ + AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType, + FirstAssetTrader, SignedToAccountId20, UtilityAvailableCalls, UtilityEncodeCall, XcmTransact, +}; + +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use sp_core::Get; +use sp_std::{ + convert::{From, Into, TryFrom}, + prelude::*, +}; + +use orml_traits::parameter_type_with_key; + +use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot}; + +parameter_types! { + // The network Id of the relay + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + // The relay chain Origin type + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); + // Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a Location: (Self Balances pallet index) + // We use the RELATIVE multilocation + pub SelfReserve: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a Location of type AccountKey20, just generate a native account + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + HashedDescription>, +); + +/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`. +pub struct LocationToH160; +impl ConvertLocation for LocationToH160 { + fn convert_location(location: &Location) -> Option { + >::convert_location(location) + .map(Into::into) + } +} + +// The non-reserve fungible transactor type +// It will use pallet-assets, and the Id will be matched against AsAssetType +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + super::Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId20 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +/// The transactor for our own chain currency. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + xcm_builder::IsConcrete, + // We can convert the MultiLocations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// We use all transactors +pub type AssetTransactors = ( + LocalAssetTransactor, + ForeignFungiblesTransactor, + Erc20XcmBridge, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + // Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte- + // account local origin + SignedAccountKey20AsNative, +); + +parameter_types! { + /// The amount of weight an XCM operation takes. This is safe overestimate. + pub UnitWeightCost: Weight = Weight::from_parts(200_000_000u64, 0); + /// Maximum number of instructions in a single XCM fragment. A sanity check against + /// weight caculations getting too crazy. + pub MaxInstructions: u32 = 100; +} + +/// Xcm Weigher shared between multiple Xcm-related configs. +pub type XcmWeigher = WeightInfoBounds< + moonbeam_xcm_benchmarks::weights::XcmWeight, + RuntimeCall, + MaxInstructions, +>; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +/// We do not burn anything because we want to mimic exactly what +/// the sovereign account has +pub type XcmFeesToAccount = xcm_primitives::XcmFeesToAccount< + super::Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +pub struct SafeCallFilter; +impl frame_support::traits::Contains for SafeCallFilter { + fn contains(_call: &RuntimeCall) -> bool { + // TODO review + // This needs to be addressed at EVM level + true + } +} + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS; +} + +// Our implementation of the Moonbeam Call +// Attachs the right origin in case the call is made to pallet-ethereum-xcm +#[cfg(not(feature = "evm-tracing"))] +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +#[cfg(feature = "evm-tracing")] +moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!(); + +moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); + +pub struct XcmExecutorConfig; +impl xcm_executor::Config for XcmExecutorConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + // Filter to the reserve withdraw operations + // Whenever the reserve matches the relative or absolute value + // of our chain, we always return the relative reserve + type IsReserve = MultiNativeAsset>; + type IsTeleporter = (); // No teleport + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = XcmWeigher; + // We use two traders + // When we receive the relative representation of the self-reserve asset, + // we use UsingComponents and the local way of handling fees + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + UsingComponents< + ::WeightToFee, + SelfReserve, + AccountId, + Balances, + DealWithFees, + >, + FirstAssetTrader, + ); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type PalletInstancesInfo = crate::AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = SafeCallFilter; + type Aliasers = Nothing; + type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor; +} + +type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper< + XcmExecutorConfig, + xcm_executor::XcmExecutor, +>; + +// Converts a Signed Local Origin into a Location +pub type LocalOriginToLocation = SignedToAccountId20; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // TODO pallet-xcm weights + type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo; + type AdminOrigin = EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = sp_core::ConstU32<1_000>; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery< + cumulus_primitives_core::ParaId, + >; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +// TODO: This pallet can be removed after the lazy migration is done and +// event `Completed` is emitted. +// https://github.com/paritytech/polkadot-sdk/pull/1246 +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DmpSink = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_dmp_queue::weights::SubstrateWeight; +} + +parameter_types! { + /// The amount of weight (if any) which should be provided to the message queue for + /// servicing enqueued items. + /// + /// This may be legitimately `None` in the case that you will call + /// `ServiceQueues::service_queues` manually. + pub MessageQueueServiceWeight: Weight = + Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block; + /// The maximum number of stale pages (i.e. of overweight messages) allowed before culling + /// can happen. Once there are more stale pages than this, then historical pages may be + /// dropped, even if they contain unprocessed overweight messages. + pub const MessageQueueMaxStale: u32 = 8; + /// The size of the page; this implies the maximum message size which can be sent. + /// + /// A good value depends on the expected message sizes, their weights, the weight that is + /// available for processing them and the maximal needed message size. The maximal message + /// size is slightly lower than this as defined by [`MaxMessageLenOf`]. + pub const MessageQueueHeapSize: u32 = 128 * 1048; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = + xcm_builder::ProcessXcmMessage; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + // NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type WeightInfo = pallet_message_queue::weights::SubstrateWeight; +} + +// Our AssetType. For now we only handle Xcm Assets +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum AssetType { + Xcm(xcm::v3::Location), +} +impl Default for AssetType { + fn default() -> Self { + Self::Xcm(xcm::v3::Location::here()) + } +} + +impl From for AssetType { + fn from(location: xcm::v3::Location) -> Self { + Self::Xcm(location) + } +} + +// This can be removed once we fully adopt xcm::v4 everywhere +impl TryFrom for AssetType { + type Error = (); + fn try_from(location: Location) -> Result { + Ok(Self::Xcm(location.try_into()?)) + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => Some(location), + } + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => xcm_builder::V4V3LocationConverter::convert_back(&location), + } + } +} + +// Implementation on how to retrieve the AssetId from an AssetType +// We simply hash the AssetType and take the lowest 128 bits +impl From for AssetId { + fn from(asset: AssetType) -> AssetId { + match asset { + AssetType::Xcm(id) => { + let mut result: [u8; 16] = [0u8; 16]; + let hash: H256 = id.using_encoded(::Hashing::hash); + result.copy_from_slice(&hash.as_fixed_bytes()[0..16]); + u128::from_le_bytes(result) + } + } + } +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + // Our native token + SelfReserve, + // Assets representing other chains native tokens + ForeignAsset(AssetId), + // Erc20 token + Erc20 { contract_address: H160 }, +} + +impl AccountIdToCurrencyId for Runtime { + fn account_to_currency_id(account: AccountId) -> Option { + Some(match account { + // the self-reserve currency is identified by the pallet-balances address + a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve, + // the rest of the currencies, by their corresponding erc20 address + _ => match Runtime::account_to_asset_id(account) { + // We distinguish by prefix, and depending on it we create either + // Foreign or Local + Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id), + // If no known prefix is identified, we consider that it's a "real" erc20 token + // (i.e. managed by a real smart contract) + None => CurrencyId::Erc20 { + contract_address: account.into(), + }, + }, + }) + } +} +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: sp_runtime::traits::MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + CurrencyId::SelfReserve => { + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + CurrencyId::Erc20 { contract_address } => { + let mut location = Erc20XcmBridgePalletLocation::get(); + location + .push_interior(Junction::AccountKey20 { + key: contract_address.0, + network: None, + }) + .ok(); + Some(location) + } + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0); + pub const MaxAssetsForTransfer: usize = 2; + + // This is how we are going to detect whether the asset is a Reserve asset + // This however is the chain part only + pub SelfLocation: Location = Location::here(); + // We need this to be able to catch when someone is trying to execute a non- + // cross-chain transfer in xtokens through the absolute path way + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(ParachainInfo::parachain_id().into()) + ].into() + }; +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + // Polkadot AssetHub fee + (1, Some(Parachain(1000u32))) => Some(50_000_000u128), + _ => None, + } + }; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdConvert = CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = AbsoluteAndRelativeReserve; +} + +// 1 DOT should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +// For now we only allow to transact in the relay, although this might change in the future +// Transactors just defines the chains in which we allow transactions to be issued through +// xcm +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum Transactors { + Relay, +} + +// Default for benchmarking +#[cfg(feature = "runtime-benchmarks")] +impl Default for Transactors { + fn default() -> Self { + Transactors::Relay + } +} + +impl TryFrom for Transactors { + type Error = (); + fn try_from(value: u8) -> Result { + match value { + 0u8 => Ok(Transactors::Relay), + _ => Err(()), + } + } +} + +impl UtilityEncodeCall for Transactors { + fn encode_call(self, call: UtilityAvailableCalls) -> Vec { + match self { + Transactors::Relay => pallet_xcm_transactor::Pallet::::encode_call( + pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::), + call, + ), + } + } +} + +impl XcmTransact for Transactors { + fn destination(self) -> Location { + match self { + Transactors::Relay => Location::parent(), + } + } +} + +pub type DerivativeAddressRegistrationOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = Transactors; + type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin; + type SovereignAccountDispatcherOrigin = EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdToLocation = CurrencyIdToLocation>; + type XcmSender = XcmRouter; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = AbsoluteAndRelativeReserve; + type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo; + type HrmpManipulatorOrigin = GeneralAdminOrRoot; + type HrmpOpenOrigin = FastGeneralAdminOrRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + // This is the relative view of erc20 assets. + // Identified by this prefix + AccountKey20(contractAddress) + // We use the RELATIVE multilocation + pub Erc20XcmBridgePalletLocation: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + + // To be able to support almost all erc20 implementations, + // we provide a sufficiently hight gas limit. + pub Erc20XcmBridgeTransferGasLimit: u64 = 200_000; +} + +impl pallet_erc20_xcm_bridge::Config for Runtime { + type AccountIdConverter = LocationToH160; + type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation; + type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit; + type EvmRunner = EvmRunnerPrecompileOrEthXcm; +} + +#[cfg(feature = "runtime-benchmarks")] +mod testing { + use super::*; + use xcm_builder::V4V3LocationConverter; + + /// This From exists for benchmarking purposes. It has the potential side-effect of calling + /// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code. + impl From for CurrencyId { + fn from(location: Location) -> CurrencyId { + use xcm_primitives::AssetTypeGetter; + + // If it does not exist, for benchmarking purposes, we create the association + let asset_id = if let Some(asset_id) = + AsAssetType::::convert_location(&location) + { + asset_id + } else { + let asset_type = AssetType::Xcm( + V4V3LocationConverter::convert(&location).expect("convert to v3"), + ); + let asset_id: AssetId = asset_type.clone().into(); + AssetManager::set_asset_type_asset_id(asset_type, asset_id); + asset_id + }; + + CurrencyId::ForeignAsset(asset_id) + } + } +} diff --git a/tracing/2900/runtime/moonbeam/tests/common/mod.rs b/tracing/2900/runtime/moonbeam/tests/common/mod.rs new file mode 100644 index 00000000..9b382cf4 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/common/mod.rs @@ -0,0 +1,373 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![allow(dead_code)] + +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use fp_evm::GenesisAccount; +use frame_support::{ + assert_ok, + traits::{OnFinalize, OnInitialize}, +}; +pub use moonbeam_runtime::{ + asset_config::AssetRegistrarMetadata, + currency::{GIGAWEI, GLMR, SUPPLY_FACTOR, WEI}, + xcm_config::AssetType, + AccountId, AssetId, AssetManager, AuthorInherent, Balance, Balances, CrowdloanRewards, + Ethereum, Executive, Header, InflationInfo, ParachainStaking, Range, Runtime, RuntimeCall, + RuntimeEvent, System, TransactionConverter, TransactionPaymentAsGasPrice, UncheckedExtrinsic, + HOURS, WEEKS, +}; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use sp_core::{Encode, H160}; +use sp_runtime::{traits::Dispatchable, BuildStorage, Digest, DigestItem, Perbill, Percent}; + +use std::collections::BTreeMap; + +use fp_rpc::ConvertTransaction; + +// A valid signed Alice transfer. +pub const VALID_ETH_TX: &str = + "02f869820501808085e8d4a51000825208943cd0a705a2dc65e5b1e1205896baa2be8a07c6e00180c\ + 001a061087911e877a5802142a89a40d231d50913db399eb50839bb2d04e612b22ec8a01aa313efdf2\ + 793bea76da6813bda611444af16a6207a8cfef2d9c8aa8f8012f7"; + +// An invalid signed Alice transfer with a gas limit artifically set to 0. +pub const INVALID_ETH_TX: &str = + "f8628085174876e800809412cb274aad8251c875c0bf6872b67d9983e53fdd01801ba011110796057\ + 0e2d49fcc2afbc582e1abd3eeb027242b92abcebcec7cdefab63ea001732f6fac84acdd5b096554230\ + 75003e7f07430652c3d6722e18f50b3d34e29"; + +pub fn rpc_run_to_block(n: u32) { + while System::block_number() < n { + Ethereum::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Ethereum::on_initialize(System::block_number()); + } +} + +/// Utility function that advances the chain to the desired block number. +/// If an author is provided, that author information is injected to all the blocks in the meantime. +pub fn run_to_block(n: u32, author: Option) { + // Finalize the first block + Ethereum::on_finalize(System::block_number()); + while System::block_number() < n { + // Set the new block number and author + match author { + Some(ref author) => { + let pre_digest = Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + }; + System::reset_events(); + System::initialize( + &(System::block_number() + 1), + &System::parent_hash(), + &pre_digest, + ); + } + None => { + System::set_block_number(System::block_number() + 1); + } + } + + // Initialize the new block + AuthorInherent::on_initialize(System::block_number()); + ParachainStaking::on_initialize(System::block_number()); + Ethereum::on_initialize(System::block_number()); + + // Finalize the block + Ethereum::on_finalize(System::block_number()); + ParachainStaking::on_finalize(System::block_number()); + } +} + +pub fn last_event() -> RuntimeEvent { + System::events().pop().expect("Event expected").event +} + +// Helper function to give a simple evm context suitable for tests. +// We can remove this once https://github.com/rust-blockchain/evm/pull/35 +// is in our dependency graph. +pub fn evm_test_context() -> fp_evm::Context { + fp_evm::Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + } +} + +// Test struct with the purpose of initializing xcm assets +#[derive(Clone)] +pub struct XcmAssetInitialization { + pub asset_type: AssetType, + pub metadata: AssetRegistrarMetadata, + pub balances: Vec<(AccountId, Balance)>, + pub is_sufficient: bool, +} + +pub struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, + // [collator, amount] + collators: Vec<(AccountId, Balance)>, + // [delegator, collator, nomination_amount] + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + // per-round inflation config + inflation: InflationInfo, + // AuthorId -> AccountId mappings + mappings: Vec<(NimbusId, AccountId)>, + // Crowdloan fund + crowdloan_fund: Balance, + // Chain id + chain_id: u64, + // EVM genesis accounts + evm_accounts: BTreeMap, + // [assettype, metadata, Vec, is_sufficient] + xcm_assets: Vec, + safe_xcm_version: Option, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![], + delegations: vec![], + collators: vec![], + inflation: InflationInfo { + expect: Range { + min: 100_000 * GLMR, + ideal: 200_000 * GLMR, + max: 500_000 * GLMR, + }, + // not used + annual: Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50), + }, + // unrealistically high parameterization, only for testing + round: Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }, + }, + mappings: vec![], + crowdloan_fund: 0, + chain_id: CHAIN_ID, + evm_accounts: BTreeMap::new(), + xcm_assets: vec![], + safe_xcm_version: None, + } + } +} + +impl ExtBuilder { + pub fn with_evm_accounts(mut self, accounts: BTreeMap) -> Self { + self.evm_accounts = accounts; + self + } + + pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self { + self.collators = collators; + self + } + + pub fn with_delegations(mut self, delegations: Vec<(AccountId, AccountId, Balance)>) -> Self { + self.delegations = delegations + .into_iter() + .map(|d| (d.0, d.1, d.2, Percent::zero())) + .collect(); + self + } + + pub fn with_crowdloan_fund(mut self, crowdloan_fund: Balance) -> Self { + self.crowdloan_fund = crowdloan_fund; + self + } + + pub fn with_mappings(mut self, mappings: Vec<(NimbusId, AccountId)>) -> Self { + self.mappings = mappings; + self + } + + pub fn with_xcm_assets(mut self, xcm_assets: Vec) -> Self { + self.xcm_assets = xcm_assets; + self + } + + pub fn with_safe_xcm_version(mut self, safe_xcm_version: u32) -> Self { + self.safe_xcm_version = Some(safe_xcm_version); + self + } + + #[allow(dead_code)] + pub fn with_inflation(mut self, inflation: InflationInfo) -> Self { + self.inflation = inflation; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_parachain_staking::GenesisConfig:: { + candidates: self.collators, + delegations: self.delegations, + inflation_config: self.inflation, + collator_commission: Perbill::from_percent(20), + parachain_bond_reserve_percent: Percent::from_percent(30), + blocks_per_round: 6 * HOURS, + num_selected_candidates: 8, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_crowdloan_rewards::GenesisConfig:: { + funded_amount: self.crowdloan_fund, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_author_mapping::GenesisConfig:: { + mappings: self.mappings, + } + .assimilate_storage(&mut t) + .unwrap(); + + let genesis_config = pallet_evm_chain_id::GenesisConfig:: { + chain_id: self.chain_id, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: self.evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_ethereum::GenesisConfig:: { + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_xcm::GenesisConfig:: { + safe_xcm_version: self.safe_xcm_version, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + let xcm_assets = self.xcm_assets.clone(); + ext.execute_with(|| { + // If any xcm assets specified, we register them here + for xcm_asset_initialization in xcm_assets { + let asset_id: AssetId = xcm_asset_initialization.asset_type.clone().into(); + AssetManager::register_foreign_asset( + root_origin(), + xcm_asset_initialization.asset_type, + xcm_asset_initialization.metadata, + 1, + xcm_asset_initialization.is_sufficient, + ) + .unwrap(); + for (account, balance) in xcm_asset_initialization.balances { + moonbeam_runtime::Assets::mint( + origin_of(AssetManager::account_id()), + asset_id.into(), + account, + balance, + ) + .unwrap(); + } + } + System::set_block_number(1); + }); + ext + } +} + +pub const CHAIN_ID: u64 = 1281; +pub const ALICE: [u8; 20] = [4u8; 20]; +pub const ALICE_NIMBUS: [u8; 32] = [4u8; 32]; +pub const BOB: [u8; 20] = [5u8; 20]; +pub const CHARLIE: [u8; 20] = [6u8; 20]; +pub const DAVE: [u8; 20] = [7u8; 20]; +pub const EVM_CONTRACT: [u8; 20] = [8u8; 20]; + +pub fn origin_of(account_id: AccountId) -> ::RuntimeOrigin { + ::RuntimeOrigin::signed(account_id) +} + +pub fn inherent_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::none() +} + +pub fn root_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::root() +} + +/// Mock the inherent that sets validation data in ParachainSystem, which +/// contains the `relay_chain_block_number`, which is used in `author-filter` as a +/// source of randomness to filter valid authors at each block. +pub fn set_parachain_inherent_data() { + use cumulus_primitives_core::PersistedValidationData; + use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; + let (relay_parent_storage_root, relay_chain_state) = + RelayStateSproofBuilder::default().into_state_root_and_proof(); + let vfp = PersistedValidationData { + relay_parent_number: 1u32, + relay_parent_storage_root, + ..Default::default() + }; + let parachain_inherent_data = ParachainInherentData { + validation_data: vfp, + relay_chain_state: relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + assert_ok!(RuntimeCall::ParachainSystem( + cumulus_pallet_parachain_system::Call::::set_validation_data { + data: parachain_inherent_data + } + ) + .dispatch(inherent_origin())); +} + +pub fn unchecked_eth_tx(raw_hex_tx: &str) -> UncheckedExtrinsic { + let converter = TransactionConverter; + converter.convert_transaction(ethereum_transaction(raw_hex_tx)) +} + +pub fn ethereum_transaction(raw_hex_tx: &str) -> pallet_ethereum::Transaction { + let bytes = hex::decode(raw_hex_tx).expect("Transaction bytes."); + let transaction = ethereum::EnvelopedDecodable::decode(&bytes[..]); + assert!(transaction.is_ok()); + transaction.unwrap() +} diff --git a/tracing/2900/runtime/moonbeam/tests/evm_tracing.rs b/tracing/2900/runtime/moonbeam/tests/evm_tracing.rs new file mode 100644 index 00000000..144dae00 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/evm_tracing.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbeam EVM tracing Integration Tests + +mod common; + +#[cfg(test)] +#[cfg(feature = "evm-tracing")] +mod tests { + use super::common::*; + + use pallet_evm::AddressMapping; + use sp_core::H160; + + use moonbeam_rpc_primitives_debug::runtime_decl_for_debug_runtime_api::DebugRuntimeApi; + use std::str::FromStr; + + #[test] + fn debug_runtime_api_trace_transaction() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * GLMR), + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * GLMR, + } + .into(), + ); + let transaction = ethereum_transaction(VALID_ETH_TX); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_transaction( + vec![non_eth_uxt.clone(), eth_uxt, non_eth_uxt.clone()], + &transaction, + &block + ) + .is_ok()); + }); + } + + #[test] + fn debug_runtime_api_trace_block() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * GLMR), + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * GLMR, + } + .into(), + ); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let eth_tx = ethereum_transaction(VALID_ETH_TX); + let eth_extrinsic_hash = eth_tx.hash(); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_block( + vec![non_eth_uxt.clone(), eth_uxt.clone(), non_eth_uxt, eth_uxt], + vec![eth_extrinsic_hash, eth_extrinsic_hash], + &block + ) + .is_ok()); + }); + } +} diff --git a/tracing/2900/runtime/moonbeam/tests/integration_test.rs b/tracing/2900/runtime/moonbeam/tests/integration_test.rs new file mode 100644 index 00000000..127663cc --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/integration_test.rs @@ -0,0 +1,2762 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbeam Runtime Integration Tests + +#![cfg(test)] + +mod common; +use common::*; + +use fp_evm::{Context, IsPrecompileResult}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchClass, + traits::{ + fungible::Inspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo, + StorageInfoTrait, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + StorageHasher, Twox128, +}; +use moonbeam_runtime::{ + asset_config::ForeignAssetInstance, + currency::GLMR, + xcm_config::{CurrencyId, SelfReserve}, + AccountId, Balances, CrowdloanRewards, OpenTechCommitteeCollective, ParachainStaking, + PolkadotXcm, Precompiles, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, System, + TransactionPayment, TreasuryCouncilCollective, XTokens, XcmTransactor, + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; +use moonbeam_xcm_benchmarks::weights::XcmWeight; +use nimbus_primitives::NimbusId; +use pallet_evm::PrecompileSet; +use pallet_evm_precompileset_assets_erc20::{ + AccountIdAssetIdConversion, SELECTOR_LOG_APPROVAL, SELECTOR_LOG_TRANSFER, +}; +use pallet_transaction_payment::Multiplier; +use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights}; +use parity_scale_codec::Encode; +use polkadot_parachain::primitives::Sibling; +use precompile_utils::{ + precompile_set::{is_precompile_or_fail, IsActivePrecompile}, + prelude::*, + testing::*, +}; +use sha3::{Digest, Keccak256}; +use sp_core::{ByteArray, Pair, H160, U256}; +use sp_runtime::{ + traits::{Convert, Dispatchable}, + BuildStorage, DispatchError, ModuleError, +}; +use std::str::from_utf8; +use xcm::latest::prelude::*; +use xcm::{VersionedAsset, VersionedAssets, VersionedLocation}; +use xcm_builder::{ParentIsPreset, SiblingParachainConvertsVia}; +use xcm_executor::traits::ConvertLocation; + +type BatchPCall = pallet_evm_precompile_batch::BatchPrecompileCall; +type CrowdloanRewardsPCall = + pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompileCall; +type XcmUtilsPCall = pallet_evm_precompile_xcm_utils::XcmUtilsPrecompileCall< + Runtime, + moonbeam_runtime::xcm_config::XcmExecutorConfig, +>; +type XtokensPCall = pallet_evm_precompile_xtokens::XtokensPrecompileCall; +type ForeignAssetsPCall = pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSetCall< + Runtime, + ForeignAssetInstance, +>; +type XcmTransactorV2PCall = + pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call; + +const BASE_FEE_GENESIS: u128 = 10000 * GIGAWEI; + +#[test] +fn xcmp_queue_controller_origin_is_root() { + // important for the XcmExecutionManager impl of PauseExecution which uses root origin + // to suspend/resume XCM execution in xcmp_queue::on_idle + assert_ok!( + ::ControllerOrigin::ensure_origin(root_origin()) + ); +} + +#[test] +fn verify_pallet_prefixes() { + fn is_pallet_prefix(name: &str) { + // Compares the unhashed pallet prefix in the `StorageInstance` implementation by every + // storage item in the pallet P. This pallet prefix is used in conjunction with the + // item name to get the unique storage key: hash(PalletPrefix) + hash(StorageName) + // https://github.com/paritytech/substrate/blob/master/frame/support/procedural/src/pallet/ + // expand/storage.rs#L389-L401 + assert_eq!( + ::PalletInfo::name::

(), + Some(name) + ); + } + // TODO: use StorageInfoTrait once https://github.com/paritytech/substrate/pull/9246 + // is pulled in substrate deps. + is_pallet_prefix::("System"); + is_pallet_prefix::("Utility"); + is_pallet_prefix::("ParachainSystem"); + is_pallet_prefix::("TransactionPayment"); + is_pallet_prefix::("ParachainInfo"); + is_pallet_prefix::("EthereumChainId"); + is_pallet_prefix::("EVM"); + is_pallet_prefix::("Ethereum"); + is_pallet_prefix::("ParachainStaking"); + is_pallet_prefix::("Scheduler"); + is_pallet_prefix::( + "OpenTechCommitteeCollective", + ); + is_pallet_prefix::("Treasury"); + is_pallet_prefix::("AuthorInherent"); + is_pallet_prefix::("AuthorFilter"); + is_pallet_prefix::("CrowdloanRewards"); + is_pallet_prefix::("AuthorMapping"); + is_pallet_prefix::("MaintenanceMode"); + is_pallet_prefix::("Identity"); + is_pallet_prefix::("XcmpQueue"); + is_pallet_prefix::("CumulusXcm"); + is_pallet_prefix::("DmpQueue"); + is_pallet_prefix::("PolkadotXcm"); + is_pallet_prefix::("Assets"); + is_pallet_prefix::("XTokens"); + is_pallet_prefix::("AssetManager"); + is_pallet_prefix::("Migrations"); + is_pallet_prefix::("XcmTransactor"); + is_pallet_prefix::("ProxyGenesisCompanion"); + is_pallet_prefix::("MoonbeamOrbiters"); + is_pallet_prefix::("TreasuryCouncilCollective"); + is_pallet_prefix::("MoonbeamLazyMigrations"); + is_pallet_prefix::("RelayStorageRoots"); + + let prefix = |pallet_name, storage_name| { + let mut res = [0u8; 32]; + res[0..16].copy_from_slice(&Twox128::hash(pallet_name)); + res[16..32].copy_from_slice(&Twox128::hash(storage_name)); + res.to_vec() + }; + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"Now".to_vec(), + prefix: prefix(b"Timestamp", b"Now"), + max_values: Some(1), + max_size: Some(8), + }, + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"DidUpdate".to_vec(), + prefix: prefix(b"Timestamp", b"DidUpdate"), + max_values: Some(1), + max_size: Some(1), + } + ] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"TotalIssuance".to_vec(), + prefix: prefix(b"Balances", b"TotalIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"InactiveIssuance".to_vec(), + prefix: prefix(b"Balances", b"InactiveIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Account".to_vec(), + prefix: prefix(b"Balances", b"Account"), + max_values: None, + max_size: Some(100), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Locks".to_vec(), + prefix: prefix(b"Balances", b"Locks"), + max_values: None, + max_size: Some(1287), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Reserves".to_vec(), + prefix: prefix(b"Balances", b"Reserves"), + max_values: None, + max_size: Some(1037), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Holds".to_vec(), + prefix: prefix(b"Balances", b"Holds"), + max_values: None, + max_size: Some(55), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Freezes".to_vec(), + prefix: prefix(b"Balances", b"Freezes"), + max_values: None, + max_size: Some(37), + }, + ] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Proxies".to_vec(), + prefix: prefix(b"Proxy", b"Proxies"), + max_values: None, + max_size: Some(845), + }, + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Announcements".to_vec(), + prefix: prefix(b"Proxy", b"Announcements"), + max_values: None, + max_size: Some(1837), + } + ] + ); + assert_eq!( + ::storage_info(), + vec![StorageInfo { + pallet_name: b"MaintenanceMode".to_vec(), + storage_name: b"MaintenanceMode".to_vec(), + prefix: prefix(b"MaintenanceMode", b"MaintenanceMode"), + max_values: Some(1), + max_size: None, + },] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRoot".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRoot"), + max_values: None, + max_size: Some(44), + }, + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRootKeys".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRootKeys"), + max_values: Some(1), + max_size: Some(121), + }, + ] + ); +} + +#[test] +fn test_collectives_storage_item_prefixes() { + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"TreasuryCouncilCollective".to_vec()); + } + + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"OpenTechCommitteeCollective".to_vec()); + } +} + +#[test] +fn collective_set_members_root_origin_works() { + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert_ok!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + // OpenTechCommitteeCollective + assert_ok!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + }); +} + +#[test] +fn collective_set_members_general_admin_origin_works() { + use moonbeam_runtime::{ + governance::custom_origins::Origin as CustomOrigin, OriginCaller, Utility, + }; + + ExtBuilder::default().build().execute_with(|| { + let root_caller = ::RuntimeOrigin::root(); + let alice = AccountId::from(ALICE); + + // TreasuryCouncilCollective + let _ = Utility::dispatch_as( + root_caller.clone(), + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + // OpenTechCommitteeCollective + let _ = Utility::dispatch_as( + root_caller, + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + + assert_eq!( + System::events() + .into_iter() + .filter_map(|r| { + match r.event { + RuntimeEvent::Utility(pallet_utility::Event::DispatchedAs { result }) + if result.is_ok() => + { + Some(true) + } + _ => None, + } + }) + .collect::>() + .len(), + 2 + ) + }); +} + +#[test] +fn collective_set_members_signed_origin_does_not_work() { + let alice = AccountId::from(ALICE); + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + // OpenTechCommitteeCollective + assert!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + }); +} + +#[test] +fn verify_pallet_indices() { + fn is_pallet_index(index: usize) { + assert_eq!( + ::PalletInfo::index::

(), + Some(index) + ); + } + + // System support + is_pallet_index::(0); + is_pallet_index::(1); + is_pallet_index::(3); + is_pallet_index::(4); + // Monetary + is_pallet_index::(10); + is_pallet_index::(11); + // Consensus support + is_pallet_index::(20); + is_pallet_index::(21); + is_pallet_index::(22); + is_pallet_index::(23); + is_pallet_index::(24); + // Handy utilities + is_pallet_index::(30); + is_pallet_index::(31); + is_pallet_index::(32); + is_pallet_index::(33); + is_pallet_index::(34); + is_pallet_index::(35); + is_pallet_index::(37); + // Ethereum compatibility + is_pallet_index::(50); + is_pallet_index::(51); + is_pallet_index::(52); + // Governance + is_pallet_index::(60); + // is_pallet_index::(61); Removed + // Council + // is_pallet_index::(70); Removed + // is_pallet_index::(71); Removed + is_pallet_index::(72); + is_pallet_index::(73); + // Treasury + is_pallet_index::(80); + // Crowdloan + is_pallet_index::(90); + // XCM Stuff + is_pallet_index::(100); + is_pallet_index::(101); + is_pallet_index::(102); + is_pallet_index::(103); + is_pallet_index::(104); + is_pallet_index::(105); + is_pallet_index::(106); + is_pallet_index::(107); +} + +#[test] +fn verify_reserved_indices() { + use frame_metadata::*; + let metadata = moonbeam_runtime::Runtime::metadata(); + let metadata = match metadata.1 { + RuntimeMetadata::V14(metadata) => metadata, + _ => panic!("metadata has been bumped, test needs to be updated"), + }; + // 40: Sudo + // 53: BaseFee + // 108: pallet_assets:: + let reserved = vec![40, 53, 108]; + let existing = metadata + .pallets + .iter() + .map(|p| p.index) + .collect::>(); + assert!(reserved.iter().all(|index| !existing.contains(index))); +} + +#[test] +fn verify_proxy_type_indices() { + assert_eq!(moonbeam_runtime::ProxyType::Any as u8, 0); + assert_eq!(moonbeam_runtime::ProxyType::NonTransfer as u8, 1); + assert_eq!(moonbeam_runtime::ProxyType::Governance as u8, 2); + assert_eq!(moonbeam_runtime::ProxyType::Staking as u8, 3); + assert_eq!(moonbeam_runtime::ProxyType::CancelProxy as u8, 4); + assert_eq!(moonbeam_runtime::ProxyType::Balances as u8, 5); + assert_eq!(moonbeam_runtime::ProxyType::AuthorMapping as u8, 6); + assert_eq!(moonbeam_runtime::ProxyType::IdentityJudgement as u8, 7); +} + +#[test] +fn join_collator_candidates() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 10_000_000 * GLMR), + (AccountId::from(BOB), 10_000_000 * GLMR), + (AccountId::from(CHARLIE), 10_000_000 * GLMR), + (AccountId::from(DAVE), 10_000_000 * GLMR), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 2_000_000 * GLMR), + (AccountId::from(BOB), 2_000_000 * GLMR), + ]) + .with_delegations(vec![ + ( + AccountId::from(CHARLIE), + AccountId::from(ALICE), + 5_000 * GLMR, + ), + (AccountId::from(CHARLIE), AccountId::from(BOB), 5_000 * GLMR), + ]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(ALICE)), + 2_000_000 * GLMR, + 2u32 + ), + pallet_parachain_staking::Error::::CandidateExists + ); + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 2_000_000 * GLMR, + 2u32 + ), + pallet_parachain_staking::Error::::DelegatorExists + ); + assert!(System::events().is_empty()); + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(DAVE)), + 2_000_000 * GLMR, + 2u32 + )); + assert_eq!( + last_event(), + RuntimeEvent::ParachainStaking( + pallet_parachain_staking::Event::JoinedCollatorCandidates { + account: AccountId::from(DAVE), + amount_locked: 2_000_000 * GLMR, + new_total_amt_locked: 6_010_000 * GLMR + } + ) + ); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(ALICE)); + assert_eq!(candidates.0[0].amount, 2_005_000 * GLMR); + assert_eq!(candidates.0[1].owner, AccountId::from(BOB)); + assert_eq!(candidates.0[1].amount, 2_005_000 * GLMR); + assert_eq!(candidates.0[2].owner, AccountId::from(DAVE)); + assert_eq!(candidates.0[2].amount, 2_000_000 * GLMR); + }); +} + +#[test] +fn transfer_through_evm_to_stake() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 10_000_000 * GLMR)]) + .build() + .execute_with(|| { + // Charlie has no balance => fails to stake + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 2_000_000 * GLMR, + 2u32 + ), + DispatchError::Module(ModuleError { + index: 20, + error: [8, 0, 0, 0], + message: Some("InsufficientBalance") + }) + ); + // Alice transfer from free balance 3_000_000 GLMR to Bob + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 3_000_000 * GLMR, + )); + assert_eq!( + Balances::free_balance(AccountId::from(BOB)), + 3_000_000 * GLMR + ); + + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + // Bob transfers 2_000_000 GLMR to Charlie via EVM + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(CHARLIE), + input: vec![], + value: (2_000_000 * GLMR).into(), + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + assert_eq!( + Balances::free_balance(AccountId::from(CHARLIE)), + 2_000_000 * GLMR, + ); + + // Charlie can stake now + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 2_000_000 * GLMR, + 2u32 + ),); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(CHARLIE)); + assert_eq!(candidates.0[0].amount, 2_000_000 * GLMR); + }); +} + +#[test] +fn reward_block_authors() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 10k extra tokens for her mapping deposit + (AccountId::from(ALICE), 10_010_000 * GLMR), + (AccountId::from(BOB), 10_000_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + for x in 2..3599 { + run_to_block(x, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + } + // no rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 8_010_000 * GLMR, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9_950_000 * GLMR, + ); + run_to_block(3601, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 8990978048702400000000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9969521950497200000000000, + ); + }); +} + +#[test] +fn reward_block_authors_with_parachain_bond_reserved() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 10k extra tokens for her mapping deposit + (AccountId::from(ALICE), 10_010_000 * GLMR), + (AccountId::from(BOB), 10_000_000 * GLMR), + (AccountId::from(CHARLIE), 10_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + assert_ok!(ParachainStaking::set_parachain_bond_account( + root_origin(), + AccountId::from(CHARLIE), + ),); + + // Stop just before round 3 + run_to_block(3599, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // no collators rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 8_010_000 * GLMR, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9_950_000 * GLMR, + ); + // 30% reserved for parachain bond + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 310300000000000000000000, + ); + + // Go to round 3 + run_to_block(3601, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + // collators rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 8698492682878000000000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9962207316621500000000000, + ); + // 30% reserved for parachain bond again + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 615104500000000000000000, + ); + }); +} + +#[test] +fn initialize_crowdloan_addresses_with_batch_and_pay() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(300_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 150_000_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 150_000_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!( + Balances::balance(&AccountId::from(CHARLIE)), + 45_000_000 * GLMR + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 45_000_000 * GLMR); + let expected = RuntimeEvent::Utility(pallet_utility::Event::BatchCompleted); + assert_eq!(last_event(), expected); + // This one should fail, as we already filled our data + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch { + calls: vec![RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(ALICE)), + 43_200_000 + )] + } + )] + }) + .dispatch(root_origin()) + ); + let expected_fail = RuntimeEvent::Utility(pallet_utility::Event::BatchInterrupted { + index: 0, + error: DispatchError::Module(ModuleError { + index: 90, + error: [8, 0, 0, 0], + message: None, + }), + }); + assert_eq!(last_event(), expected_fail); + // Claim 1 block. + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(CHARLIE)))); + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(DAVE)))); + + let vesting_period = 4 * WEEKS as u128; + let per_block = (105_000_000 * GLMR) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (45_000_000 * GLMR) + per_block + ); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (45_000_000 * GLMR) + per_block + ); + // The total claimed reward should be equal to the account balance at this point. + assert_eq!( + Balances::balance(&AccountId::from(CHARLIE)), + (45_000_000 * GLMR) + per_block + ); + assert_eq!( + Balances::balance(&AccountId::from(DAVE)), + (45_000_000 * GLMR) + per_block + ); + assert_noop!( + CrowdloanRewards::claim(origin_of(AccountId::from(ALICE))), + pallet_crowdloan_rewards::Error::::NoAssociatedClaim + ); + }); +} + +#[test] +fn initialize_crowdloan_address_and_change_with_relay_key_sig() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + + let (pair1, _) = sp_core::sr25519::Pair::generate(); + let (pair2, _) = sp_core::sr25519::Pair::generate(); + + let public1 = pair1.public(); + let public2 = pair2.public(); + + // signature is new_account || previous_account + let mut message = pallet_crowdloan_rewards::WRAPPED_BYTES_PREFIX.to_vec(); + message.append(&mut b"moonbeam-".to_vec()); + message.append(&mut AccountId::from(DAVE).encode()); + message.append(&mut AccountId::from(CHARLIE).encode()); + message.append(&mut pallet_crowdloan_rewards::WRAPPED_BYTES_POSTFIX.to_vec()); + let signature1 = pair1.sign(&message); + let signature2 = pair2.sign(&message); + + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + // two relay accounts pointing at the same reward account + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public1.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public2.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 900_000 * GLMR); + + // this should fail, as we are only providing one signature + assert_noop!( + CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![(public1.into(), signature1.clone().into())] + ), + pallet_crowdloan_rewards::Error::::InsufficientNumberOfValidProofs + ); + + // this should be valid + assert_ok!(CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![ + (public1.into(), signature1.into()), + (public2.into(), signature2.into()) + ] + )); + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (900_000 * GLMR) + ); + }); +} + +#[test] +fn claim_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + assert!(CrowdloanRewards::initialized()); + + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * GLMR); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * GLMR); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Alice uses the crowdloan precompile to claim through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + + // Construct the call data (selector, amount) + let mut call_data = Vec::::from([0u8; 4]); + call_data[0..4].copy_from_slice(&Keccak256::digest(b"claim()")[0..4]); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let vesting_period = 4 * WEEKS as u128; + let per_block = (1_050_000 * GLMR) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (450_000 * GLMR) + per_block + ); + }) +} + +#[test] +fn is_contributor_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Assert precompile reports Bob is not a contributor + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(AccountId::from(BOB).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(false); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(AccountId::from(CHARLIE).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(true); + }) +} + +#[test] +fn reward_info_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + let expected_total: U256 = (1_500_000 * GLMR).into(); + let expected_claimed: U256 = (450_000 * GLMR).into(); + + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::reward_info { + contributor: Address(AccountId::from(CHARLIE).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns((expected_total, expected_claimed)); + }) +} + +#[test] +fn update_reward_address_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * GLMR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * GLMR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Charlie uses the crowdloan precompile to update address through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + + // Construct the input data to check if Bob is a contributor + let mut call_data = Vec::::from([0u8; 36]); + call_data[0..4] + .copy_from_slice(&Keccak256::digest(b"update_reward_address(address)")[0..4]); + call_data[16..36].copy_from_slice(&ALICE); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + assert!(CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)).is_none()); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(ALICE)) + .unwrap() + .claimed_reward, + (450_000 * GLMR) + ); + }) +} + +fn run_with_system_weight(w: Weight, mut assertions: F) +where + F: FnMut() -> (), +{ + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); +} + +#[test] +#[rustfmt::skip] +fn length_fee_is_sensible() { + use sp_runtime::testing::TestXt; + + // tests that length fee is sensible for a few hypothetical transactions + ExtBuilder::default().build().execute_with(|| { + let call = frame_system::Call::remark:: { remark: vec![] }; + let uxt: TestXt<_, ()> = TestXt::new(call, Some((1u64, ()))); + + let calc_fee = |len: u32| -> Balance { + moonbeam_runtime::TransactionPayment::query_fee_details(uxt.clone(), len) + .inclusion_fee + .expect("fee should be calculated") + .len_fee + }; + + // editorconfig-checker-disable + // left: cost of length fee, right: size in bytes + // /------------- proportional component: O(N * 1B) + // | /- exponential component: O(N ** 3) + // | | + assert_eq!( 100_000_000_100, calc_fee(1)); + assert_eq!( 1_000_000_100_000, calc_fee(10)); + assert_eq!( 10_000_100_000_000, calc_fee(100)); + assert_eq!( 100_100_000_000_000, calc_fee(1_000)); + assert_eq!( 1_100_000_000_000_000, calc_fee(10_000)); // inflection point + assert_eq!( 110_000_000_000_000_000, calc_fee(100_000)); + assert_eq!( 100_100_000_000_000_000_000, calc_fee(1_000_000)); // 100 GLMR, ~ 1MB + assert_eq!( 100_001_000_000_000_000_000_000, calc_fee(10_000_000)); + assert_eq!(100_000_010_000_000_000_000_000_000, calc_fee(100_000_000)); + // editorconfig-checker-enable + }); +} + +#[test] +fn multiplier_can_grow_from_zero() { + use frame_support::traits::Get; + + let minimum_multiplier = moonbeam_runtime::MinimumMultiplier::get(); + let target = moonbeam_runtime::TargetBlockFullness::get() + * RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = moonbeam_runtime::SlowAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) +} + +#[test] +fn ethereum_invalid_transaction() { + ExtBuilder::default().build().execute_with(|| { + // Ensure an extrinsic not containing enough gas limit to store the transaction + // on chain is rejected. + assert_eq!( + Executive::apply_extrinsic(unchecked_eth_tx(INVALID_ETH_TX)), + Err( + sp_runtime::transaction_validity::TransactionValidityError::Invalid( + sp_runtime::transaction_validity::InvalidTransaction::Custom(0u8) + ) + ) + ); + }); +} + +#[test] +fn initial_gas_fee_is_correct() { + use fp_evm::FeeCalculator; + + ExtBuilder::default().build().execute_with(|| { + let multiplier = TransactionPayment::next_fee_multiplier(); + assert_eq!(multiplier, Multiplier::from(1u128)); + + assert_eq!( + TransactionPaymentAsGasPrice::min_gas_price(), + ( + 125_000_000_000u128.into(), + Weight::from_parts(25_000_000u64, 0) + ) + ); + }); +} + +#[test] +fn min_gas_fee_is_correct() { + use fp_evm::FeeCalculator; + use frame_support::traits::Hooks; + + ExtBuilder::default().build().execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::put(Multiplier::from(0)); + TransactionPayment::on_finalize(System::block_number()); // should trigger min to kick in + + let multiplier = TransactionPayment::next_fee_multiplier(); + assert_eq!(multiplier, Multiplier::from(1u128)); + + assert_eq!( + TransactionPaymentAsGasPrice::min_gas_price(), + ( + 125_000_000_000u128.into(), + Weight::from_parts(25_000_000u64, 0) + ) + ); + }); +} + +#[test] +fn transfer_ed_0_substrate() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), (1 * GLMR) + (1 * WEI)), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // Substrate transfer + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 1 * GLMR, + )); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI); + }); +} + +#[test] +fn transfer_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * GLMR) + (21_000 * BASE_FEE_GENESIS)) + (1 * WEI), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * GLMR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: BASE_FEE_GENESIS.into(), + max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI,); + }); +} + +#[test] +fn refund_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * GLMR) + (21_777 * BASE_FEE_GENESIS)), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer that zeroes ALICE + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * GLMR).into(), + gas_limit: 21_777u64, + max_fee_per_gas: BASE_FEE_GENESIS.into(), + max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // ALICE is refunded + assert_eq!( + Balances::free_balance(AccountId::from(ALICE)), + 777 * BASE_FEE_GENESIS, + ); + }); +} + +#[test] +fn author_does_not_receive_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * GLMR) + (21_000 * (500 * GIGAWEI)), + )]) + .build() + .execute_with(|| { + // Some block author as seen by pallet-evm. + let author = AccountId::from(>::find_author()); + // Currently the default impl of the evm uses `deposit_into_existing`. + // If we were to use this implementation, and for an author to receive eventual tips, + // the account needs to be somehow initialized, otherwise the deposit would fail. + Balances::make_free_balance_be(&author, 100 * GLMR); + + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * GLMR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(300 * GIGAWEI), + max_priority_fee_per_gas: Some(U256::from(200 * GIGAWEI)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // Author free balance didn't change. + assert_eq!(Balances::free_balance(author), 100 * GLMR,); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_with_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * GLMR) + (21_000 * (200 * GIGAWEI)), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * GLMR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(200 * GIGAWEI), + max_priority_fee_per_gas: Some(U256::from(100 * GIGAWEI)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + // Fee is 100 GWEI base fee + 100 GWEI tip. + let fee = ((200 * GIGAWEI) * 21_000) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonbeam_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_without_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * GLMR) + (21_000 * BASE_FEE_GENESIS), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * GLMR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: BASE_FEE_GENESIS.into(), + max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + // Fee is 100 GWEI base fee. + let fee = (BASE_FEE_GENESIS * 21_000) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonbeam_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn root_can_change_default_xcm_vers() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let source_id: moonbeam_runtime::AssetId = source_location.clone().into(); + // Default XCM version is not set yet, so xtokens should fail because it does not + // know with which version to send + assert_noop!( + XTokens::transfer( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest.clone())), + WeightLimit::Limited(4000000000.into()) + ), + orml_xtokens::Error::::XcmExecutionFailed + ); + + // Root sets the defaultXcm + assert_ok!(PolkadotXcm::force_default_xcm_version( + root_origin(), + Some(2) + )); + + // Now transferring does not fail + assert_ok!(XTokens::transfer( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest)), + WeightLimit::Limited(4000000000.into()) + )); + }) +} + +#[test] +fn asset_can_be_registered() { + ExtBuilder::default().build().execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonbeam_runtime::AssetId = source_location.clone().into(); + let asset_metadata = AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }; + assert_ok!(AssetManager::register_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + source_location, + asset_metadata, + 1u128, + true + )); + assert!(AssetManager::asset_id_type(source_id).is_some()); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_supply_and_balance() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: moonbeam_runtime::AssetId = + AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Assert the asset has been created with the correct supply + assert_eq!( + moonbeam_runtime::Assets::total_supply(relay_asset_id), + 1_000 * GLMR + ); + + // Access totalSupply through precompile. Important that the context is correct + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::total_supply {}, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * GLMR)); + + // Access balanceOf through precompile + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(ALICE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * GLMR)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: moonbeam_runtime::AssetId = + AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Transfer tokens from Aice to Bob, 400 GLMR. + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::transfer { + to: Address(BOB.into()), + value: { 400 * GLMR }.into(), + }, + ) + .expect_cost(24377) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * GLMR)), + )) + .execute_returns(true); + + // Make sure BOB has 400 GLMR + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(BOB.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * GLMR)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_approve() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: moonbeam_runtime::AssetId = + AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Aprove Bob for spending 400 GLMR from Alice + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::approve { + spender: Address(BOB.into()), + value: { 400 * GLMR }.into(), + }, + ) + .expect_cost(14429) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * GLMR)), + )) + .execute_returns(true); + + // Transfer tokens from Alice to Charlie by using BOB as origin + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::transfer_from { + from: Address(ALICE.into()), + to: Address(CHARLIE.into()), + value: { 400 * GLMR }.into(), + }, + ) + .expect_cost(29748) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + solidity::encode_event_data(U256::from(400 * GLMR)), + )) + .execute_returns(true); + + // Make sure CHARLIE has 400 GLMR + Precompiles::new() + .prepare_test( + CHARLIE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(CHARLIE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * GLMR)); + }); +} + +#[test] +fn xtokens_precompile_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: moonbeam_runtime::AssetId = + AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer { + currency_address: Address(asset_precompile_address.into()), + amount: 500_000_000_000_000u128.into(), + destination: destination.clone(), + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()) + }) +} + +#[test] +fn xtokens_precompile_transfer_multiasset() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // This time we transfer it through TransferMultiAsset + // Instead of the address, we encode directly the multilocation referencing the asset + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer_multiasset { + // We want to transfer the relay token + asset: Location::parent(), + amount: 500_000_000_000_000u128.into(), + destination: destination.clone(), + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()); + }) +} + +#[test] +fn make_sure_glmr_can_be_transferred_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + assert_ok!(XTokens::transfer_multiasset( + origin_of(AccountId::from(ALICE)), + Box::new(VersionedAsset::V4(Asset { + id: AssetId(moonbeam_runtime::xcm_config::SelfReserve::get()), + fun: Fungible(1000) + })), + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(40000.into()) + )); + }); +} + +#[test] +fn make_sure_glmr_can_be_transferred() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + assert_ok!(XTokens::transfer( + origin_of(AccountId::from(ALICE)), + CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(40000.into()) + )); + }); +} + +#[test] +fn make_sure_polkadot_xcm_cannot_be_called() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let assets: Assets = [Asset { + id: AssetId(moonbeam_runtime::xcm_config::SelfLocation::get()), + fun: Fungible(1000), + }] + .to_vec() + .into(); + assert_noop!( + RuntimeCall::PolkadotXcm(pallet_xcm::Call::::reserve_transfer_assets { + dest: Box::new(VersionedLocation::V4(dest.clone())), + beneficiary: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets)), + fee_asset_item: 0, + }) + .dispatch(::RuntimeOrigin::signed( + AccountId::from(ALICE) + )), + frame_system::Error::::CallFiltered + ); + }); +} + +#[test] +fn transact_through_signed_precompile_works_v2() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::parent(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .expect_cost(17559) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn transact_through_signed_cannot_send_to_local_chain() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::here(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error:") + && from_utf8(&output).unwrap().contains("ErrorValidating") + }); + }); +} + +#[test] +fn transactor_cannot_use_more_than_max_weight() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonbeam_runtime::AssetId = source_location.clone().into(); + assert_ok!(XcmTransactor::register( + root_origin(), + AccountId::from(ALICE), + 0, + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000.into(), + None + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + 1, + )); + + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonbeam_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + vec![], + // 2000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonbeam_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsCurrencyId(CurrencyId::ForeignAsset(source_id)), + fee_amount: None + }, + vec![], + // 20000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + }) +} + +#[test] +fn call_xtokens_with_fee() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 1_000 * GLMR), + ]) + .with_safe_xcm_version(2) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let source_id: moonbeam_runtime::AssetId = source_location.clone().into(); + + let before_balance = + moonbeam_runtime::Assets::balance(source_id, &AccountId::from(ALICE)); + + // We are able to transfer with fee + assert_ok!(XTokens::transfer_with_fee( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + 100, + Box::new(xcm::VersionedLocation::V4(dest.clone())), + WeightLimit::Limited(4000000000.into()) + )); + + let after_balance = + moonbeam_runtime::Assets::balance(source_id, &AccountId::from(ALICE)); + // At least these much (plus fees) should have been charged + assert_eq!(before_balance - 100_000_000_000_000 - 100, after_balance); + }); +} + +#[test] +fn test_xcm_utils_ml_tp_account() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_address_parent: H160 = + ParentIsPreset::::convert_location(&Location::parent()) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: Location::parent(), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parent)); + + let parachain_2000_location = Location::new(1, [Parachain(2000)]); + let expected_address_parachain: H160 = + SiblingParachainConvertsVia::::convert_location( + ¶chain_2000_location, + ) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: parachain_2000_location, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parachain)); + + let alice_in_parachain_2000_location = Location::new( + 1, + [ + Parachain(2000), + AccountKey20 { + network: None, + key: ALICE, + }, + ], + ); + let expected_address_alice_in_parachain_2000 = + xcm_builder::HashedDescription::< + AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&alice_in_parachain_2000_location) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: alice_in_parachain_2000_location, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_alice_in_parachain_2000)); + }); +} + +#[test] +fn test_xcm_utils_weight_message() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_weight = + XcmWeight::::clear_origin().ref_time(); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + let input = XcmUtilsPCall::weight_message { + message: message.into(), + }; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(0) + .expect_no_logs() + .execute_returns(expected_weight); + }); +} + +#[test] +fn test_xcm_utils_get_units_per_second() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let location = SelfReserve::get(); + + let input = XcmUtilsPCall::get_units_per_second { location }; + + let expected_units = + WEIGHT_REF_TIME_PER_SECOND as u128 * moonbeam_runtime::currency::WEIGHT_FEE; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(expected_units); + }); +} + +#[test] +fn precompile_existence() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let precompile_addresses: std::collections::BTreeSet<_> = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054, + 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, + 2069, 2070, 2071, 2072, 2073, + ] + .into_iter() + .map(H160::from_low_u64_be) + .collect(); + + for i in 0..3000 { + let address = H160::from_low_u64_be(i); + + if precompile_addresses.contains(&address) { + assert!( + is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return true", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_some(), + "execute({},..) should return Some(_)", + i + ); + } else { + assert!( + !is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return false", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_none(), + "execute({},..) should return None", + i + ); + } + } + }); +} + +#[test] +fn removed_precompiles() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let removed_precompiles = [1025, 2051, 2062, 2063]; + + for i in 1..3000 { + let address = H160::from_low_u64_be(i); + + if !is_precompile_or_fail::(address, 100_000u64).expect("to be ok") { + continue; + } + + if !removed_precompiles.contains(&i) { + assert!( + match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} should be an active precompile" + ); + continue; + } + + assert!( + !match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} shouldn't be an active precompile" + ); + + precompiles + .prepare_test(Alice, address, []) + .execute_reverts(|out| out == b"Removed precompile"); + } + }) +} + +#[test] +fn deal_with_fees_handles_tip() { + use frame_support::traits::OnUnbalanced; + use moonbeam_runtime::{DealWithFees, Treasury}; + + ExtBuilder::default().build().execute_with(|| { + // This test checks the functionality of the `DealWithFees` trait implementation in the runtime. + // It simulates a scenario where a fee and a tip are issued to an account and ensures that the + // treasury receives the correct amount (20% of the total), and the rest is burned (80%). + // + // The test follows these steps: + // 1. It issues a fee of 100 and a tip of 1000. + // 2. It checks the total supply before the fee and tip are dealt with, which should be 1_100. + // 3. It checks that the treasury's balance is initially 0. + // 4. It calls `DealWithFees::on_unbalanceds` with the fee and tip. + // 5. It checks that the treasury's balance is now 220 (20% of the fee and tip). + // 6. It checks that the total supply has decreased by 880 (80% of the fee and tip), indicating + // that this amount was burned. + let fee = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(100); + let tip = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(1000); + + let total_supply_before = Balances::total_issuance(); + assert_eq!(total_supply_before, 1_100); + assert_eq!(Balances::free_balance(&Treasury::account_id()), 0); + + DealWithFees::on_unbalanceds(vec![fee, tip].into_iter()); + + // treasury should have received 20% + assert_eq!(Balances::free_balance(&Treasury::account_id()), 220); + + // verify 80% burned + let total_supply_after = Balances::total_issuance(); + assert_eq!(total_supply_before - total_supply_after, 880); + }); +} + +#[test] +fn evm_revert_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + // Batch a transfer followed by an invalid call to batch. + // Thus BatchAll will revert the transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + + input: BatchPCall::batch_all { + to: vec![Address(BOB.into()), Address(batch_precompile_address)].into(), + value: vec![U256::from(1 * GLMR), U256::zero()].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: BASE_FEE_GENESIS.into(), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 0, "there should be no transfer event"); + }); +} + +#[test] +fn evm_success_keeps_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + input: BatchPCall::batch_all { + to: vec![Address(BOB.into())].into(), + value: vec![U256::from(1 * GLMR)].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: BASE_FEE_GENESIS.into(), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 1, "there should be 1 transfer event"); + }); +} + +#[cfg(test)] +mod fee_tests { + use super::*; + use frame_support::{ + traits::ConstU128, + weights::{ConstantMultiplier, WeightToFee}, + }; + use moonbeam_runtime::{ + currency, LengthToFee, MinimumMultiplier, RuntimeBlockWeights, SlowAdjustingFeeUpdate, + TargetBlockFullness, TransactionPayment, + }; + use sp_core::Get; + use sp_runtime::FixedPointNumber; + + fn run_with_system_weight(w: Weight, mut assertions: F) + where + F: FnMut() -> (), + { + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); + } + + #[test] + fn test_multiplier_can_grow_from_zero() { + let minimum_multiplier = MinimumMultiplier::get(); + let target = TargetBlockFullness::get() + * RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = SlowAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) + } + + #[test] + fn test_fee_calculation() { + let base_extrinsic = RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let multiplier = sp_runtime::FixedU128::from_float(0.999000000000000000); + let extrinsic_len = 100u32; + let extrinsic_weight = 5_000u64; + let tip = 42u128; + type WeightToFeeImpl = ConstantMultiplier>; + type LengthToFeeImpl = LengthToFee; + + // base_fee + (multiplier * extrinsic_weight_fee) + extrinsic_length_fee + tip + let expected_fee = + WeightToFeeImpl::weight_to_fee(&base_extrinsic) + + multiplier.saturating_mul_int(WeightToFeeImpl::weight_to_fee( + &Weight::from_parts(extrinsic_weight, 1), + )) + LengthToFeeImpl::weight_to_fee(&Weight::from_parts(extrinsic_len as u64, 1)) + + tip; + + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier); + let actual_fee = TransactionPayment::compute_fee( + extrinsic_len, + &frame_support::dispatch::DispatchInfo { + class: DispatchClass::Normal, + pays_fee: frame_support::dispatch::Pays::Yes, + weight: Weight::from_parts(extrinsic_weight, 1), + }, + tip, + ); + + assert_eq!( + expected_fee, + actual_fee, + "The actual fee did not match the expected fee, diff {}", + actual_fee - expected_fee + ); + }); + } +} diff --git a/tracing/2900/runtime/moonbeam/tests/runtime_apis.rs b/tracing/2900/runtime/moonbeam/tests/runtime_apis.rs new file mode 100644 index 00000000..26609076 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/runtime_apis.rs @@ -0,0 +1,393 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbeam Runtime Api Integration Tests + +mod common; +use common::*; + +use fp_evm::GenesisAccount; +use frame_support::assert_ok; +use nimbus_primitives::NimbusId; +use pallet_evm::{Account as EVMAccount, AddressMapping, FeeCalculator}; +use sp_core::{ByteArray, H160, H256, U256}; + +use fp_rpc::runtime_decl_for_ethereum_runtime_rpc_api::EthereumRuntimeRPCApi; +use moonbeam_rpc_primitives_txpool::runtime_decl_for_tx_pool_runtime_api::TxPoolRuntimeApi; +use nimbus_primitives::runtime_decl_for_nimbus_api::NimbusApi; +use std::{collections::BTreeMap, str::FromStr}; + +#[test] +fn ethereum_runtime_rpc_api_chain_id() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Runtime::chain_id(), CHAIN_ID); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_basic() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * GLMR)]) + .build() + .execute_with(|| { + assert_eq!( + Runtime::account_basic(H160::from(ALICE)), + EVMAccount { + balance: U256::from(2_000 * GLMR), + nonce: U256::zero() + } + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_gas_price() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!( + Runtime::gas_price(), + TransactionPaymentAsGasPrice::min_gas_price().0 + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_code_at() { + let address = H160::from(EVM_CONTRACT); + let code: Vec = vec![1, 2, 3, 4, 5]; + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: code.clone(), + nonce: Default::default(), + storage: Default::default(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::account_code_at(address), code); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_author() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + assert_eq!(Runtime::author(), H160::from(ALICE)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_storage_at() { + let address = H160::from(EVM_CONTRACT); + let mut key = [0u8; 32]; + key[31..32].copy_from_slice(&[6u8][..]); + let mut value = [0u8; 32]; + value[31..32].copy_from_slice(&[7u8][..]); + let item = H256::from_slice(&key[..]); + let mut storage: BTreeMap = BTreeMap::new(); + storage.insert(H256::from_slice(&key[..]), item); + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: Vec::new(), + nonce: Default::default(), + storage: storage.clone(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::storage_at(address, U256::from(6)), item); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_call() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * GLMR), + (AccountId::from(BOB), 2_000 * GLMR), + ]) + .build() + .execute_with(|| { + let execution_result = Runtime::call( + H160::from(ALICE), // from + H160::from(BOB), // to + Vec::new(), // data + U256::from(1000u64), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_create() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * GLMR)]) + .build() + .execute_with(|| { + let execution_result = Runtime::create( + H160::from(ALICE), // from + vec![0, 1, 1, 0], // data + U256::zero(), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_transaction_statuses() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 200_000 * GLMR), + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + + let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)); + + rpc_run_to_block(2); + let statuses = + Runtime::current_transaction_statuses().expect("Transaction statuses result."); + assert_eq!(statuses.len(), 1); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_block() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + rpc_run_to_block(2); + let block = Runtime::current_block().expect("Block result."); + assert_eq!(block.header.number, U256::from(1u8)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_receipts() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 200_000 * GLMR), + (AccountId::from(ALICE), 200_000 * GLMR), + (AccountId::from(BOB), 100_000 * GLMR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 50_000 * GLMR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + + let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)); + + rpc_run_to_block(2); + let receipts = Runtime::current_receipts().expect("Receipts result."); + assert_eq!(receipts.len(), 1); + }); +} + +#[test] +fn txpool_runtime_api_extrinsic_filter() { + ExtBuilder::default().build().execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * GLMR, + } + .into(), + ); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let txpool = >::extrinsic_filter( + vec![eth_uxt.clone(), non_eth_uxt.clone()], + vec![unchecked_eth_tx(VALID_ETH_TX), non_eth_uxt], + ); + assert_eq!(txpool.ready.len(), 1); + assert_eq!(txpool.future.len(), 1); + }); +} + +#[test] +fn can_author_when_selected_is_empty() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 20_000_000 * GLMR), + (AccountId::from(BOB), 10_000_000 * GLMR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }; + + // Base case: ALICE can author blocks when she is the only candidate + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Remove ALICE from candidate pool, leaving the candidate_pool empty + assert_ok!(ParachainStaking::go_offline(origin_of(AccountId::from( + ALICE + )))); + + // Need to fast forward to right before the next session, which is when selected candidates + // will be updated. We want to test the creation of the first block of the next session. + run_to_block(1799, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1799, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Check that it works as expected after session update + run_to_block(1800, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1800, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + }); +} diff --git a/tracing/2900/runtime/moonbeam/tests/xcm_mock/mod.rs b/tracing/2900/runtime/moonbeam/tests/xcm_mock/mod.rs new file mode 100644 index 00000000..d52700b8 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/xcm_mock/mod.rs @@ -0,0 +1,271 @@ +// Copyright 2021 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 . + +pub mod parachain; +pub mod relay_chain; +pub mod statemint_like; + +use cumulus_primitives_core::ParaId; +use pallet_xcm_transactor::relay_indices::*; +use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{AccountId32, BuildStorage}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +use polkadot_runtime_parachains::configuration::{ + GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, +}; +use polkadot_runtime_parachains::paras::{ + GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, +}; +use sp_core::{H160, U256}; +use std::{collections::BTreeMap, str::FromStr}; + +pub const PARAALICE: [u8; 20] = [1u8; 20]; +pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); + +pub fn para_a_account() -> AccountId32 { + ParaId::from(1).into_account_truncating() +} + +pub fn para_b_account() -> AccountId32 { + ParaId::from(2).into_account_truncating() +} + +pub fn para_a_account_20() -> parachain::AccountId { + ParaId::from(1).into_account_truncating() +} + +pub fn evm_account() -> H160 { + H160::from_str("1000000000000000000000000000000000000001").unwrap() +} + +pub fn mock_para_genesis_info() -> ParaGenesisArgs { + ParaGenesisArgs { + genesis_head: vec![1u8].into(), + validation_code: vec![1u8].into(), + para_kind: ParaKind::Parachain, + } +} + +pub fn mock_relay_config() -> HostConfiguration { + HostConfiguration:: { + hrmp_channel_max_capacity: u32::MAX, + hrmp_channel_max_total_size: u32::MAX, + hrmp_max_parachain_inbound_channels: 10, + hrmp_max_parachain_outbound_channels: 10, + hrmp_channel_max_message_size: u32::MAX, + // Changed to avoid aritmetic errors within hrmp_close + max_downward_message_size: 100_000u32, + ..Default::default() + } +} + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_parachain! { + pub struct ParaC { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(3), + } +} + +decl_test_parachain! { + pub struct Statemint { + Runtime = statemint_like::Runtime, + XcmpMessageHandler = statemint_like::MsgQueue, + DmpMessageHandler = statemint_like::MsgQueue, + new_ext = statemint_ext(4), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(vec![1, 2, 3, 4]), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + (3, ParaC), + (4, Statemint), + ], + } +} + +pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; + +pub const INITIAL_EVM_BALANCE: u128 = 0; +pub const INITIAL_EVM_NONCE: u32 = 1; + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(PARAALICE.into(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm_transactor::GenesisConfig:: { + // match relay runtime construct_runtime order in xcm_mock::relay_chain + relay_indices: RelayChainIndices { + hrmp: 6u8, + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + ..Default::default() + }, + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // EVM accounts are self-sufficient. + let mut evm_accounts = BTreeMap::new(); + evm_accounts.insert( + evm_account(), + fp_evm::GenesisAccount { + nonce: U256::from(INITIAL_EVM_NONCE), + balance: U256::from(INITIAL_EVM_BALANCE), + storage: Default::default(), + code: vec![ + 0x00, // STOP + ], + }, + ); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn statemint_ext(para_id: u32) -> sp_io::TestExternalities { + use statemint_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (RELAYALICE.into(), INITIAL_BALANCE), + (RELAYBOB.into(), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext(paras: Vec) -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(RELAYALICE, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras + .iter() + .map(|¶_id| (para_id.into(), mock_para_genesis_info())) + .collect(); + + let genesis_config = ConfigurationGenesisConfig:: { + config: mock_relay_config(), + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = ParasGenesisConfig:: { + paras: para_genesis, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type Hrmp = polkadot_runtime_parachains::hrmp::Pallet; + +pub type StatemintBalances = pallet_balances::Pallet; +pub type StatemintChainPalletXcm = pallet_xcm::Pallet; +pub type StatemintAssets = pallet_assets::Pallet; + +pub type Assets = pallet_assets::Pallet; +pub type Treasury = pallet_treasury::Pallet; +pub type AssetManager = pallet_asset_manager::Pallet; +pub type XTokens = orml_xtokens::Pallet; +pub type RelayBalances = pallet_balances::Pallet; +pub type ParaBalances = pallet_balances::Pallet; +pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/tracing/2900/runtime/moonbeam/tests/xcm_mock/parachain.rs b/tracing/2900/runtime/moonbeam/tests/xcm_mock/parachain.rs new file mode 100644 index 00000000..3ed310da --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/xcm_mock/parachain.rs @@ -0,0 +1,1088 @@ +// Copyright 2021 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 . + +//! Parachain runtime mock. + +use frame_support::{ + construct_runtime, + dispatch::GetDispatchInfo, + ensure, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU32, Everything, Get, InstanceFilter, Nothing, PalletInfoAccess, + }, + weights::Weight, + PalletId, +}; +pub use moonbeam_runtime::xcm_config::AssetType; + +use frame_system::{pallet_prelude::BlockNumberFor, EnsureNever, EnsureRoot}; +use pallet_xcm::migration::v1::VersionUncheckedMigrateToV1; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, + Permill, +}; +use sp_std::{convert::TryFrom, prelude::*}; +use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; + +use cumulus_primitives_core::relay_chain::HrmpChannelId; +use orml_traits::parameter_type_with_key; +use pallet_ethereum::PostLogContent; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use xcm::latest::{ + AssetId as XcmAssetId, Error as XcmError, ExecuteXcm, + Junction::{PalletInstance, Parachain}, + Location, NetworkId, Outcome, Xcm, +}; +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, + FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, + NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; + +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; +use scale_info::TypeInfo; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; + +pub type AccountId = moonbeam_core_primitives::AccountId; +pub type Balance = u128; +pub type AssetId = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 0; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +pub type ForeignAssetInstance = (); + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 1; // Does not really matter as this will be only called by root + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + xcm_builder::HashedDescription< + AccountId, + xcm_builder::DescribeFamily, + >, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + SignedAccountKey20AsNative, +); + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); + pub MaxInstructions: u32 = 100; +} + +// Instructing how incoming xcm assets will be handled +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId32 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + IsConcrete, + // We can convert the Locations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// We use all transactors +pub type AssetTransactors = (LocalAssetTransactor, ForeignFungiblesTransactor); +pub type XcmRouter = super::ParachainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +pub type XcmFeesToAccount_ = xcm_primitives::XcmFeesToAccount< + Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +parameter_types! { + // We cannot skip the native trader for some specific tests, so we will have to work with + // a native trader that charges same number of units as weight + pub ParaTokensPerSecond: (XcmAssetId, u128, u128) = ( + AssetId(SelfReserve::get()), + 1000000000000, + 0, + ); +} + +parameter_types! { + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub SelfReserve: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +use frame_system::RawOrigin; +use sp_runtime::traits::PostDispatchInfoOf; +use sp_runtime::DispatchErrorWithPostInfo; +use xcm_executor::traits::CallDispatcher; +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + // We use two traders + // When we receive the self-reserve asset, + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + FixedRateOfFungible, + xcm_primitives::FirstAssetTrader, + ); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + SelfReserve, + ForeignAsset(AssetId), +} + +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + CurrencyId::SelfReserve => { + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxAssetsForTransfer: usize = 2; + pub SelfLocation: Location = Location::here(); + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(MsgQueue::parachain_id().into()) + ].into() + }; +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + (1, Some(Parachain(4u32))) => Some(50u128), + _ => None, + } + }; +} + +// The XCM message wrapper wrapper +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdConvert = + CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: Balance = 0; + pub const SpendPeriod: u32 = 0; + pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); + pub const MaxApprovals: u32 = 100; + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + type ApproveOrigin = EnsureRoot; + type RejectOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = MaxApprovals; + type WeightInfo = (); + type SpendFunds = (); + type ProposalBondMaximum = (); + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<0>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ArgumentsBenchmarkHelper; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} + +// Pallet to provide the version, used to test runtime upgrade version changes +#[frame_support::pallet] +pub mod mock_version_changer { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_version)] + pub(super) type CurrentVersion = StorageValue<_, XcmVersion, ValueQuery>; + + impl Get for Pallet { + fn get() -> XcmVersion { + Self::current_version() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + VersionChanged(XcmVersion), + } + + impl Pallet { + pub fn set_version(version: XcmVersion) { + CurrentVersion::::put(version); + Self::deposit_event(Event::VersionChanged(version)); + } + } +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl mock_version_changer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +pub type LocalOriginToLocation = + xcm_primitives::SignedToAccountId20; + +parameter_types! { + pub MatcherLocation: Location = Location::here(); +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = frame_support::traits::Nothing; + type XcmExecutor = XcmExecutor; + // Do not allow teleports + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // We use a custom one to test runtime ugprades + type AdvertisedXcmVersion = XcmVersioner; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::pallet_prelude::DispatchResult; +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset, + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset, + metadata.name, + metadata.symbol, + metadata.decimals, + false, + ) + } + + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into())?; + + Ok(()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, +} + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetMetadata; + type ForeignAssetType = AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = EnsureRoot; + type WeightInfo = (); +} + +// 1 DOT should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = MockTransactors; + type DerivativeAddressRegistrationOrigin = EnsureRoot; + type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdToLocation = + CurrencyIdToLocation>; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type XcmSender = XcmRouter; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; + type WeightInfo = (); + type HrmpManipulatorOrigin = EnsureRoot; + type HrmpOpenOrigin = EnsureRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 1000; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +use sp_core::U256; + +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; +/// Block storage limit in bytes. Set to 40 KB. +const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(u64::MAX); + pub WeightPerGas: Weight = Weight::from_parts(1, 0); + pub GasLimitPovSizeRatio: u64 = { + let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); + block_gas_limit.saturating_div(MAX_POV_SIZE) + }; + pub GasLimitStorageGrowthRatio: u64 = + BlockGasLimit::get().min(u64::MAX.into()).low_u64().saturating_div(BLOCK_STORAGE_LIMIT); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + + type CallOrigin = pallet_evm::EnsureAddressRoot; + type WithdrawOrigin = pallet_evm::EnsureAddressNever; + + type AddressMapping = pallet_evm::IdentityAddressMapping; + type Currency = Balances; + type Runner = pallet_evm::runner::stack::Runner; + + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = (); + type PrecompilesValue = (); + type ChainId = (); + type BlockGasLimit = BlockGasLimit; + type OnChargeTransaction = (); + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = Timestamp; + type WeightInfo = pallet_evm::weights::SubstrateWeight; +} + +pub struct NormalFilter; +impl frame_support::traits::Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + _ => true, + } + } +} + +// We need to use the encoding from the relay mock runtime +#[derive(Encode, Decode)] +pub enum RelayCall { + #[codec(index = 5u8)] + // the index should match the position of the module in `construct_runtime!` + Utility(UtilityCall), + #[codec(index = 6u8)] + // the index should match the position of the module in `construct_runtime!` + Hrmp(HrmpCall), +} + +#[derive(Encode, Decode)] +pub enum UtilityCall { + #[codec(index = 1u8)] + AsDerivative(u16), +} + +// HRMP call encoding, needed for xcm transactor pallet +#[derive(Encode, Decode)] +pub enum HrmpCall { + #[codec(index = 0u8)] + InitOpenChannel(ParaId, u32, u32), + #[codec(index = 1u8)] + AcceptOpenChannel(ParaId), + #[codec(index = 2u8)] + CloseChannel(HrmpChannelId), + #[codec(index = 6u8)] + CancelOpenRequest(HrmpChannelId, u32), +} + +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum MockTransactors { + Relay, +} + +impl xcm_primitives::XcmTransact for MockTransactors { + fn destination(self) -> Location { + match self { + MockTransactors::Relay => Location::parent(), + } + } +} + +impl xcm_primitives::UtilityEncodeCall for MockTransactors { + fn encode_call(self, call: xcm_primitives::UtilityAvailableCalls) -> Vec { + match self { + MockTransactors::Relay => match call { + xcm_primitives::UtilityAvailableCalls::AsDerivative(a, b) => { + let mut call = + RelayCall::Utility(UtilityCall::AsDerivative(a.clone())).encode(); + call.append(&mut b.clone()); + call + } + }, + } + } +} + +pub struct MockHrmpEncoder; +impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { + fn hrmp_encode_call( + call: xcm_primitives::HrmpAvailableCalls, + ) -> Result, xcm::latest::Error> { + match call { + xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( + HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), + ) + .encode()), + xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { + Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) + } + } + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} +parameter_types! { + pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); +} + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +pub enum ProxyType { + NotAllowed = 0, + Any = 1, +} + +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} + +impl InstanceFilter for ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + match self { + ProxyType::NotAllowed => false, + ProxyType::Any => true, + } + } + fn is_superset(&self, _o: &Self) -> bool { + false + } +} + +impl Default for ProxyType { + fn default() -> Self { + Self::NotAllowed + } +} + +parameter_types! { + pub const ProxyCost: u64 = 1; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyCost; + type ProxyDepositFactor = ProxyCost; + type MaxProxies = ConstU32<32>; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ProxyCost; + type AnnouncementDepositFactor = ProxyCost; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will iMmediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +type Block = frame_system::mocking::MockBlockU32; + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_msg_queue, + XcmVersioner: mock_version_changer, + + PolkadotXcm: pallet_xcm, + Assets: pallet_assets, + CumulusXcm: cumulus_pallet_xcm, + XTokens: orml_xtokens, + AssetManager: pallet_asset_manager, + XcmTransactor: pallet_xcm_transactor, + Treasury: pallet_treasury, + Proxy: pallet_proxy, + + Timestamp: pallet_timestamp, + EVM: pallet_evm, + Ethereum: pallet_ethereum, + EthereumXcm: pallet_ethereum_xcm, + } +); + +pub(crate) fn para_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::tokens::{PayFromAccount, UnityAssetBalanceConversion}; +use frame_support::traits::{OnFinalize, OnInitialize, OnRuntimeUpgrade}; +pub(crate) fn on_runtime_upgrade() { + VersionUncheckedMigrateToV1::::on_runtime_upgrade(); +} + +pub(crate) fn para_roll_to(n: BlockNumber) { + while System::block_number() < n { + PolkadotXcm::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + PolkadotXcm::on_initialize(System::block_number()); + } +} diff --git a/tracing/2900/runtime/moonbeam/tests/xcm_mock/relay_chain.rs b/tracing/2900/runtime/moonbeam/tests/xcm_mock/relay_chain.rs new file mode 100644 index 00000000..40bff5e1 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/xcm_mock/relay_chain.rs @@ -0,0 +1,412 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, IdentityLookup}, + AccountId32, +}; + +use frame_support::weights::{Weight, WeightMeter}; +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{ + configuration, dmp, hrmp, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, paras, shared, +}; +use sp_runtime::transaction_validity::TransactionPriority; +use sp_runtime::Permill; +use xcm::latest::prelude::*; +use xcm_builder::{ + Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, + FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + WithComputedOrigin, +}; +use xcm_executor::{Config, XcmExecutor}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); + type PalletsOrigin = OriginCaller; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub KsmLocation: Location = Here.into(); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: Option = None; + pub UniversalLocation: InteriorLocation = Here; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, + // Not enabled in the relay per se, but we enable it to test + // the transact_through_signed extrinsic + Account32Hash, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); + pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub MatcherLocation: Location = Location::here(); +} + +pub type XcmRouter = super::RelayChainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); + pub Statemine: Location = Parachain(4).into(); + pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); +} + +pub type TrustedTeleporters = xcm_builder::Case; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + +/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this +/// is more to satisfy type requirements rather than to test anything. +pub struct TestNextSessionRotation; + +impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { + fn average_session_length() -> u32 { + 10 + } + + fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } + + fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } +} + +impl paras::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = TestNextSessionRotation; + type QueueFootprinter = (); + type OnNewHead = (); + type AssignCoretime = (); +} + +impl dmp::Config for Runtime {} + +impl hrmp::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type WeightInfo = TestHrmpWeightInfo; + type ChannelManager = frame_system::EnsureRoot; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlockU32; + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + ProcessXcmMessage::, RuntimeCall>::process_message( + message, + Junction::Parachain(para.into()), + meter, + id, + ) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); + type QueuePausedQuery = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + MessageQueue: pallet_message_queue, + XcmPallet: pallet_xcm, + Utility: pallet_utility, + Hrmp: hrmp, + Dmp: dmp, + Paras: paras, + Configuration: configuration, + } +); + +pub(crate) fn relay_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::{OnFinalize, OnInitialize}; +pub(crate) fn relay_roll_to(n: BlockNumber) { + while System::block_number() < n { + XcmPallet::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + XcmPallet::on_initialize(System::block_number()); + } +} + +/// A weight info that is only suitable for testing. +pub struct TestHrmpWeightInfo; + +impl hrmp::WeightInfo for TestHrmpWeightInfo { + fn hrmp_accept_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn force_clean_hrmp(_: u32, _: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_close(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_open(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_cancel_open_request(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_close_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_init_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn clean_open_channel_requests(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_open_hrmp_channel(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn establish_system_channel() -> Weight { + Weight::from_parts(1, 0) + } + + fn poke_channel_deposits() -> Weight { + Weight::from_parts(1, 0) + } +} diff --git a/tracing/2900/runtime/moonbeam/tests/xcm_mock/statemint_like.rs b/tracing/2900/runtime/moonbeam/tests/xcm_mock/statemint_like.rs new file mode 100644 index 00000000..a3da2c21 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/xcm_mock/statemint_like.rs @@ -0,0 +1,575 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + weights::Weight, +}; +use frame_system::{EnsureRoot, EnsureSigned}; + +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, Hash, IdentityLookup}, + AccountId32, +}; + +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_parachain::primitives::Sibling; +use sp_std::convert::TryFrom; +use xcm::latest::prelude::*; +use xcm::VersionedXcm; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type AssetId = u128; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) + // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const ExecutiveBody: BodyId = BodyId::Executive; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub Local: Location = Here.into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = + (AssetId(KsmLocation::get()), 1, 1); +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// Means for transacting assets besides the native currency on this chain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ConvertedConcreteId< + AssetId, + Balance, + AsPrefixedGeneralIndex, + JustTry, + >, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only want to allow teleports of known assets. We use non-zero issuance as an indication + // that this asset is known. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `RuntimeOrigin::signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxInstructions: u32 = 100; +} + +pub struct ParentOrParentsExecutivePlurality; +impl Contains for ParentOrParentsExecutivePlurality { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (1, []) + | ( + 1, + [Plurality { + id: BodyId::Executive, + .. + }] + ) + ) + } +} + +pub struct ParentOrSiblings; +impl Contains for ParentOrSiblings { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (1, []) | (1, [_])) + } +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution + AllowUnpaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, +); + +parameter_types! { + pub MatcherLocation: Location = Location::here(); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = + orml_xcm_support::MultiNativeAsset; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +pub type XcmRouter = super::ParachainXcmRouter; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 +#[frame_support::pallet] +pub mod mock_statemint_prefix { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_prefix)] + pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; + + impl Get for Pallet { + fn get() -> Location { + Self::current_prefix() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // Changed Prefix + PrefixChanged(Location), + } + + impl Pallet { + pub fn set_prefix(prefix: Location) { + CurrentPrefix::::put(&prefix); + Self::deposit_event(Event::PrefixChanged(prefix)); + } + } +} + +impl mock_statemint_prefix::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +type Block = frame_system::mocking::MockBlockU32; +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + PolkadotXcm: pallet_xcm, + CumulusXcm: cumulus_pallet_xcm, + MsgQueue: mock_msg_queue, + Assets: pallet_assets, + PrefixChanger: mock_statemint_prefix, + + } +); diff --git a/tracing/2900/runtime/moonbeam/tests/xcm_tests.rs b/tracing/2900/runtime/moonbeam/tests/xcm_tests.rs new file mode 100644 index 00000000..20e55146 --- /dev/null +++ b/tracing/2900/runtime/moonbeam/tests/xcm_tests.rs @@ -0,0 +1,3689 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonbeam Runtime Xcm Tests + +mod xcm_mock; + +use cumulus_primitives_core::relay_chain::HrmpChannelId; +use frame_support::{ + assert_ok, + traits::{PalletInfo, PalletInfoAccess}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + BoundedVec, +}; +use pallet_xcm_transactor::{ + Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, +}; +use sp_core::ConstU32; +use sp_runtime::traits::MaybeEquivalence; +use xcm::latest::prelude::{ + AccountId32, AccountKey20, GeneralIndex, Junction, Junctions, Limited, Location, OriginKind, + PalletInstance, Parachain, QueryResponse, Reanchorable, Response, WeightLimit, Xcm, +}; +use xcm::{VersionedLocation, WrapVersion}; +use xcm_executor::traits::ConvertLocation; +use xcm_mock::parachain; +use xcm_mock::relay_chain; +use xcm_mock::*; +use xcm_primitives::{UtilityEncodeCall, DEFAULT_PROOF_SIZE}; +use xcm_simulator::TestExt; + +// Send a relay asset (like DOT) to a parachain A +#[test] +fn receive_relay_asset_from_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register relay asset in paraA + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // First send relay chain asset to Parachain + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // Verify that parachain received the asset + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +// Send relay asset (like DOT) back from Parachain A to relaychain +#[test] +fn send_relay_asset_to_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register relay asset in paraA + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // Free execution + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // First send relay chain asset to Parachain like in previous test + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Lets gather the balance before sending back money + let mut balance_before_sending = 0; + Relay::execute_with(|| { + balance_before_sending = RelayBalances::free_balance(&RELAYALICE); + }); + + // We now send back some money to the relay + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: RELAYALICE.into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 123, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The balances in paraAlice should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); + + // Balances in the relay should have been received + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); + }); +} + +#[test] +fn send_relay_asset_to_para_b() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register asset in paraA. Free execution + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Now send relay asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 23); + }); + + // Para B balances should have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_to_para_b() { + MockNet::reset(); + + // This represents the asset in paraA + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // Native token is substracted in paraA + ParaA::execute_with(|| { + // Free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Asset is minted in paraB + ParaB::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_from_para_b_to_para_c() { + MockNet::reset(); + + // Represents para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in parachain B. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register para A asset in parachain C. Free execution + ParaC::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send para A asset from para B to para C + let dest = Location { + parents: 1, + interior: [ + Parachain(3), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The message passed through parachainA so we needed to pay since its the native token + ParaC::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 96); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a() { + MockNet::reset(); + + // para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in para B + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send back para A asset to para A + let dest = Location { + parents: 1, + interior: [ + Parachain(1), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn receive_relay_asset_with_trader() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // We are sending 100 tokens from relay. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Therefore with no refund, we should receive 10 tokens less + // Native trader fails for this, and we use the asset trader + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 100).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // non-free execution, not full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // In destination chain, we only need 4 weight + // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // We are sending 100 tokens from para A. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Since we set 10 weight in destination chain, 25 will be charged upfront + // 15 of those will be refunded, while 10 will go to treasury as the true weight used + // will be 4 + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader_and_fee() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // With these units per second, 80K weight convrets to 1 asset unit + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 12500000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // we use transfer_with_fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // 100 tokens transferred plus 1 taken from fees + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 - 1 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received because trully the xcm instruction does not cost + // what it is specified + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 101); + }); +} + +#[test] +fn error_when_not_paying_enough() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + // We are sending 100 tokens from relay. + // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 + // Therefore with no refund, we should receive 10 tokens less + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 5).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // amount not received as it is not paying enough + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); +} + +#[test] +fn transact_through_derivative_multilocation() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + encoded, + // 400000000 + 3000 we should have taken out 4000003000 tokens from the caller + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000003000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + false + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn transact_through_sovereign() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000003000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000009000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A and set XCM version to 1 + ParaA::execute_with(|| { + parachain::XcmVersioner::set_version(1); + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let response = Response::Version(2); + let querier: Location = ([]/* Here */).into(); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: Weight::zero(), + querier: Some(querier), + }]); + // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force + // it directly here + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + Relay::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(RelayChainPalletXcm::force_default_xcm_version( + relay_chain::RuntimeOrigin::root(), + Some(2) + )); + + // Wrap version, which sets VersionedStorage + // This is necessary because the mock router does not use wrap_version, but + // this is not necessary in prod + assert_ok!(::wrap_version( + &Parachain(1).into(), + mock_message + )); + + // Transfer assets. Since it is an unknown destination, it will query for version + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + + // Let's advance the relay. This should trigger the subscription message + relay_chain::relay_roll_to(2); + + // queries should have been updated + assert!(RelayChainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 1, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the version change + assert!(relay_chain::relay_events().contains(&expected_supported_version)); + }); + + // ParaA changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaA::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().iter().any(|e| matches!( + e, + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { + result: 2, + .. + }) + ))); + }); + + // This event should have been seen in the relay + let expected_supported_version_2: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 2, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(relay_chain::relay_events().contains(&expected_supported_version_2)); + }); +} + +#[test] +fn receive_asset_with_no_sufficients_not_possible_if_non_existent_account() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should not have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 0); + }); + + // Send native token to fresh_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + fresh_account.into(), + 100 + )); + }); + + // Re-send tokens + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { + MockNet::reset(); + + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + // Evm account is self sufficient + ParaA::execute_with(|| { + assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // Evm account sufficient ref count increased by 1. + ParaA::execute_with(|| { + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); + }); + + ParaA::execute_with(|| { + // Remove the account from the evm context. + parachain::EVM::remove_account(&evm_account()); + // Evm account sufficient ref count decreased by 1. + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); +} + +#[test] +fn empty_account_should_not_be_reset() { + MockNet::reset(); + + // Test account has nonce 1 on genesis. + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send native token to evm_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + evm_account_id, + 100 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Empty the assets from the account. + // As this makes the account go below the `min_balance`, the account is considered dead + // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. + assert_ok!(parachain::Assets::transfer( + parachain::RuntimeOrigin::signed(evm_account_id), + source_id, + PARAALICE.into(), + 123 + )); + // Verify account asset balance is Zero. + assert_eq!( + parachain::Assets::balance(source_id, &evm_account_id.into()), + 0 + ); + // Because we no longer have consumer references, we can set the balance to Zero. + // This would reset the account if our ED were to be > than Zero. + assert_ok!(ParaBalances::force_set_balance( + parachain::RuntimeOrigin::root(), + evm_account_id, + 0, + )); + // Verify account native balance is Zero. + assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); + // Remove the account from the evm context. + // This decreases the sufficients reference by 1 and now is Zero. + parachain::EVM::remove_account(&evm_account()); + // Verify reference count. + let account = parachain::System::account(evm_account_id); + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // account.sufficients shall be 0 + assert_eq!(account.sufficients, 1); + assert_eq!(account.consumers, 0); + assert_eq!(account.providers, 1); + // We expect the account to be alive in a Zero ED context. + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // this shall be changed to 1 + assert_eq!(parachain::System::account_nonce(evm_account_id), 2); + }); +} + +#[test] +fn test_statemint_like() { + MockNet::reset(); + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + let statemint_asset_a_balances = Location::new( + 1, + [ + Parachain(4), + PalletInstance(5), + xcm::latest::prelude::GeneralIndex(0u128), + ], + ); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemint_asset_a_balances) + .expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"StatemintToken".to_vec(), + symbol: b"StatemintToken".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + Statemint::execute_with(|| { + // Set new prefix + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 300000000000000 + )); + + // This is needed, since the asset is created as non-sufficient + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send with new prefix + assert_ok!(StatemintChainPalletXcm::reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + xcm::latest::prelude::GeneralIndex(0), + ], + 123 + ) + .into() + ), + 0, + )); + }); + + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +#[test] +fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send relay chain asset to Alice in Parachain A + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_relay) + .clone() + .into() + ), + Box::new(([] /* Here */, 200).into()), + 0, + )); + }); + + Statemint::execute_with(|| { + // Set new prefix + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Send some native statemint tokens to sovereign for fees. + // We can't pay fees with USDC as the asset is minted as non-sufficient. + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Send statemint USDC asset to Alice in Parachain A + let parachain_beneficiary_from_statemint: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send with new prefix + assert_ok!(StatemintChainPalletXcm::reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_statemint) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + )); + }); + + let statemint_beneficiary = Location { + parents: 1, + interior: [ + Parachain(4), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + + // Alice has received 200 Relay assets + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + Statemint::execute_with(|| { + // Check that BOB's balance is empty before the transfer + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![]); + }); + + // Transfer USDC from Parachain A to Statemint using Relay asset as fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(statemint_beneficiary)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + }); + + ParaA::execute_with(|| { + // Alice has 100 USDC less + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 25 + ); + + // Alice has 100 relay asset less + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + println!("STATEMINT EVENTS: {:?}", parachain::para_events()); + // Check that BOB received 100 USDC on statemint + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); +} + +#[test] +fn transact_through_signed_multilocation() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4000.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000004000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // 100 transferred + assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); + + // 4000005186 refunded + assert_eq!(RelayBalances::free_balance(&derived), 4000005186); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + assert!(ParaBalances::free_balance(&derived) == 0); + + assert!(ParaBalances::free_balance(¶_a_account_20()) == 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: Some(overall_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + }); + + ParaB::execute_with(|| { + // Check the derived account was refunded + assert_eq!(ParaBalances::free_balance(&derived), 8993); + + // Check the transfer was executed + assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact { + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer went through + assert!( + ParaBalances::free_balance(&PARAALICE.into()) + == parachain_b_alice_balances_before + 100 + ); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer wasn't executed + assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let transfer_recipient = evm_account(); + let mut transfer_recipient_balance_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); + + // Add proxy ALICE -> derived + let _ = parachain::Proxy::add_proxy_delegate( + &PARAALICE.into(), + derived, + parachain::ProxyType::Any, + 0, + ); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer was executed + assert!( + ParaBalances::free_balance(&transfer_recipient.into()) + == transfer_recipient_balance_before + 100 + ); + }); +} + +#[test] +fn hrmp_init_accept_through_root() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_b_account(), + 1000u128 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp init channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: 2u32.into(), + proposed_max_capacity: 1, + proposed_max_message_size: 1 + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { + sender: 1u32.into(), + recipient: 2u32.into(), + proposed_max_capacity: 1u32, + proposed_max_message_size: 1u32, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); + ParaB::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp accept channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: 1u32.into() + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { + sender: 1u32.into(), + recipient: 2u32.into(), + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +#[test] +fn hrmp_close_works() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(Hrmp::force_open_hrmp_channel( + relay_chain::RuntimeOrigin::root(), + 1u32.into(), + 2u32.into(), + 1u32, + 1u32 + )); + assert_ok!(Hrmp::force_process_hrmp_open( + relay_chain::RuntimeOrigin::root(), + 1u32 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp close + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Close(HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into() + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { + by_parachain: 1u32.into(), + channel_id: HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into(), + }, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +use parity_scale_codec::{Decode, Encode}; +use sp_io::hashing::blake2_256; + +// Helper to derive accountIds +pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { + let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); + sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") +} diff --git a/tracing/2900/runtime/moonriver/Cargo.toml b/tracing/2900/runtime/moonriver/Cargo.toml new file mode 100644 index 00000000..42e5f0ea --- /dev/null +++ b/tracing/2900/runtime/moonriver/Cargo.toml @@ -0,0 +1,406 @@ +[package] +authors = { workspace = true } +build = "build.rs" +description = "Moonriver Runtime" +edition = "2021" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +name = "moonriver-runtime" +version = "0.8.4" + +[dependencies] +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +num_enum = { workspace = true } +rlp = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +sha3 = { workspace = true, optional = true } +smallvec = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } + +# Moonbeam +account = { workspace = true } +moonbeam-core-primitives = { workspace = true } +moonbeam-relay-encoder = { workspace = true } +moonbeam-runtime-common = { workspace = true } +precompile-utils = { workspace = true } +session-keys-primitives = { workspace = true } +xcm-primitives = { workspace = true } + +# Moonbeam pallets +moonbeam-xcm-benchmarks = { workspace = true } +pallet-asset-manager = { workspace = true } +pallet-author-mapping = { workspace = true } +pallet-crowdloan-rewards = { workspace = true } +pallet-erc20-xcm-bridge = { workspace = true } +pallet-ethereum-xcm = { workspace = true } +pallet-evm-chain-id = { workspace = true } +pallet-maintenance-mode = { workspace = true, features = ["xcm-support"] } +pallet-migrations = { workspace = true } +pallet-moonbeam-lazy-migrations = { workspace = true } +pallet-moonbeam-orbiters = { workspace = true } +pallet-parachain-staking = { workspace = true } +pallet-precompile-benchmarks = { workspace = true } +pallet-proxy-genesis-companion = { workspace = true } +pallet-randomness = { workspace = true } +pallet-xcm-transactor = { workspace = true } + +# Moonbeam precompiles +pallet-evm-precompile-author-mapping = { workspace = true } +pallet-evm-precompile-balances-erc20 = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } +pallet-evm-precompile-call-permit = { workspace = true } +pallet-evm-precompile-collective = { workspace = true } +pallet-evm-precompile-conviction-voting = { workspace = true } +pallet-evm-precompile-crowdloan-rewards = { workspace = true } +pallet-evm-precompile-gmp = { workspace = true } +pallet-evm-precompile-identity = { workspace = true } +pallet-evm-precompile-parachain-staking = { workspace = true } +pallet-evm-precompile-preimage = { workspace = true } +pallet-evm-precompile-proxy = { workspace = true } +pallet-evm-precompile-randomness = { workspace = true } +pallet-evm-precompile-referenda = { workspace = true } +pallet-evm-precompile-registry = { workspace = true } +pallet-evm-precompile-relay-encoder = { workspace = true } +pallet-evm-precompile-relay-verifier = { workspace = true } +pallet-evm-precompile-xcm-transactor = { workspace = true } +pallet-evm-precompile-xcm-utils = { workspace = true } +pallet-evm-precompile-xtokens = { workspace = true } +pallet-evm-precompileset-assets-erc20 = { workspace = true } + +# Moonbeam tracing +evm-tracing-events = { workspace = true, optional = true } +moonbeam-evm-tracer = { workspace = true, optional = true } +moonbeam-rpc-primitives-debug = { workspace = true } +moonbeam-rpc-primitives-txpool = { workspace = true } + +# Substrate +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-system-rpc-runtime-api = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true, features = ["insecure_zero_ed"] } +pallet-collective = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-identity = { workspace = true } +pallet-multisig = { workspace = true } +pallet-preimage = { workspace = true } +pallet-proxy = { workspace = true } +pallet-referenda = { workspace = true } +pallet-root-testing = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-society = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } +pallet-treasury = { workspace = true } +pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } +parity-scale-codec = { workspace = true, features = [ + "derive", + "max-encoded-len", + "chain-error", +] } +scale-info = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-slots = { workspace = true } +sp-core = { workspace = true } +sp-debug-derive = { workspace = true } +sp-inherents = { workspace = true } +sp-io = { workspace = true, features = ["improved_panic_error_reporting"] } +sp-offchain = { workspace = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true } +sp-std = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-version = { workspace = true } +sp-weights = { workspace = true } +sp-genesis-builder = { workspace = true } + +# Frontier +fp-evm = { workspace = true } +fp-rpc = { workspace = true } +fp-self-contained = { workspace = true, features = ["serde"] } +pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } +pallet-evm-precompile-blake2 = { workspace = true } +pallet-evm-precompile-bn128 = { workspace = true } +pallet-evm-precompile-dispatch = { workspace = true } +pallet-evm-precompile-modexp = { workspace = true } +pallet-evm-precompile-sha3fips = { workspace = true } +pallet-evm-precompile-simple = { workspace = true } + +# Polkadot / XCM +orml-traits = { workspace = true } +orml-xcm-support = { workspace = true } +orml-xtokens = { workspace = true } +pallet-xcm = { workspace = true } +pallet-xcm-benchmarks = { workspace = true, optional = true } +polkadot-core-primitives = { workspace = true } +polkadot-parachain = { workspace = true } +polkadot-runtime-common = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } +pallet-message-queue = { workspace = true } + +# Cumulus +cumulus-pallet-dmp-queue = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-xcm = { workspace = true } +cumulus-pallet-xcmp-queue = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-timestamp = { workspace = true } +cumulus-primitives-utility = { workspace = true } +parachain-info = { workspace = true } +parachains-common = { workspace = true } + +# Moonkit +async-backing-primitives = { workspace = true } +moonkit-xcm-primitives = { workspace = true } +nimbus-primitives = { workspace = true } +pallet-author-inherent = { workspace = true } +pallet-author-slot-filter = { workspace = true } +pallet-relay-storage-roots = { workspace = true } + +# Benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } +frame-try-runtime = { workspace = true, optional = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +default = ["std", "evm-tracing"] +std = [ + "account/std", + "async-backing-primitives/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "evm-tracing-events/std", + "fp-evm/std", + "fp-rpc/std", + "fp-self-contained/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "moonbeam-core-primitives/std", + "moonbeam-evm-tracer/std", + "moonbeam-relay-encoder/std", + "moonbeam-rpc-primitives-debug/std", + "moonbeam-rpc-primitives-txpool/std", + "moonbeam-runtime-common/std", + "moonbeam-xcm-benchmarks/std", + "moonkit-xcm-primitives/std", + "nimbus-primitives/std", + "orml-xtokens/std", + "pallet-asset-manager/std", + "pallet-assets/std", + "pallet-author-inherent/std", + "pallet-author-mapping/std", + "pallet-author-slot-filter/std", + "pallet-balances/std", + "pallet-collective/std", + "pallet-conviction-voting/std", + "pallet-crowdloan-rewards/std", + "pallet-erc20-xcm-bridge/std", + "pallet-evm-chain-id/std", + "pallet-ethereum-xcm/std", + "pallet-ethereum/std", + "pallet-evm-precompile-author-mapping/std", + "pallet-evm-precompile-balances-erc20/std", + "pallet-evm-precompile-batch/std", + "pallet-evm-precompile-call-permit/std", + "pallet-evm-precompile-collective/std", + "pallet-evm-precompile-conviction-voting/std", + "pallet-evm-precompile-parachain-staking/std", + "pallet-evm-precompile-preimage/std", + "pallet-evm-precompile-randomness/std", + "pallet-evm-precompile-referenda/std", + "pallet-evm-precompile-relay-encoder/std", + "pallet-evm-precompile-relay-verifier/std", + "pallet-evm-precompile-xcm-transactor/std", + "pallet-evm-precompile-xcm-utils/std", + "pallet-evm-precompile-xtokens/std", + "pallet-evm/std", + "pallet-identity/std", + "pallet-maintenance-mode/std", + "pallet-migrations/std", + "pallet-moonbeam-lazy-migrations/std", + "pallet-moonbeam-orbiters/std", + "pallet-multisig/std", + "pallet-parachain-staking/std", + "pallet-precompile-benchmarks/std", + "pallet-preimage/std", + "pallet-proxy-genesis-companion/std", + "pallet-proxy/std", + "pallet-randomness/std", + "pallet-referenda/std", + "pallet-relay-storage-roots/std", + "pallet-root-testing/std", + "pallet-scheduler/std", + "pallet-society/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-treasury/std", + "pallet-utility/std", + "pallet-whitelist/std", + "pallet-xcm-transactor/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parity-scale-codec/std", + "precompile-utils/std", + "scale-info/std", + "session-keys-primitives/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "sp-genesis-builder/std", + "strum/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-primitives/std", + "xcm/std", +] + +# Must be enabled for tracing runtimes only +evm-tracing = ["evm-tracing-events", "moonbeam-evm-tracer", "rlp", "sha3"] + +# Allow to print logs details (no wasm:stripped) +force-debug = ["sp-debug-derive/force-debug"] + +# Will be enabled by the `wasm-builder` when building the runtime for WASM. +runtime-wasm = [] + +# A feature that should be enabled when the runtime should be build for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller like logging for example. +on-chain-release-build = ["sp-api/disable-logging"] + +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "moonbeam-relay-encoder/runtime-benchmarks", + "moonbeam-runtime-common/runtime-benchmarks", + "moonbeam-xcm-benchmarks/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "pallet-asset-manager/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-author-inherent/runtime-benchmarks", + "pallet-author-mapping/runtime-benchmarks", + "pallet-author-slot-filter/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-crowdloan-rewards/runtime-benchmarks", + "pallet-ethereum-xcm/runtime-benchmarks", + "pallet-ethereum/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-identity/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", + "pallet-moonbeam-lazy-migrations/runtime-benchmarks", + "pallet-moonbeam-orbiters/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-parachain-staking/runtime-benchmarks", + "pallet-precompile-benchmarks/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-randomness/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-relay-storage-roots/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-society/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-treasury/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-transactor/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "session-keys-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +try-runtime = [ + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-dmp-queue/try-runtime", + "fp-self-contained/try-runtime", + "frame-executive/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime", + "moonbeam-runtime-common/try-runtime", + "pallet-asset-manager/try-runtime", + "pallet-author-mapping/try-runtime", + "pallet-author-slot-filter/try-runtime", + "pallet-balances/try-runtime", + "pallet-collective/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-maintenance-mode/try-runtime", + "pallet-migrations/try-runtime", + "pallet-moonbeam-lazy-migrations/try-runtime", + "pallet-parachain-staking/try-runtime", + "pallet-precompile-benchmarks/try-runtime", + "pallet-preimage/try-runtime", + "pallet-referenda/try-runtime", + "pallet-relay-storage-roots/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-society/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-whitelist/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-xcm/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-utility/try-runtime", + "pallet-transaction-payment/try-runtime", + "parachain-info/try-runtime", + "pallet-evm-chain-id/try-runtime", + "parachain-info/try-runtime", + "pallet-evm/try-runtime", + "pallet-ethereum/try-runtime", + "pallet-treasury/try-runtime", + "pallet-author-inherent/try-runtime", + "pallet-crowdloan-rewards/try-runtime", + "pallet-proxy/try-runtime", + "pallet-identity/try-runtime", + "orml-xtokens/try-runtime", + "pallet-assets/try-runtime", + "pallet-xcm-transactor/try-runtime", + "pallet-proxy-genesis-companion/try-runtime", + "pallet-moonbeam-orbiters/try-runtime", + "pallet-ethereum-xcm/try-runtime", + "pallet-randomness/try-runtime", + "pallet-whitelist/try-runtime", + "pallet-erc20-xcm-bridge/try-runtime", + "pallet-multisig/try-runtime", +] diff --git a/tracing/2900/runtime/moonriver/build.rs b/tracing/2900/runtime/moonriver/build.rs new file mode 100644 index 00000000..3934b9c5 --- /dev/null +++ b/tracing/2900/runtime/moonriver/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/tracing/2900/runtime/moonriver/src/asset_config.rs b/tracing/2900/runtime/moonriver/src/asset_config.rs new file mode 100644 index 00000000..ca1cce4d --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/asset_config.rs @@ -0,0 +1,219 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset configuration for Moonbase. +//! + +use super::{ + currency, governance, xcm_config, AccountId, AssetId, AssetManager, Assets, Balance, Balances, + OpenTechCommitteeInstance, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; + +use frame_support::{ + dispatch::GetDispatchInfo, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse}, + weights::Weight, +}; +use frame_system::{EnsureNever, EnsureRoot}; +use sp_core::H160; + +use parity_scale_codec::{Compact, Decode, Encode}; +use scale_info::TypeInfo; + +use sp_std::{ + convert::{From, Into}, + prelude::*, +}; + +// Number of items that can be destroyed with our configured max extrinsic proof size. +// x = (a - b) / c where: +// a: maxExtrinsic proof size +// b: base proof size for destroy_accounts in pallet_assets weights +// c: proof size for each item +// 656.87 = (3_407_872 - 8232) / 5180 +const REMOVE_ITEMS_LIMIT: u32 = 656; + +// Not to disrupt the previous asset instance, we assign () to Foreign +pub type ForeignAssetInstance = (); + +// For foreign assets, these parameters dont matter much +// as this will only be called by root with the forced arguments +// No deposit is substracted with those methods +parameter_types! { + pub const AssetDeposit: Balance = 100 * currency::MOVR * currency::SUPPLY_FACTOR; + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = currency::deposit(1,68); + pub const MetadataDepositPerByte: Balance = currency::deposit(0, 1); +} + +/// We allow Root and General Admin to execute privileged asset operations. +pub type AssetsForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +// Foreign assets +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = ConstU128<{ currency::deposit(1, 18) }>; + type WeightInfo = moonbeam_weights::pallet_assets::WeightInfo; + type RemoveItemsLimit = ConstU32<{ REMOVE_ITEMS_LIMIT }>; + type AssetIdParameter = Compact; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::{pallet_prelude::DispatchResult, transactional}; + +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + #[transactional] + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetRegistrarMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset.into(), + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + // Lastly, the metadata + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset.into(), + metadata.name, + metadata.symbol, + metadata.decimals, + metadata.is_frozen, + ) + } + + #[transactional] + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + // For us both of them (Foreign and Local) have the same annotated weight for a given + // witness + // We need to take the dispatch info from the destroy call, which is already annotated in + // the assets pallet + + // This is the dispatch info of destroy + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetRegistrarMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, + pub is_frozen: bool, +} + +pub type ForeignAssetModifierOrigin = EitherOfDiverse< + EnsureRoot, + EitherOfDiverse< + pallet_collective::EnsureProportionMoreThan, + governance::custom_origins::GeneralAdmin, + >, +>; +pub type LocalAssetModifierOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetRegistrarMetadata; + type ForeignAssetType = xcm_config::AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = ForeignAssetModifierOrigin; + type WeightInfo = moonbeam_weights::pallet_asset_manager::WeightInfo; +} + +// Instruct how to go from an H160 to an AssetID +// We just take the lowest 128 bits +impl AccountIdAssetIdConversion for Runtime { + /// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF + /// and by taking the lowest 128 bits as the assetId + fn account_to_asset_id(account: AccountId) -> Option<(Vec, AssetId)> { + let h160_account: H160 = account.into(); + let mut data = [0u8; 16]; + let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(4); + if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX { + data.copy_from_slice(id_part); + let asset_id: AssetId = u128::from_be_bytes(data).into(); + Some((prefix_part.to_vec(), asset_id)) + } else { + None + } + } + + // The opposite conversion + fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId { + let mut data = [0u8; 20]; + data[0..4].copy_from_slice(prefix); + data[4..20].copy_from_slice(&asset_id.to_be_bytes()); + AccountId::from(data) + } +} diff --git a/tracing/2900/runtime/moonriver/src/governance/councils.rs b/tracing/2900/runtime/moonriver/src/governance/councils.rs new file mode 100644 index 00000000..1c51423e --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/governance/councils.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; + +pub type TreasuryCouncilInstance = pallet_collective::Instance3; +pub type OpenTechCommitteeInstance = pallet_collective::Instance4; + +parameter_types! { + // TODO: Check value of this parameter + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for treasury council members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 3 * DAYS }>; + /// The maximum number of proposals that can be open in the treasury council at once. + type MaxProposals = ConstU32<20>; + /// The maximum number of treasury council members. + type MaxMembers = ConstU32<9>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Proposal = RuntimeCall; + /// The maximum amount of time (in blocks) for technical committee members to vote on motions. + /// Motions may end in fewer blocks if enough votes are cast to determine the result. + type MotionDuration = ConstU32<{ 14 * DAYS }>; + /// The maximum number of proposals that can be open in the technical committee at once. + type MaxProposals = ConstU32<100>; + /// The maximum number of technical committee members. + type MaxMembers = ConstU32<100>; + type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote; + type WeightInfo = moonbeam_weights::pallet_collective::WeightInfo; + type SetMembersOrigin = referenda::GeneralAdminOrRoot; + type MaxProposalWeight = MaxProposalWeight; +} diff --git a/tracing/2900/runtime/moonriver/src/governance/mod.rs b/tracing/2900/runtime/moonriver/src/governance/mod.rs new file mode 100644 index 00000000..36a2c6be --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/governance/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Governance configurations + +pub mod councils; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{ + custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller, +}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/tracing/2900/runtime/moonriver/src/governance/origins.rs b/tracing/2900/runtime/moonriver/src/governance/origins.rs new file mode 100644 index 00000000..ef4675d6 --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/governance/origins.rs @@ -0,0 +1,84 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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. + +//! Custom origins for governance interventions. +#![cfg_attr(not(feature = "std"), no_std)] +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive( + PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString, + )] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + /// Fast General Admin + FastGeneralAdmin, + } + + 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!( + ReferendumCanceller, + ReferendumKiller, + WhitelistedCaller, + GeneralAdmin, + FastGeneralAdmin, + ); +} diff --git a/tracing/2900/runtime/moonriver/src/governance/referenda.rs b/tracing/2900/runtime/moonriver/src/governance/referenda.rs new file mode 100644 index 00000000..8177e88e --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/governance/referenda.rs @@ -0,0 +1,100 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use crate::currency::*; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_conviction_voting::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub const SubmissionDeposit: Balance = 10 * MOVR * SUPPLY_FACTOR; + pub const UndecidingTimeout: BlockNumber = 21 * DAYS; +} + +// Origin for general admin or root +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; +// The policy allows for Root or FastGeneralAdmin. +pub type FastGeneralAdminOrRoot = EitherOf, origins::FastGeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_whitelist::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast< + Self::AccountId, + OpenTechCommitteeInstance, + 5, + 9, + >, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +impl pallet_referenda::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_referenda::WeightInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + 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; +} diff --git a/tracing/2900/runtime/moonriver/src/governance/tracks.rs b/tracing/2900/runtime/moonriver/src/governance/tracks.rs new file mode 100644 index 00000000..8dddbeca --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/governance/tracks.rs @@ -0,0 +1,193 @@ +// Copyright 2022 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 crate::currency::{KILOMOVR, MOVR, SUPPLY_FACTOR}; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +use pallet_referenda::Curve; +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 6] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 100 * KILOMOVR * SUPPLY_FACTOR, + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 10 * KILOMOVR * SUPPLY_FACTOR, + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 500 * MOVR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 10 * KILOMOVR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 20 * KILOMOVR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 5, + pallet_referenda::TrackInfo { + name: "fast_general_admin", + max_deciding: 10, + decision_deposit: 500 * MOVR * SUPPLY_FACTOR, + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(5, 14, percent(1), percent(0), percent(50)), + }, + ), +]; + +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(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks() + .into_iter() + .find(|(_, track)| track.name == "root") + { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in TRACKS_DATA { + assert!( + ::VoteLockingPeriod::get() + >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in TRACKS_DATA { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/tracing/2900/runtime/moonriver/src/lib.rs b/tracing/2900/runtime/moonriver/src/lib.rs new file mode 100644 index 00000000..f637f9c0 --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/lib.rs @@ -0,0 +1,1790 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! The Moonriver Runtime. +//! +//! Primary features of this runtime include: +//! * Ethereum compatibility +//! * Moonriver tokenomics + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use account::AccountId20; +use cumulus_pallet_parachain_system::{RelayChainStateProof, RelaychainDataProvider}; +use fp_rpc::TransactionStatus; + +// Re-export required by get! macro. +use cumulus_primitives_core::{relay_chain, AggregateMessageOrigin}; +#[cfg(feature = "std")] +pub use fp_evm::GenesisAccount; +pub use frame_support::traits::Get; +use frame_support::{ + construct_runtime, + dispatch::{DispatchClass, GetDispatchInfo, PostDispatchInfo}, + ensure, + pallet_prelude::DispatchResult, + parameter_types, + traits::{ + fungible::{Balanced, Credit, HoldConsideration, Inspect}, + tokens::imbalance::ResolveTo, + tokens::{PayFromAccount, UnityAssetBalanceConversion}, + ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Contains, EitherOfDiverse, + EqualPrivilegeOnly, Imbalance, InstanceFilter, LinearStoragePrice, OnFinalize, + OnUnbalanced, + }, + weights::{ + constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + PalletId, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +pub use moonbeam_core_primitives::{ + AccountId, AccountIndex, Address, AssetId, Balance, BlockNumber, DigestItem, Hash, Header, + Index, Signature, +}; +use moonbeam_rpc_primitives_txpool::TxPoolResponse; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_ethereum::Call::transact; +use pallet_ethereum::{PostLogContent, Transaction as EthereumTransaction}; +use pallet_evm::{ + Account as EVMAccount, EVMFungibleAdapter, EnsureAddressNever, EnsureAddressRoot, + FeeCalculator, GasWeightMapping, IdentityAddressMapping, + OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner, +}; +pub use pallet_parachain_staking::{weights::WeightInfo, InflationInfo, Range}; +use pallet_transaction_payment::{FungibleAdapter, Multiplier, TargetedFeeAdjustment}; +use pallet_treasury::TreasuryAccountId; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_api::impl_runtime_apis; +use sp_consensus_slots::Slot; +use sp_core::{OpaqueMetadata, H160, H256, U256}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + serde::{Deserialize, Serialize}, + traits::{ + BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentityLookup, + PostDispatchInfoOf, UniqueSaturatedInto, Zero, + }, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + }, + ApplyExtrinsicResult, DispatchErrorWithPostInfo, FixedPointNumber, Perbill, Permill, + Perquintill, SaturatedConversion, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +use smallvec::smallvec; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use nimbus_primitives::CanAuthor; + +mod precompiles; +pub use precompiles::{ + MoonriverPrecompiles, PrecompileName, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +pub type Precompiles = MoonriverPrecompiles; + +pub mod asset_config; +pub mod governance; +pub mod xcm_config; + +pub use governance::councils::*; + +/// MOVR, the native token, uses 18 decimals of precision. +pub mod currency { + use super::Balance; + + // Provide a common factor between runtimes based on a supply of 10_000_000 tokens. + pub const SUPPLY_FACTOR: Balance = 1; + + pub const WEI: Balance = 1; + pub const KILOWEI: Balance = 1_000; + pub const MEGAWEI: Balance = 1_000_000; + pub const GIGAWEI: Balance = 1_000_000_000; + pub const MICROMOVR: Balance = 1_000_000_000_000; + pub const MILLIMOVR: Balance = 1_000_000_000_000_000; + pub const MOVR: Balance = 1_000_000_000_000_000_000; + pub const KILOMOVR: Balance = 1_000_000_000_000_000_000_000; + + pub const TRANSACTION_BYTE_FEE: Balance = 1 * GIGAWEI * SUPPLY_FACTOR; + pub const STORAGE_BYTE_FEE: Balance = 100 * MICROMOVR * SUPPLY_FACTOR; + pub const WEIGHT_FEE: Balance = 50 * KILOWEI * SUPPLY_FACTOR; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 1 * MOVR * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE + } +} + +/// Maximum weight per block +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX) + .saturating_div(2) + .set_proof_size(relay_chain::MAX_POV_SIZE as u64); + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const WEEKS: BlockNumber = DAYS * 7; +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core datastructures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + pub type Block = generic::Block; + + impl_opaque_keys! { + pub struct SessionKeys { + pub nimbus: AuthorInherent, + pub vrf: session_keys_primitives::VrfSessionKey, + } + } +} + +/// This runtime version. +/// The spec_version is composed of 2x2 digits. The first 2 digits represent major changes +/// that can't be skipped, such as data migration upgrades. The last 2 digits represent minor +/// changes which can be skipped. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("moonriver"), + impl_name: create_runtime_str!("moonriver"), + authoring_version: 3, + spec_version: 2900, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 2, + state_version: 0, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +pub const NORMAL_WEIGHT: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_mul(3).saturating_div(4); +// Here we assume Ethereum's base fee of 21000 gas and convert to weight, but we +// subtract roughly the cost of a balance transfer from it (about 1/3 the cost) +// and some cost to account for per-byte-fee. +// TODO: we should use benchmarking's overhead feature to measure this +pub const EXTRINSIC_BASE_WEIGHT: Weight = Weight::from_parts(10000 * WEIGHT_PER_GAS, 0); + +pub struct RuntimeBlockWeights; +impl Get for RuntimeBlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .for_class(DispatchClass::Normal, |weights| { + weights.base_extrinsic = EXTRINSIC_BASE_WEIGHT; + weights.max_total = NORMAL_WEIGHT.into(); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = MAXIMUM_BLOCK_WEIGHT.into(); + weights.reserved = (MAXIMUM_BLOCK_WEIGHT - NORMAL_WEIGHT).into(); + }) + .avg_block_initialization(Perbill::from_percent(10)) + .build() + .expect("Provided BlockWeight definitions are valid, qed") + } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + /// We allow for 5 MB blocks. + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); +} + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Index; + /// The index type for blocks. + type Block = Block; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The output of the `Hashing` function. + type Hash = H256; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// The aggregated RuntimeTask type. + type RuntimeTask = RuntimeTask; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = ConstU32<256>; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type BlockWeights = RuntimeBlockWeights; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type BlockLength = BlockLength; + /// Runtime version. + type Version = Version; + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = RocksDbWeight; + type BaseCallFilter = MaintenanceMode; + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = ConstU16<1285>; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = moonbeam_weights::pallet_utility::WeightInfo; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<6000>; + type WeightInfo = moonbeam_weights::pallet_timestamp::WeightInfo; +} + +impl pallet_balances::Config for Runtime { + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 4]; + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<0>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = moonbeam_weights::pallet_balances::WeightInfo; +} + +pub struct DealWithFees(sp_std::marker::PhantomData); +impl OnUnbalanced>> for DealWithFees +where + R: pallet_balances::Config + pallet_treasury::Config, +{ + // this seems to be called for substrate-based transactions + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>>, + ) { + if let Some(fees) = fees_then_tips.next() { + // for fees, 80% are burned, 20% to the treasury + let (_, to_treasury) = fees.ration(80, 20); + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + + // handle tip if there is one + if let Some(tip) = fees_then_tips.next() { + // for now we use the same burn/treasury strategy used for regular fees + let (_, to_treasury) = tip.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced( + to_treasury, + ); + } + } + } + + // this is called from pallet_evm for Ethereum-based transactions + // (technically, it calls on_unbalanced, which calls this when non-zero) + fn on_nonzero_unbalanced(amount: Credit>) { + // Balances pallet automatically burns dropped Credits by decreasing + // total_supply accordingly + let (_, to_treasury) = amount.ration(80, 20); + ResolveTo::, pallet_balances::Pallet>::on_unbalanced(to_treasury); + } +} + +pub struct LengthToFee; +impl WeightToFeePolynomial for LengthToFee { + type Balance = Balance; + + fn polynomial() -> WeightToFeeCoefficients { + smallvec![ + WeightToFeeCoefficient { + degree: 1, + coeff_frac: Perbill::zero(), + coeff_integer: currency::TRANSACTION_BYTE_FEE, + negative: false, + }, + WeightToFeeCoefficient { + degree: 3, + coeff_frac: Perbill::zero(), + coeff_integer: 1 * currency::SUPPLY_FACTOR, + negative: false, + }, + ] + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = FungibleAdapter>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = ConstantMultiplier>; + type LengthToFee = LengthToFee; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; +} + +impl pallet_evm_chain_id::Config for Runtime {} + +/// Current approximation of the gas/s consumption considering +/// EVM execution over compiled WASM (on 4.4Ghz CPU). +/// Given the 500ms Weight, from which 75% only are used for transactions, +/// the total EVM execution gas limit is: GAS_PER_SECOND * 0.500 * 0.75 ~= 15_000_000. +pub const GAS_PER_SECOND: u64 = 40_000_000; + +/// Approximate ratio of the amount of Weight per Gas. +/// u64 works for approximations because Weight is a very small unit compared to gas. +pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND; + +parameter_types! { + pub BlockGasLimit: U256 + = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); + /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less + /// than this will decrease the weight and more will increase. + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(50); + /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to + /// change the fees more rapidly. This low value causes changes to occur slowly over time. + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(4, 1_000); + /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure + /// that combined with `AdjustmentVariable`, we can recover from the minimum. + /// See `multiplier_can_grow_from_zero` in integration_tests.rs. + /// This value is currently only used by pallet-transaction-payment as an assertion that the + /// next multiplier is always > min value. + pub MinimumMultiplier: Multiplier = Multiplier::from(1u128); + /// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act + /// as a safety net. + pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128); + pub PrecompilesValue: MoonriverPrecompiles = MoonriverPrecompiles::<_>::new(); + pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); + /// The amount of gas per pov. A ratio of 4 if we convert ref_time to gas and we compare + /// it with the pov_size for a block. E.g. + /// ceil( + /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS + /// ) + pub const GasLimitPovSizeRatio: u64 = 4; + /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT + /// The current definition of BLOCK_STORAGE_LIMIT is 40 KB, resulting in a value of 366. + pub GasLimitStorageGrowthRatio: u64 = 366; +} + +pub struct TransactionPaymentAsGasPrice; +impl FeeCalculator for TransactionPaymentAsGasPrice { + fn min_gas_price() -> (U256, Weight) { + // note: transaction-payment differs from EIP-1559 in that its tip and length fees are not + // scaled by the multiplier, which means its multiplier will be overstated when + // applied to an ethereum transaction + // note: transaction-payment uses both a congestion modifier (next_fee_multiplier, which is + // updated once per block in on_finalize) and a 'WeightToFee' implementation. Our + // runtime implements this as a 'ConstantModifier', so we can get away with a simple + // multiplication here. + // It is imperative that `saturating_mul_int` be performed as late as possible in the + // expression since it involves fixed point multiplication with a division by a fixed + // divisor. This leads to truncation and subsequent precision loss if performed too early. + // This can lead to min_gas_price being same across blocks even if the multiplier changes. + // There's still some precision loss when the final `gas_price` (used_gas * min_gas_price) + // is computed in frontier, but that's currently unavoidable. + let min_gas_price = TransactionPayment::next_fee_multiplier() + .saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128)); + ( + min_gas_price.into(), + ::DbWeight::get().reads(1), + ) + } +} + +/// Parameterized slow adjusting fee updated based on +/// https://w3f-research.readthedocs.io/en/latest/polkadot/overview/2-token-economics.html#-2.-slow-adjusting-mechanism // editorconfig-checker-disable-line +/// +/// The adjustment algorithm boils down to: +/// +/// diff = (previous_block_weight - target) / maximum_block_weight +/// next_multiplier = prev_multiplier * (1 + (v * diff) + ((v * diff)^2 / 2)) +/// assert(next_multiplier > min) +/// where: v is AdjustmentVariable +/// target is TargetBlockFullness +/// min is MinimumMultiplier +pub type SlowAdjustingFeeUpdate = TargetedFeeAdjustment< + R, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, +>; + +use frame_support::traits::FindAuthor; +//TODO It feels like this shold be able to work for any T: H160, but I tried for +// embarassingly long and couldn't figure that out. + +/// The author inherent provides a AccountId20, but pallet evm needs an H160. +/// This simple adapter makes the conversion. +pub struct FindAuthorAdapter(sp_std::marker::PhantomData); + +impl FindAuthor for FindAuthorAdapter +where + Inner: FindAuthor, +{ + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + Inner::find_author(digests).map(Into::into) + } +} + +moonbeam_runtime_common::impl_on_charge_evm_transaction!(); + +impl pallet_evm::Config for Runtime { + type FeeCalculator = TransactionPaymentAsGasPrice; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = MoonriverPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = EthereumChainId; + type OnChargeTransaction = OnChargeEVMTransaction>; + type BlockGasLimit = BlockGasLimit; + type FindAuthor = FindAuthorAdapter; + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = Timestamp; + type WeightInfo = moonbeam_weights::pallet_evm::WeightInfo; +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = NORMAL_DISPATCH_RATIO * RuntimeBlockWeights::get().max_block; +} + +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<50>; + type WeightInfo = moonbeam_weights::pallet_scheduler::WeightInfo; + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +parameter_types! { + pub const PreimageBaseDeposit: Balance = 5 * currency::MOVR * currency::SUPPLY_FACTOR ; + pub const PreimageByteDeposit: Balance = currency::STORAGE_BYTE_FEE; + pub const PreimageHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + +impl pallet_preimage::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_preimage::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const TreasuryId: PalletId = PalletId(*b"py/trsry"); + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +type TreasuryApproveOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +type TreasuryRejectOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionMoreThan, +>; + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + // At least three-fifths majority of the council is required (or root) to approve a proposal + type ApproveOrigin = TreasuryApproveOrigin; + // More than half of the council is required (or root) to reject a proposal + type RejectOrigin = TreasuryRejectOrigin; + type RuntimeEvent = RuntimeEvent; + // If spending proposal rejected, transfer proposer bond to treasury + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ConstU128<{ 1 * currency::MOVR * currency::SUPPLY_FACTOR }>; + type SpendPeriod = ConstU32<{ 6 * DAYS }>; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = ConstU32<100>; + type WeightInfo = moonbeam_weights::pallet_treasury::WeightInfo; + type SpendFunds = (); + type ProposalBondMaximum = (); + #[cfg(not(feature = "runtime-benchmarks"))] + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Disabled, no spending + #[cfg(feature = "runtime-benchmarks")] + type SpendOrigin = + frame_system::EnsureWithSuccess, AccountId, benches::MaxBalance>; + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<{ 30 * DAYS }>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +parameter_types! { + pub const MaxSubAccounts: u32 = 100; + pub const MaxAdditionalFields: u32 = 100; + pub const MaxRegistrars: u32 = 20; + pub const PendingUsernameExpiration: u32 = 7 * DAYS; + pub const MaxSuffixLength: u32 = 7; + pub const MaxUsernameLength: u32 = 32; +} + +type IdentityForceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type IdentityRegistrarOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_identity::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + // Add one item in storage and take 258 bytes + type BasicDeposit = ConstU128<{ currency::deposit(1, 258) }>; + // Does not add any item to the storage but takes 1 bytes + type ByteDeposit = ConstU128<{ currency::deposit(0, 1) }>; + // Add one item in storage and take 53 bytes + type SubAccountDeposit = ConstU128<{ currency::deposit(1, 53) }>; + type MaxSubAccounts = MaxSubAccounts; + type IdentityInformation = pallet_identity::legacy::IdentityInfo; + type MaxRegistrars = MaxRegistrars; + type Slashed = Treasury; + type ForceOrigin = IdentityForceOrigin; + type RegistrarOrigin = IdentityRegistrarOrigin; + type OffchainSignature = Signature; + type SigningPublicKey = ::Signer; + type UsernameAuthorityOrigin = EnsureRoot; + type PendingUsernameExpiration = PendingUsernameExpiration; + type MaxSuffixLength = MaxSuffixLength; + type MaxUsernameLength = MaxUsernameLength; + type WeightInfo = moonbeam_weights::pallet_identity::WeightInfo; +} + +pub struct TransactionConverter; + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> opaque::UncheckedExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + opaque::UncheckedExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will immediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = ParachainInfo; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; + type ConsensusHook = cumulus_pallet_parachain_system::ExpectParentIncluded; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_parachain_system::weights::SubstrateWeight; +} + +impl parachain_info::Config for Runtime {} + +pub struct OnNewRound; +impl pallet_parachain_staking::OnNewRound for OnNewRound { + fn on_new_round(round_index: pallet_parachain_staking::RoundIndex) -> Weight { + MoonbeamOrbiters::on_new_round(round_index) + } +} +pub struct PayoutCollatorOrOrbiterReward; +impl pallet_parachain_staking::PayoutCollatorReward for PayoutCollatorOrOrbiterReward { + fn payout_collator_reward( + for_round: pallet_parachain_staking::RoundIndex, + collator_id: AccountId, + amount: Balance, + ) -> Weight { + let extra_weight = + if MoonbeamOrbiters::is_collator_pool_with_active_orbiter(for_round, collator_id) { + MoonbeamOrbiters::distribute_rewards(for_round, collator_id, amount) + } else { + ParachainStaking::mint_collator_reward(for_round, collator_id, amount) + }; + + ::DbWeight::get() + .reads(1) + .saturating_add(extra_weight) + } +} + +pub struct OnInactiveCollator; +impl pallet_parachain_staking::OnInactiveCollator for OnInactiveCollator { + fn on_inactive_collator( + collator_id: AccountId, + round: pallet_parachain_staking::RoundIndex, + ) -> Result> { + let extra_weight = if !MoonbeamOrbiters::is_collator_pool_with_active_orbiter( + round, + collator_id.clone(), + ) { + ParachainStaking::go_offline_inner(collator_id)?; + ::WeightInfo::go_offline( + pallet_parachain_staking::MAX_CANDIDATES, + ) + } else { + Weight::zero() + }; + + Ok(::DbWeight::get() + .reads(1) + .saturating_add(extra_weight)) + } +} + +type MonetaryGovernanceOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +/// TODO: +/// Temporary type that we should replace by RelayChainSlotProvider once async backing is enabled. +pub struct StakingRoundSlotProvider; +impl Get for StakingRoundSlotProvider { + fn get() -> Slot { + let block_number: u64 = frame_system::pallet::Pallet::::block_number().into(); + Slot::from(block_number) + } +} + +impl pallet_parachain_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MonetaryGovernanceOrigin = MonetaryGovernanceOrigin; + /// Minimum round length is 2 minutes (10 * 12 second block times) + type MinBlocksPerRound = ConstU32<10>; + /// If a collator doesn't produce any block on this number of rounds, it is notified as inactive + type MaxOfflineRounds = ConstU32<2>; + /// Rounds before the collator leaving the candidates request can be executed + type LeaveCandidatesDelay = ConstU32<24>; + /// Rounds before the candidate bond increase/decrease can be executed + type CandidateBondLessDelay = ConstU32<24>; + /// Rounds before the delegator exit can be executed + type LeaveDelegatorsDelay = ConstU32<24>; + /// Rounds before the delegator revocation can be executed + type RevokeDelegationDelay = ConstU32<24>; + /// Rounds before the delegator bond increase/decrease can be executed + type DelegationBondLessDelay = ConstU32<24>; + /// Rounds before the reward is paid + type RewardPaymentDelay = ConstU32<2>; + /// Minimum collators selected per round, default at genesis and minimum forever after + type MinSelectedCandidates = ConstU32<8>; + /// Maximum top delegations per candidate + type MaxTopDelegationsPerCandidate = ConstU32<300>; + /// Maximum bottom delegations per candidate + type MaxBottomDelegationsPerCandidate = ConstU32<50>; + /// Maximum delegations per delegator + type MaxDelegationsPerDelegator = ConstU32<100>; + /// Minimum stake required to be reserved to be a candidate + type MinCandidateStk = ConstU128<{ 500 * currency::MOVR * currency::SUPPLY_FACTOR }>; + /// Minimum stake required to be reserved to be a delegator + type MinDelegation = ConstU128<{ 5 * currency::MOVR * currency::SUPPLY_FACTOR }>; + type BlockAuthor = AuthorInherent; + type OnCollatorPayout = (); + type PayoutCollatorReward = PayoutCollatorOrOrbiterReward; + type OnInactiveCollator = OnInactiveCollator; + type OnNewRound = OnNewRound; + type SlotProvider = StakingRoundSlotProvider; + type WeightInfo = moonbeam_weights::pallet_parachain_staking::WeightInfo; + type MaxCandidates = ConstU32<200>; + type SlotDuration = ConstU64<12_000>; + type BlockTime = ConstU64<12_000>; +} + +impl pallet_author_inherent::Config for Runtime { + type SlotBeacon = RelaychainDataProvider; + type AccountLookup = MoonbeamOrbiters; + type CanAuthor = AuthorFilter; + type AuthorId = AccountId; + type WeightInfo = moonbeam_weights::pallet_author_inherent::WeightInfo; +} + +impl pallet_author_slot_filter::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RandomnessSource = Randomness; + type PotentialAuthors = ParachainStaking; + type WeightInfo = moonbeam_weights::pallet_author_slot_filter::WeightInfo; +} + +parameter_types! { + pub const InitializationPayment: Perbill = Perbill::from_percent(30); + pub const RelaySignaturesThreshold: Perbill = Perbill::from_percent(100); + pub const SignatureNetworkIdentifier: &'static [u8] = b"moonriver-"; + +} + +impl pallet_crowdloan_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Initialized = ConstBool; + type InitializationPayment = InitializationPayment; + type MaxInitContributors = ConstU32<500>; + type MinimumReward = ConstU128<0>; + type RewardCurrency = Balances; + type RelayChainAccountId = [u8; 32]; + type RewardAddressAssociateOrigin = EnsureSigned; + type RewardAddressChangeOrigin = EnsureSigned; + type RewardAddressRelayVoteThreshold = RelaySignaturesThreshold; + type SignatureNetworkIdentifier = SignatureNetworkIdentifier; + type VestingBlockNumber = relay_chain::BlockNumber; + type VestingBlockProvider = RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_crowdloan_rewards::WeightInfo; +} + +// This is a simple session key manager. It should probably either work with, or be replaced +// entirely by pallet sessions +impl pallet_author_mapping::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DepositCurrency = Balances; + type DepositAmount = ConstU128<{ 100 * currency::MOVR * currency::SUPPLY_FACTOR }>; + type Keys = session_keys_primitives::VrfId; + type WeightInfo = moonbeam_weights::pallet_author_mapping::WeightInfo; +} + +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, +)] +pub enum ProxyType { + /// All calls can be proxied. This is the trivial/most permissive filter. + Any = 0, + /// Only extrinsics that do not transfer funds. + NonTransfer = 1, + /// Only extrinsics related to governance (democracy and collectives). + Governance = 2, + /// Only extrinsics related to staking. + Staking = 3, + /// Allow to veto an announced proxy call. + CancelProxy = 4, + /// Allow extrinsic related to Balances. + Balances = 5, + /// Allow extrinsic related to AuthorMapping. + AuthorMapping = 6, + /// Allow extrinsic related to IdentityJudgement. + IdentityJudgement = 7, +} + +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +fn is_governance_precompile(precompile_name: &precompiles::PrecompileName) -> bool { + matches!( + precompile_name, + PrecompileName::TreasuryCouncilInstance + | PrecompileName::PreimagePrecompile + | PrecompileName::ReferendaPrecompile + | PrecompileName::ConvictionVotingPrecompile + | PrecompileName::OpenTechCommitteeInstance + ) +} + +// Be careful: Each time this filter is modified, the substrate filter must also be modified +// consistently. +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType { + fn is_evm_proxy_call_allowed( + &self, + call: &pallet_evm_precompile_proxy::EvmSubCall, + recipient_has_code: bool, + gas: u64, + ) -> precompile_utils::EvmResult { + Ok(match self { + ProxyType::Any => { + match PrecompileName::from_address(call.to.0) { + // Any precompile that can execute a subcall should be forbidden here, + // to ensure that unauthorized smart contract can't be called + // indirectly. + // To be safe, we only allow the precompiles we need. + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile, + ) => true, + Some(ref precompile) if is_governance_precompile(precompile) => true, + // All non-whitelisted precompiles are forbidden + Some(_) => false, + // Allow evm transfer to "simple" account (no code nor precompile) + // For the moment, no smart contract other than precompiles is allowed. + // In the future, we may create a dynamic whitelist to authorize some audited + // smart contracts through governance. + None => { + // If the address is not recognized, allow only evm transfert to "simple" + // accounts (no code nor precompile). + // Note: Checking the presence of the code is not enough because some + // precompiles have no code. + !recipient_has_code + && !precompile_utils::precompile_set::is_precompile_or_fail::( + call.to.0, gas, + )? + } + } + } + ProxyType::NonTransfer => { + call.value == U256::zero() + && match PrecompileName::from_address(call.to.0) { + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile, + ) => true, + Some(ref precompile) if is_governance_precompile(precompile) => true, + _ => false, + } + } + ProxyType::Governance => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(ref precompile) if is_governance_precompile(precompile) + ) + } + ProxyType::Staking => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some( + PrecompileName::AuthorMappingPrecompile + | PrecompileName::ParachainStakingPrecompile + ) + ) + } + // The proxy precompile does not contain method cancel_proxy + ProxyType::CancelProxy => false, + ProxyType::Balances => { + // Allow only "simple" accounts as recipient (no code nor precompile). + // Note: Checking the presence of the code is not enough because some precompiles + // have no code. + !recipient_has_code + && !precompile_utils::precompile_set::is_precompile_or_fail::( + call.to.0, gas, + )? + } + ProxyType::AuthorMapping => { + call.value == U256::zero() + && matches!( + PrecompileName::from_address(call.to.0), + Some(PrecompileName::AuthorMappingPrecompile) + ) + } + // There is no identity precompile + ProxyType::IdentityJudgement => false, + }) + } +} + +// Be careful: Each time this filter is modified, the EVM filter must also be modified consistently. +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => { + matches!( + c, + RuntimeCall::System(..) + | RuntimeCall::ParachainSystem(..) + | RuntimeCall::Timestamp(..) + | RuntimeCall::ParachainStaking(..) + | RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Identity(..) + | RuntimeCall::Utility(..) + | RuntimeCall::Proxy(..) | RuntimeCall::AuthorMapping(..) + | RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::claim { .. } + ) + ) + } + ProxyType::Governance => matches!( + c, + RuntimeCall::Referenda(..) + | RuntimeCall::Preimage(..) + | RuntimeCall::ConvictionVoting(..) + | RuntimeCall::TreasuryCouncilCollective(..) + | RuntimeCall::OpenTechCommitteeCollective(..) + | RuntimeCall::Utility(..) + ), + ProxyType::Staking => matches!( + c, + RuntimeCall::ParachainStaking(..) + | RuntimeCall::Utility(..) + | RuntimeCall::AuthorMapping(..) + | RuntimeCall::MoonbeamOrbiters(..) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) + ), + ProxyType::Balances => { + matches!(c, RuntimeCall::Balances(..) | RuntimeCall::Utility(..)) + } + ProxyType::AuthorMapping => matches!(c, RuntimeCall::AuthorMapping(..)), + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) + | RuntimeCall::Utility(..) + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + _ => false, + } + } +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + // One storage item; key size 32, value size 8 + type ProxyDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 21 bytes (20 bytes AccountId + 1 byte sizeof(ProxyType)). + type ProxyDepositFactor = ConstU128<{ currency::deposit(0, 21) }>; + type MaxProxies = ConstU32<32>; + type WeightInfo = moonbeam_weights::pallet_proxy::WeightInfo; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU128<{ currency::deposit(1, 8) }>; + // Additional storage item size of 56 bytes: + // - 20 bytes AccountId + // - 32 bytes Hasher (Blake2256) + // - 4 bytes BlockNumber (u32) + type AnnouncementDepositFactor = ConstU128<{ currency::deposit(0, 56) }>; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MigrationsList = (moonbeam_runtime_common::migrations::CommonMigrations,); + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_moonbeam_lazy_migrations::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_moonbeam_lazy_migrations::WeightInfo; +} + +/// Maintenance mode Call filter +pub struct MaintenanceFilter; +impl Contains for MaintenanceFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(_) => false, + RuntimeCall::Balances(_) => false, + RuntimeCall::CrowdloanRewards(_) => false, + RuntimeCall::Ethereum(_) => false, + RuntimeCall::EVM(_) => false, + RuntimeCall::Identity(_) => false, + RuntimeCall::XTokens(_) => false, + RuntimeCall::ParachainStaking(_) => false, + RuntimeCall::MoonbeamOrbiters(_) => false, + RuntimeCall::PolkadotXcm(_) => false, + RuntimeCall::Treasury(_) => false, + RuntimeCall::XcmTransactor(_) => false, + RuntimeCall::EthereumXcm(_) => false, + _ => true, + } + } +} + +/// Normal Call Filter +/// We dont allow to create nor mint assets, this for now is disabled +/// We only allow transfers. For now creation of assets will go through +/// asset-manager, while minting/burning only happens through xcm messages +/// This can change in the future +pub struct NormalFilter; +impl Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + RuntimeCall::Assets(method) => match method { + pallet_assets::Call::transfer { .. } => true, + pallet_assets::Call::transfer_keep_alive { .. } => true, + pallet_assets::Call::approve_transfer { .. } => true, + pallet_assets::Call::transfer_approved { .. } => true, + pallet_assets::Call::cancel_approval { .. } => true, + pallet_assets::Call::destroy_accounts { .. } => true, + pallet_assets::Call::destroy_approvals { .. } => true, + pallet_assets::Call::finish_destroy { .. } => true, + _ => false, + }, + // We just want to enable this in case of live chains, since the default version + // is populated at genesis + RuntimeCall::PolkadotXcm(method) => match method { + pallet_xcm::Call::force_default_xcm_version { .. } => true, + _ => false, + }, + // We filter anonymous proxy as they make "reserve" inconsistent + // See: https://github.com/paritytech/substrate/blob/37cca710eed3dadd4ed5364c7686608f5175cce1/frame/proxy/src/lib.rs#L270 // editorconfig-checker-disable-line + RuntimeCall::Proxy(method) => match method { + pallet_proxy::Call::create_pure { .. } => false, + pallet_proxy::Call::kill_pure { .. } => false, + pallet_proxy::Call::proxy { real, .. } => { + !pallet_evm::AccountCodes::::contains_key(H160::from(*real)) + } + _ => true, + }, + // Filtering the EVM prevents possible re-entrancy from the precompiles which could + // lead to unexpected scenarios. + // See https://github.com/PureStake/sr-moonbeam/issues/30 + // Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so + // this can be seen as an additional security + RuntimeCall::EVM(_) => false, + RuntimeCall::Treasury( + pallet_treasury::Call::spend { .. } + | pallet_treasury::Call::payout { .. } + | pallet_treasury::Call::check_status { .. } + | pallet_treasury::Call::void_spend { .. }, + ) => false, + _ => true, + } + } +} + +pub struct XcmExecutionManager; +impl moonkit_xcm_primitives::PauseXcmExecution for XcmExecutionManager { + fn suspend_xcm_execution() -> DispatchResult { + XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root()) + } + fn resume_xcm_execution() -> DispatchResult { + XcmpQueue::resume_xcm_execution(RuntimeOrigin::root()) + } +} + +impl pallet_maintenance_mode::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NormalCallFilter = NormalFilter; + type MaintenanceCallFilter = MaintenanceFilter; + type MaintenanceOrigin = + pallet_collective::EnsureProportionAtLeast; + type XcmExecutionManager = XcmExecutionManager; +} + +impl pallet_proxy_genesis_companion::Config for Runtime { + type ProxyType = ProxyType; +} + +parameter_types! { + pub OrbiterReserveIdentifier: [u8; 4] = [b'o', b'r', b'b', b'i']; +} + +type AddCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; +type DelCollatorOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_moonbeam_orbiters::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AccountLookup = AuthorMapping; + type AddCollatorOrigin = AddCollatorOrigin; + type Currency = Balances; + type DelCollatorOrigin = DelCollatorOrigin; + /// Maximum number of orbiters per collator + type MaxPoolSize = ConstU32<8>; + /// Maximum number of round to keep on storage + type MaxRoundArchive = ConstU32<4>; + type OrbiterReserveIdentifier = OrbiterReserveIdentifier; + type RotatePeriod = ConstU32<3>; + /// Round index type. + type RoundIndex = pallet_parachain_staking::RoundIndex; + type WeightInfo = moonbeam_weights::pallet_moonbeam_orbiters::WeightInfo; +} + +/// Only callable after `set_validation_data` is called which forms this proof the same way +fn relay_chain_state_proof() -> RelayChainStateProof { + let relay_storage_root = ParachainSystem::validation_data() + .expect("set in `set_validation_data`") + .relay_parent_storage_root; + let relay_chain_state = + ParachainSystem::relay_state_proof().expect("set in `set_validation_data`"); + RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) + .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") +} + +pub struct BabeDataGetter; +impl pallet_randomness::GetBabeData> for BabeDataGetter { + // Tolerate panic here because only ever called in inherent (so can be omitted) + fn get_epoch_index() -> u64 { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + const BENCHMARKING_NEW_EPOCH: u64 = 10u64; + return BENCHMARKING_NEW_EPOCH; + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::EPOCH_INDEX) + .ok() + .flatten() + .expect("expected to be able to read epoch index from relay chain state proof") + } + fn get_epoch_randomness() -> Option { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = ParachainSystem::validation_data(); + let _relay_chain_state = ParachainSystem::relay_state_proof(); + let benchmarking_babe_output = Hash::default(); + return Some(benchmarking_babe_output); + } + relay_chain_state_proof() + .read_optional_entry(relay_chain::well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) + .ok() + .flatten() + } +} + +impl pallet_randomness::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddressMapping = sp_runtime::traits::ConvertInto; + type Currency = Balances; + type BabeDataGetter = BabeDataGetter; + type VrfKeyLookup = AuthorMapping; + type Deposit = ConstU128<{ 1 * currency::MOVR * currency::SUPPLY_FACTOR }>; + type MaxRandomWords = ConstU8<100>; + type MinBlockDelay = ConstU32<2>; + type MaxBlockDelay = ConstU32<2_000>; + type BlockExpirationDelay = ConstU32<10_000>; + type EpochExpirationDelay = ConstU64<10_000>; + type WeightInfo = moonbeam_weights::pallet_randomness::WeightInfo; +} + +impl pallet_root_testing::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +parameter_types! { + // One storage item; key size is 32 + 20; value is size 4+4+16+20 bytes = 44 bytes. + pub const DepositBase: Balance = currency::deposit(1, 96); + // Additional storage item size of 20 bytes. + pub const DepositFactor: Balance = currency::deposit(0, 20); + pub const MaxSignatories: u32 = 100; +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = MaxSignatories; + type WeightInfo = moonbeam_weights::pallet_multisig::WeightInfo; +} + +impl pallet_relay_storage_roots::Config for Runtime { + type MaxStorageRoots = ConstU32<30>; + type RelaychainStateProvider = cumulus_pallet_parachain_system::RelaychainDataProvider; + type WeightInfo = moonbeam_weights::pallet_relay_storage_roots::WeightInfo; +} + +impl pallet_precompile_benchmarks::Config for Runtime { + type WeightInfo = moonbeam_weights::pallet_precompile_benchmarks::WeightInfo; +} + +construct_runtime! { + pub enum Runtime + { + // System support stuff. + System: frame_system::{Pallet, Call, Storage, Config, Event} = 0, + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 1, + // Previously 2: pallet_randomness_collective_flip + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 3, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 4, + RootTesting: pallet_root_testing::{Pallet, Call, Storage, Event} = 5, + + // Monetary stuff. + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Config, Event} = 11, + + // Consensus support. + ParachainStaking: pallet_parachain_staking::{Pallet, Call, Storage, Event, Config} = 20, + AuthorInherent: pallet_author_inherent::{Pallet, Call, Storage, Inherent} = 21, + AuthorFilter: pallet_author_slot_filter::{Pallet, Call, Storage, Event, Config} = 22, + AuthorMapping: pallet_author_mapping::{Pallet, Call, Config, Storage, Event} = 23, + MoonbeamOrbiters: pallet_moonbeam_orbiters::{Pallet, Call, Storage, Event} = 24, + + // Handy utilities. + Utility: pallet_utility::{Pallet, Call, Event} = 30, + Proxy: pallet_proxy::{Pallet, Call, Storage, Event} = 31, + MaintenanceMode: pallet_maintenance_mode::{Pallet, Call, Config, Storage, Event} = 32, + Identity: pallet_identity::{Pallet, Call, Storage, Event} = 33, + Migrations: pallet_migrations::{Pallet, Storage, Config, Event} = 34, + ProxyGenesisCompanion: pallet_proxy_genesis_companion::{Pallet, Config} = 35, + Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 36, + MoonbeamLazyMigrations: pallet_moonbeam_lazy_migrations::{Pallet, Call, Storage} = 37, + + // Sudo was previously index 40 + + // Ethereum compatibility + EthereumChainId: pallet_evm_chain_id::{Pallet, Storage, Config} = 50, + EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 51, + Ethereum: pallet_ethereum::{Pallet, Call, Storage, Event, Origin, Config} = 52, + + // Governance stuff. + Scheduler: pallet_scheduler::{Pallet, Storage, Event, Call} = 60, + // Democracy: 61, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event, HoldReason} = 62, + ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event} = 63, + Referenda: pallet_referenda::{Pallet, Call, Storage, Event} = 64, + Origins: governance::custom_origins::{Origin} = 65, + Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event} = 66, + + // Council stuff. + // CouncilCollective: 70 + // TechCommitteeCollective: 71, + TreasuryCouncilCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 72, + OpenTechCommitteeCollective: + pallet_collective::::{Pallet, Call, Storage, Event, Origin, Config} = 73, + + // Treasury stuff. + Treasury: pallet_treasury::{Pallet, Storage, Config, Event, Call} = 80, + + // Crowdloan stuff. + CrowdloanRewards: pallet_crowdloan_rewards::{Pallet, Call, Config, Storage, Event} = 90, + + // XCM Stuff + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Storage, Event} = 100, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 101, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 102, + PolkadotXcm: pallet_xcm::{Pallet, Storage, Call, Event, Origin, Config} = 103, + Assets: pallet_assets::{Pallet, Call, Storage, Event} = 104, + AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event} = 105, + XTokens: orml_xtokens::{Pallet, Call, Storage, Event} = 106, + XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event} = 107, + // Previously 108: pallet_assets:: + EthereumXcm: pallet_ethereum_xcm::{Pallet, Call, Storage, Origin} = 109, + Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 110, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event} = 111, + RelayStorageRoots: pallet_relay_storage_roots::{Pallet, Storage} = 112, + PrecompileBenchmarks: pallet_precompile_benchmarks::{Pallet} = 113, + + // Randomness + Randomness: pallet_randomness::{Pallet, Call, Storage, Event, Inherent} = 120, + } +} + +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_support::parameter_types! { + pub const MaxBalance: crate::Balance = crate::Balance::max_value(); + } + + frame_benchmarking::define_benchmarks!( + [pallet_utility, Utility] + [pallet_timestamp, Timestamp] + [pallet_balances, Balances] + [pallet_evm, EVM] + [pallet_assets, Assets] + [pallet_parachain_staking, ParachainStaking] + [pallet_scheduler, Scheduler] + [pallet_treasury, Treasury] + [pallet_author_inherent, AuthorInherent] + [pallet_author_slot_filter, AuthorFilter] + [pallet_crowdloan_rewards, CrowdloanRewards] + [pallet_author_mapping, AuthorMapping] + [pallet_proxy, Proxy] + [pallet_identity, Identity] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_asset_manager, AssetManager] + [pallet_xcm_transactor, XcmTransactor] + [pallet_moonbeam_orbiters, MoonbeamOrbiters] + [pallet_randomness, Randomness] + [pallet_conviction_voting, ConvictionVoting] + [pallet_referenda, Referenda] + [pallet_preimage, Preimage] + [pallet_whitelist, Whitelist] + [pallet_multisig, Multisig] + [pallet_moonbeam_lazy_migrations, MoonbeamLazyMigrations] + [pallet_relay_storage_roots, RelayStorageRoots] + ); +} + +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + fp_self_contained::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = + fp_self_contained::CheckedExtrinsic; +/// Executive: handles dispatch to the various pallets. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +// All of our runtimes share most of their Runtime API implementations. +// We use a macro to implement this common part and add runtime-specific additional implementations. +// This macro expands to : +// ``` +// impl_runtime_apis! { +// // All impl blocks shared between all runtimes. +// +// // Specific impls provided to the `impl_runtime_apis_plus_common!` macro. +// } +// ``` +moonbeam_runtime_common::impl_runtime_apis_plus_common! { + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + xt: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + // Filtered calls should not enter the tx pool as they'll fail if inserted. + // If this call is not allowed, we return early. + if !::BaseCallFilter::contains(&xt.0.function) { + return InvalidTransaction::Call.into(); + } + + // This runtime uses Substrate's pallet transaction payment. This + // makes the chain feel like a standard Substrate chain when submitting + // frame transactions and using Substrate ecosystem tools. It has the downside that + // transaction are not prioritized by gas_price. The following code reprioritizes + // transactions to overcome this. + // + // A more elegant, ethereum-first solution is + // a pallet that replaces pallet transaction payment, and allows users + // to directly specify a gas price rather than computing an effective one. + // #HopefullySomeday + + // First we pass the transactions to the standard FRAME executive. This calculates all the + // necessary tags, longevity and other properties that we will leave unchanged. + // This also assigns some priority that we don't care about and will overwrite next. + let mut intermediate_valid = Executive::validate_transaction(source, xt.clone(), block_hash)?; + + let dispatch_info = xt.get_dispatch_info(); + + // If this is a pallet ethereum transaction, then its priority is already set + // according to gas price from pallet ethereum. If it is any other kind of transaction, + // we modify its priority. + Ok(match &xt.0.function { + RuntimeCall::Ethereum(transact { .. }) => intermediate_valid, + _ if dispatch_info.class != DispatchClass::Normal => intermediate_valid, + _ => { + let tip = match xt.0.signature { + None => 0, + Some((_, _, ref signed_extra)) => { + // Yuck, this depends on the index of charge transaction in Signed Extra + let charge_transaction = &signed_extra.7; + charge_transaction.tip() + } + }; + + // Calculate the fee that will be taken by pallet transaction payment + let fee: u64 = TransactionPayment::compute_fee( + xt.encode().len() as u32, + &dispatch_info, + tip, + ).saturated_into(); + + // Calculate how much gas this effectively uses according to the existing mapping + let effective_gas = + ::GasWeightMapping::weight_to_gas( + dispatch_info.weight + ); + + // Here we calculate an ethereum-style effective gas price using the + // current fee of the transaction. Because the weight -> gas conversion is + // lossy, we have to handle the case where a very low weight maps to zero gas. + let effective_gas_price = if effective_gas > 0 { + fee / effective_gas + } else { + // If the effective gas was zero, we just act like it was 1. + fee + }; + + // Overwrite the original prioritization with this ethereum one + intermediate_valid.priority = effective_gas_price; + intermediate_valid + } + }) + } + } + + impl async_backing_primitives::UnincludedSegmentApi for Runtime { + fn can_build_upon( + _included_hash: ::Hash, + _slot: async_backing_primitives::Slot, + ) -> bool { + // This runtime API can be called only when asynchronous backing is enabled client-side + // We return false here to force the client to not use async backing in moonriver. + false + } + } +} + +struct CheckInherents; + +// Parity has decided to depreciate this trait, but does not offer a satisfactory replacement, +// see issue: https://github.com/paritytech/polkadot-sdk/issues/2841 +#[allow(deprecated)] +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + inherent_data.check_extrinsics(block) + } +} + +// Nimbus's Executive wrapper allows relay validators to verify the seal digest +cumulus_pallet_parachain_system::register_validate_block!( + Runtime = Runtime, + BlockExecutor = pallet_author_inherent::BlockExecutor::, + CheckInherents = CheckInherents, +); + +moonbeam_runtime_common::impl_self_contained_call!(); + +// Shorthand for a Get field of a pallet Config. +#[macro_export] +macro_rules! get { + ($pallet:ident, $name:ident, $type:ty) => { + <<$crate::Runtime as $pallet::Config>::$name as $crate::Get<$type>>::get() + }; +} + +#[cfg(test)] +mod tests { + use super::{currency::*, *}; + + #[test] + // Helps us to identify a Pallet Call in case it exceeds the 1kb limit. + // Hint: this should be a rare case. If that happens, one or more of the dispatchable arguments + // need to be Boxed. + fn call_max_size() { + const CALL_ALIGN: u32 = 1024; + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() <= CALL_ALIGN as usize + ); + assert!(std::mem::size_of::>() <= CALL_ALIGN as usize); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + assert!( + std::mem::size_of::>() + <= CALL_ALIGN as usize + ); + } + + #[test] + fn currency_constants_are_correct() { + assert_eq!(SUPPLY_FACTOR, 1); + + // txn fees + assert_eq!(TRANSACTION_BYTE_FEE, Balance::from(1 * GIGAWEI)); + assert_eq!( + get!(pallet_transaction_payment, OperationalFeeMultiplier, u8), + 5_u8 + ); + assert_eq!(STORAGE_BYTE_FEE, Balance::from(100 * MICROMOVR)); + + // treasury minimums + assert_eq!( + get!(pallet_treasury, ProposalBondMinimum, u128), + Balance::from(1 * MOVR) + ); + + // pallet_identity deposits + assert_eq!( + get!(pallet_identity, BasicDeposit, u128), + Balance::from(1 * MOVR + 25800 * MICROMOVR) + ); + assert_eq!( + get!(pallet_identity, ByteDeposit, u128), + Balance::from(100 * MICROMOVR) + ); + assert_eq!( + get!(pallet_identity, SubAccountDeposit, u128), + Balance::from(1 * MOVR + 5300 * MICROMOVR) + ); + + // staking minimums + assert_eq!( + get!(pallet_parachain_staking, MinCandidateStk, u128), + Balance::from(500 * MOVR) + ); + assert_eq!( + get!(pallet_parachain_staking, MinDelegation, u128), + Balance::from(5 * MOVR) + ); + + // crowdloan min reward + assert_eq!( + get!(pallet_crowdloan_rewards, MinimumReward, u128), + Balance::from(0u128) + ); + + // deposit for AuthorMapping + assert_eq!( + get!(pallet_author_mapping, DepositAmount, u128), + Balance::from(100 * MOVR) + ); + + // proxy deposits + assert_eq!( + get!(pallet_proxy, ProxyDepositBase, u128), + Balance::from(1 * MOVR + 800 * MICROMOVR) + ); + assert_eq!( + get!(pallet_proxy, ProxyDepositFactor, u128), + Balance::from(2100 * MICROMOVR) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositBase, u128), + Balance::from(1 * MOVR + 800 * MICROMOVR) + ); + assert_eq!( + get!(pallet_proxy, AnnouncementDepositFactor, u128), + Balance::from(5600 * MICROMOVR) + ); + } + + #[test] + fn max_offline_rounds_lower_or_eq_than_reward_payment_delay() { + assert!( + get!(pallet_parachain_staking, MaxOfflineRounds, u32) + <= get!(pallet_parachain_staking, RewardPaymentDelay, u32) + ); + } + + #[test] + // Required migration is + // pallet_parachain_staking::migrations::IncreaseMaxTopDelegationsPerCandidate + // Purpose of this test is to remind of required migration if constant is ever changed + fn updating_maximum_delegators_per_candidate_requires_configuring_required_migration() { + assert_eq!( + get!(pallet_parachain_staking, MaxTopDelegationsPerCandidate, u32), + 300 + ); + assert_eq!( + get!( + pallet_parachain_staking, + MaxBottomDelegationsPerCandidate, + u32 + ), + 50 + ); + } + + #[test] + fn configured_base_extrinsic_weight_is_evm_compatible() { + let min_ethereum_transaction_weight = WeightPerGas::get() * 21_000; + let base_extrinsic = ::BlockWeights::get() + .get(frame_support::dispatch::DispatchClass::Normal) + .base_extrinsic; + assert!(base_extrinsic.ref_time() <= min_ethereum_transaction_weight.ref_time()); + } + + #[test] + fn test_storage_growth_ratio_is_correct() { + // This is the highest amount of new storage that can be created in a block 40 KB + let block_storage_limit = 40 * 1024; + let expected_storage_growth_ratio = BlockGasLimit::get() + .low_u64() + .saturating_div(block_storage_limit); + let actual_storage_growth_ratio = + ::GasLimitStorageGrowthRatio::get(); + assert_eq!( + expected_storage_growth_ratio, actual_storage_growth_ratio, + "Storage growth ratio is not correct" + ); + } +} diff --git a/tracing/2900/runtime/moonriver/src/precompiles.rs b/tracing/2900/runtime/moonriver/src/precompiles.rs new file mode 100644 index 00000000..6af007a2 --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/precompiles.rs @@ -0,0 +1,259 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use crate::{ + asset_config::ForeignAssetInstance, xcm_config::XcmExecutorConfig, OpenTechCommitteeInstance, + TreasuryCouncilInstance, +}; +use frame_support::parameter_types; +use pallet_evm_precompile_author_mapping::AuthorMappingPrecompile; +use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata}; +use pallet_evm_precompile_batch::BatchPrecompile; +use pallet_evm_precompile_blake2::Blake2F; +use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; +use pallet_evm_precompile_call_permit::CallPermitPrecompile; +use pallet_evm_precompile_collective::CollectivePrecompile; +use pallet_evm_precompile_conviction_voting::ConvictionVotingPrecompile; +use pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompile; +use pallet_evm_precompile_gmp::GmpPrecompile; +use pallet_evm_precompile_identity::IdentityPrecompile; +use pallet_evm_precompile_modexp::Modexp; +use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile; +use pallet_evm_precompile_preimage::PreimagePrecompile; +use pallet_evm_precompile_proxy::{OnlyIsProxyAndProxy, ProxyPrecompile}; +use pallet_evm_precompile_randomness::RandomnessPrecompile; +use pallet_evm_precompile_referenda::ReferendaPrecompile; +use pallet_evm_precompile_registry::PrecompileRegistry; +use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile; +use pallet_evm_precompile_relay_verifier::RelayDataVerifierPrecompile; +use pallet_evm_precompile_sha3fips::Sha3FIPS256; +use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use pallet_evm_precompile_xcm_transactor::{ + v1::XcmTransactorPrecompileV1, v2::XcmTransactorPrecompileV2, v3::XcmTransactorPrecompileV3, +}; +use pallet_evm_precompile_xcm_utils::XcmUtilsPrecompile; +use pallet_evm_precompile_xtokens::XtokensPrecompile; +use pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSet; +use precompile_utils::precompile_set::*; + +pub struct NativeErc20Metadata; + +/// ERC20 metadata for the native token. +impl Erc20Metadata for NativeErc20Metadata { + /// Returns the name of the token. + fn name() -> &'static str { + "MOVR token" + } + + /// Returns the symbol of the token. + fn symbol() -> &'static str { + "MOVR" + } + + /// Returns the decimals places of the token. + fn decimals() -> u8 { + 18 + } + + /// Must return `true` only if it represents the main native currency of + /// the network. It must be the currency used in `pallet_evm`. + fn is_native_currency() -> bool { + true + } +} + +/// The asset precompile address prefix. Addresses that match against this prefix will be routed +/// to Erc20AssetsPrecompileSet being marked as foreign +pub const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8; 4]; + +parameter_types! { + pub ForeignAssetPrefix: &'static [u8] = FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX; +} + +type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile); + +#[precompile_utils::precompile_name_from_address] +type MoonriverPrecompilesAt = ( + // Ethereum precompiles: + // We allow DELEGATECALL to stay compliant with Ethereum behavior. + PrecompileAt, ECRecover, EthereumPrecompilesChecks>, + PrecompileAt, Sha256, EthereumPrecompilesChecks>, + PrecompileAt, Ripemd160, EthereumPrecompilesChecks>, + PrecompileAt, Identity, EthereumPrecompilesChecks>, + PrecompileAt, Modexp, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Add, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>, + PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>, + PrecompileAt, Blake2F, EthereumPrecompilesChecks>, + // Non-Moonbeam specific nor Ethereum precompiles : + PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>, + RemovedPrecompileAt>, // Dispatch + PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>, + // Moonbeam specific precompiles: + PrecompileAt< + AddressU64<2048>, + ParachainStakingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2049>, + CrowdloanRewardsPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2050>, + Erc20BalancesPrecompile, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompileAt>, // DemocracyPrecompile + PrecompileAt< + AddressU64<2052>, + XtokensPrecompile, + ( + SubcallWithMaxNesting<1>, + CallableByContract, + CallableByPrecompile, + ), + >, + PrecompileAt< + AddressU64<2053>, + RelayEncoderPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2054>, + XcmTransactorPrecompileV1, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2055>, + AuthorMappingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2056>, + BatchPrecompile, + ( + SubcallWithMaxNesting<2>, + // Batch is the only precompile allowed to call Batch. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2057>, + RandomnessPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2058>, + CallPermitPrecompile, + (SubcallWithMaxNesting<0>, CallableByContract), + >, + PrecompileAt< + AddressU64<2059>, + ProxyPrecompile, + ( + CallableByContract>, + SubcallWithMaxNesting<0>, + // Batch is the only precompile allowed to call Proxy. + CallableByPrecompile>>, + ), + >, + PrecompileAt< + AddressU64<2060>, + XcmUtilsPrecompile, + CallableByContract< + pallet_evm_precompile_xcm_utils::AllExceptXcmExecute, + >, + >, + PrecompileAt< + AddressU64<2061>, + XcmTransactorPrecompileV2, + (CallableByContract, CallableByPrecompile), + >, + RemovedPrecompileAt>, //CouncilInstance + RemovedPrecompileAt>, // TechCommitteeInstance + PrecompileAt< + AddressU64<2064>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2065>, + ReferendaPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2066>, + ConvictionVotingPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2067>, + PreimagePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2068>, + CollectivePrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2069>, + PrecompileRegistry, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt, GmpPrecompile, SubcallWithMaxNesting<0>>, + PrecompileAt< + AddressU64<2071>, + XcmTransactorPrecompileV3, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2072>, + IdentityPrecompile, + (CallableByContract, CallableByPrecompile), + >, + PrecompileAt< + AddressU64<2073>, + RelayDataVerifierPrecompile, + (CallableByContract, CallableByPrecompile), + >, +); + +/// The PrecompileSet installed in the Moonriver runtime. +/// We include the nine Istanbul precompiles +/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69) +/// The following distribution has been decided for the precompiles +/// 0-1023: Ethereum Mainnet Precompiles +/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither Moonbeam specific +/// 2048-4095 Moonbeam specific precompiles +pub type MoonriverPrecompiles = PrecompileSetBuilder< + R, + ( + // Skip precompiles if out of range. + PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), MoonriverPrecompilesAt>, + // Prefixed precompile sets (XC20) + PrecompileSetStartingWith< + ForeignAssetPrefix, + Erc20AssetsPrecompileSet, + CallableByContract, + >, + // Moonriver never had any local assets (No blacklist needed + // https://moonriver.subscan.io/event?module=localassets&event_id=created + // https://moonriver.subscan.io/event?module=localassets&event_id=forcecreated + ), +>; diff --git a/tracing/2900/runtime/moonriver/src/xcm_config.rs b/tracing/2900/runtime/moonriver/src/xcm_config.rs new file mode 100644 index 00000000..58cc41b3 --- /dev/null +++ b/tracing/2900/runtime/moonriver/src/xcm_config.rs @@ -0,0 +1,722 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM configuration for Moonbase. +//! + +use super::{ + governance, AccountId, AssetId, AssetManager, Balance, Balances, DealWithFees, Erc20XcmBridge, + MaintenanceMode, MessageQueue, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, + RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, +}; + +use frame_support::{ + parameter_types, + traits::{EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin}, +}; +use moonbeam_runtime_common::weights as moonbeam_weights; +use pallet_evm_precompileset_assets_erc20::AccountIdAssetIdConversion; +use sp_runtime::{ + traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf}, + DispatchErrorWithPostInfo, +}; +use sp_weights::Weight; + +use frame_system::{EnsureRoot, RawOrigin}; +use sp_core::{ConstU32, H160, H256}; + +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, HashedDescription, + NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, +}; + +use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; +use xcm::latest::prelude::{ + Asset, GlobalConsensus, InteriorLocation, Junction, Location, NetworkId, PalletInstance, + Parachain, +}; +use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry}; + +use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use orml_xcm_support::MultiNativeAsset; +use xcm_primitives::{ + AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType, + FirstAssetTrader, SignedToAccountId20, UtilityAvailableCalls, UtilityEncodeCall, XcmTransact, +}; + +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use sp_core::Get; +use sp_std::{ + convert::{From, Into, TryFrom}, + prelude::*, +}; + +use orml_traits::parameter_type_with_key; + +use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot}; + +parameter_types! { + // The network Id of the relay + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + // The relay chain Origin type + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + // The universal location within the global consensus system + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); + + // Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a Location: (Self Balances pallet index) + // We use the RELATIVE multilocation + pub SelfReserve: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a Location of type AccountKey20, just generate a native account + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + HashedDescription>, +); + +/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`. +pub struct LocationToH160; +impl ConvertLocation for LocationToH160 { + fn convert_location(location: &Location) -> Option { + >::convert_location(location) + .map(Into::into) + } +} + +// The non-reserve fungible transactor type +// It will use pallet-assets, and the Id will be matched against AsAssetType +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + super::Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId20 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +/// The transactor for our own chain currency. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + xcm_builder::IsConcrete, + // We can convert the MultiLocations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// We use all transactors +// These correspond to +// SelfReserve asset, both pre and post 0.9.16 +// Foreign assets +// Local assets, both pre and post 0.9.16 +// We can remove the Old reanchor once +// we import https://github.com/open-web3-stack/open-runtime-module-library/pull/708 +pub type AssetTransactors = ( + LocalAssetTransactor, + ForeignFungiblesTransactor, + Erc20XcmBridge, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + // Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte- + // account local origin + SignedAccountKey20AsNative, +); + +parameter_types! { + /// The amount of weight an XCM operation takes. This is safe overestimate. + pub UnitWeightCost: Weight = Weight::from_parts(200_000_000u64, 0); + /// Maximum number of instructions in a single XCM fragment. A sanity check against + /// weight caculations getting too crazy. + pub MaxInstructions: u32 = 100; +} + +/// Xcm Weigher shared between multiple Xcm-related configs. +pub type XcmWeigher = WeightInfoBounds< + moonbeam_xcm_benchmarks::weights::XcmWeight, + RuntimeCall, + MaxInstructions, +>; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +/// We do not burn anything because we want to mimic exactly what +/// the sovereign account has +pub type XcmFeesToAccount = xcm_primitives::XcmFeesToAccount< + super::Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +pub struct SafeCallFilter; +impl frame_support::traits::Contains for SafeCallFilter { + fn contains(_call: &RuntimeCall) -> bool { + // TODO review + // This needs to be addressed at EVM level + true + } +} + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS; +} + +// Our implementation of the Moonbeam Call +// Attachs the right origin in case the call is made to pallet-ethereum-xcm +#[cfg(not(feature = "evm-tracing"))] +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +#[cfg(feature = "evm-tracing")] +moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!(); + +moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); + +pub struct XcmExecutorConfig; +impl xcm_executor::Config for XcmExecutorConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + // Filter to the reserve withdraw operations + // Whenever the reserve matches the relative or absolute value + // of our chain, we always return the relative reserve + type IsReserve = MultiNativeAsset>; + type IsTeleporter = (); // No teleport + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = XcmWeigher; + // We use two traders + // When we receive the relative representation of the self-reserve asset, + // we use UsingComponents and the local way of handling fees + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + UsingComponents< + ::WeightToFee, + SelfReserve, + AccountId, + Balances, + DealWithFees, + >, + FirstAssetTrader, + ); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type PalletInstancesInfo = crate::AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = SafeCallFilter; + type Aliasers = Nothing; + type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor; +} + +type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper< + XcmExecutorConfig, + xcm_executor::XcmExecutor, +>; + +// Converts a Signed Local Origin into a Location +pub type LocalOriginToLocation = SignedToAccountId20; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // TODO pallet-xcm weights + type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo; + type AdminOrigin = EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = sp_core::ConstU32<1_000>; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery< + cumulus_primitives_core::ParaId, + >; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +// TODO: This pallet can be removed after the lazy migration is done and +// event `Completed` is emitted. +// https://github.com/paritytech/polkadot-sdk/pull/1246 +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DmpSink = frame_support::traits::EnqueueWithOrigin; + type WeightInfo = cumulus_pallet_dmp_queue::weights::SubstrateWeight; +} + +parameter_types! { + /// The amount of weight (if any) which should be provided to the message queue for + /// servicing enqueued items. + /// + /// This may be legitimately `None` in the case that you will call + /// `ServiceQueues::service_queues` manually. + pub MessageQueueServiceWeight: Weight = + Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block; + /// The maximum number of stale pages (i.e. of overweight messages) allowed before culling + /// can happen. Once there are more stale pages than this, then historical pages may be + /// dropped, even if they contain unprocessed overweight messages. + pub const MessageQueueMaxStale: u32 = 8; + /// The size of the page; this implies the maximum message size which can be sent. + /// + /// A good value depends on the expected message sizes, their weights, the weight that is + /// available for processing them and the maximal needed message size. The maximal message + /// size is slightly lower than this as defined by [`MaxMessageLenOf`]. + pub const MessageQueueHeapSize: u32 = 128 * 1048; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = + xcm_builder::ProcessXcmMessage; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + // NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins + type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling); + type WeightInfo = pallet_message_queue::weights::SubstrateWeight; +} + +// Our AssetType. For now we only handle Xcm Assets +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum AssetType { + Xcm(xcm::v3::Location), +} +impl Default for AssetType { + fn default() -> Self { + Self::Xcm(xcm::v3::Location::here()) + } +} + +impl From for AssetType { + fn from(location: xcm::v3::Location) -> Self { + Self::Xcm(location) + } +} + +// This can be removed once we fully adopt xcm::v4 everywhere +impl TryFrom for AssetType { + type Error = (); + fn try_from(location: Location) -> Result { + Ok(Self::Xcm(location.try_into()?)) + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => Some(location), + } + } +} + +impl Into> for AssetType { + fn into(self) -> Option { + match self { + Self::Xcm(location) => xcm_builder::V4V3LocationConverter::convert_back(&location), + } + } +} + +// Implementation on how to retrieve the AssetId from an AssetType +// We simply hash the AssetType and take the lowest 128 bits +impl From for AssetId { + fn from(asset: AssetType) -> AssetId { + match asset { + AssetType::Xcm(id) => { + let mut result: [u8; 16] = [0u8; 16]; + let hash: H256 = id.using_encoded(::Hashing::hash); + result.copy_from_slice(&hash.as_fixed_bytes()[0..16]); + u128::from_le_bytes(result) + } + } + } +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + // Our native token + SelfReserve, + // Assets representing other chains native tokens + ForeignAsset(AssetId), + // Erc20 token + Erc20 { contract_address: H160 }, +} + +impl AccountIdToCurrencyId for Runtime { + fn account_to_currency_id(account: AccountId) -> Option { + Some(match account { + // the self-reserve currency is identified by the pallet-balances address + a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve, + // the rest of the currencies, by their corresponding erc20 address + _ => match Runtime::account_to_asset_id(account) { + // A foreign asset + Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id), + // If no known prefix is identified, we consider that it's a "real" erc20 token + // (i.e. managed by a real smart contract) + None => CurrencyId::Erc20 { + contract_address: account.into(), + }, + }, + }) + } +} + +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it + CurrencyId::SelfReserve => { + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + CurrencyId::Erc20 { contract_address } => { + let mut location = Erc20XcmBridgePalletLocation::get(); + location + .push_interior(Junction::AccountKey20 { + key: contract_address.0, + network: None, + }) + .ok(); + Some(location) + } + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0); + pub const MaxAssetsForTransfer: usize = 2; + + // This is how we are going to detect whether the asset is a Reserve asset + // This however is the chain part only + pub SelfLocation: Location = Location::here(); + // We need this to be able to catch when someone is trying to execute a non- + // cross-chain transfer in xtokens through the absolute path way + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(ParachainInfo::parachain_id().into()) + ].into() + }; +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + // Kusama AssetHub fee + (1, Some(Parachain(1000u32))) => Some(50_000_000u128), + _ => None, + } + }; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdConvert = CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = AbsoluteAndRelativeReserve; +} + +// 1 KSM should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +// For now we only allow to transact in the relay, although this might change in the future +// Transactors just defines the chains in which we allow transactions to be issued through +// xcm +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum Transactors { + Relay, +} + +// Default for benchmarking +#[cfg(feature = "runtime-benchmarks")] +impl Default for Transactors { + fn default() -> Self { + Transactors::Relay + } +} + +impl TryFrom for Transactors { + type Error = (); + fn try_from(value: u8) -> Result { + match value { + 0u8 => Ok(Transactors::Relay), + _ => Err(()), + } + } +} + +impl UtilityEncodeCall for Transactors { + fn encode_call(self, call: UtilityAvailableCalls) -> Vec { + match self { + Transactors::Relay => pallet_xcm_transactor::Pallet::::encode_call( + pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::), + call, + ), + } + } +} + +impl XcmTransact for Transactors { + fn destination(self) -> Location { + match self { + Transactors::Relay => Location::parent(), + } + } +} + +pub type DerivativeAddressRegistrationOrigin = + EitherOfDiverse, governance::custom_origins::GeneralAdmin>; + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = Transactors; + type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin; + type SovereignAccountDispatcherOrigin = EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = AccountIdToLocation; + type CurrencyIdToLocation = CurrencyIdToLocation>; + type XcmSender = XcmRouter; + type SelfLocation = SelfLocation; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = AbsoluteAndRelativeReserve; + type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo; + type HrmpManipulatorOrigin = GeneralAdminOrRoot; + type HrmpOpenOrigin = FastGeneralAdminOrRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + // This is the relative view of erc20 assets. + // Identified by this prefix + AccountKey20(contractAddress) + // We use the RELATIVE multilocation + pub Erc20XcmBridgePalletLocation: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + + // To be able to support almost all erc20 implementations, + // we provide a sufficiently hight gas limit. + pub Erc20XcmBridgeTransferGasLimit: u64 = 200_000; +} + +impl pallet_erc20_xcm_bridge::Config for Runtime { + type AccountIdConverter = LocationToH160; + type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation; + type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit; + type EvmRunner = EvmRunnerPrecompileOrEthXcm; +} + +#[cfg(feature = "runtime-benchmarks")] +mod testing { + use super::*; + use xcm_builder::V4V3LocationConverter; + + /// This From exists for benchmarking purposes. It has the potential side-effect of calling + /// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code. + impl From for CurrencyId { + fn from(location: Location) -> CurrencyId { + use xcm_primitives::AssetTypeGetter; + + // If it does not exist, for benchmarking purposes, we create the association + let asset_id = if let Some(asset_id) = + AsAssetType::::convert_location(&location) + { + asset_id + } else { + let asset_type = AssetType::Xcm( + V4V3LocationConverter::convert(&location).expect("convert to v3"), + ); + let asset_id: AssetId = asset_type.clone().into(); + AssetManager::set_asset_type_asset_id(asset_type, asset_id); + asset_id + }; + + CurrencyId::ForeignAsset(asset_id) + } + } +} diff --git a/tracing/2900/runtime/moonriver/tests/common/mod.rs b/tracing/2900/runtime/moonriver/tests/common/mod.rs new file mode 100644 index 00000000..2fbe1236 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/common/mod.rs @@ -0,0 +1,380 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![allow(dead_code)] + +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use fp_evm::GenesisAccount; +use frame_support::{ + assert_ok, + traits::{OnFinalize, OnInitialize}, +}; +pub use moonriver_runtime::{ + asset_config::AssetRegistrarMetadata, + currency::{GIGAWEI, MOVR, SUPPLY_FACTOR, WEI}, + xcm_config::AssetType, + AccountId, AssetId, AssetManager, AuthorInherent, Balance, Balances, CrowdloanRewards, + Ethereum, Executive, Header, InflationInfo, ParachainStaking, Range, Runtime, RuntimeCall, + RuntimeEvent, System, TransactionConverter, TransactionPaymentAsGasPrice, UncheckedExtrinsic, + HOURS, WEEKS, +}; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use sp_core::{Encode, H160}; +use sp_runtime::{traits::Dispatchable, BuildStorage, Digest, DigestItem, Perbill, Percent}; + +use std::collections::BTreeMap; + +use fp_rpc::ConvertTransaction; +use pallet_transaction_payment::Multiplier; + +// A valid signed Alice transfer. +pub const VALID_ETH_TX: &str = + "02f86d8205018085174876e80085e8d4a5100082520894f24ff3a9cf04c71dbc94d0b566f7a27b9456\ + 6cac8080c001a0e1094e1a52520a75c0255db96132076dd0f1263089f838bea548cbdbfc64a4d19f031c\ + 92a8cb04e2d68d20a6158d542a07ac440cc8d07b6e36af02db046d92df"; + +// An invalid signed Alice transfer with a gas limit artifically set to 0. +pub const INVALID_ETH_TX: &str = + "f86180843b9aca00809412cb274aad8251c875c0bf6872b67d9983e53fdd01801ca00e28ba2dd3c5a\ + 3fd467d4afd7aefb4a34b373314fff470bb9db743a84d674a0aa06e5994f2d07eafe1c37b4ce5471ca\ + ecec29011f6f5bf0b1a552c55ea348df35f"; + +pub fn rpc_run_to_block(n: u32) { + while System::block_number() < n { + Ethereum::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Ethereum::on_initialize(System::block_number()); + } +} + +/// Utility function that advances the chain to the desired block number. +/// If an author is provided, that author information is injected to all the blocks in the meantime. +pub fn run_to_block(n: u32, author: Option) { + // Finalize the first block + Ethereum::on_finalize(System::block_number()); + while System::block_number() < n { + // Set the new block number and author + match author { + Some(ref author) => { + let pre_digest = Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + }; + System::reset_events(); + System::initialize( + &(System::block_number() + 1), + &System::parent_hash(), + &pre_digest, + ); + } + None => { + System::set_block_number(System::block_number() + 1); + } + } + + // Initialize the new block + AuthorInherent::on_initialize(System::block_number()); + ParachainStaking::on_initialize(System::block_number()); + Ethereum::on_initialize(System::block_number()); + + // Finalize the block + Ethereum::on_finalize(System::block_number()); + ParachainStaking::on_finalize(System::block_number()); + } +} + +pub fn last_event() -> RuntimeEvent { + System::events().pop().expect("Event expected").event +} + +// Helper function to give a simple evm context suitable for tests. +// We can remove this once https://github.com/rust-blockchain/evm/pull/35 +// is in our dependency graph. +pub fn evm_test_context() -> fp_evm::Context { + fp_evm::Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + } +} + +// Test struct with the purpose of initializing xcm assets +#[derive(Clone)] +pub struct XcmAssetInitialization { + pub asset_type: AssetType, + pub metadata: AssetRegistrarMetadata, + pub balances: Vec<(AccountId, Balance)>, + pub is_sufficient: bool, +} + +pub struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, + // [collator, amount] + collators: Vec<(AccountId, Balance)>, + // [delegator, collator, nomination_amount] + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + // per-round inflation config + inflation: InflationInfo, + // AuthorId -> AccoutId mappings + mappings: Vec<(NimbusId, AccountId)>, + // Crowdloan fund + crowdloan_fund: Balance, + // Chain id + chain_id: u64, + // EVM genesis accounts + evm_accounts: BTreeMap, + // [assettype, metadata, Vec, is_sufficient] + xcm_assets: Vec, + safe_xcm_version: Option, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![], + delegations: vec![], + collators: vec![], + inflation: InflationInfo { + expect: Range { + min: 100_000 * MOVR, + ideal: 200_000 * MOVR, + max: 500_000 * MOVR, + }, + // not used + annual: Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50), + }, + // unrealistically high parameterization, only for testing + round: Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }, + }, + mappings: vec![], + crowdloan_fund: 0, + chain_id: CHAIN_ID, + evm_accounts: BTreeMap::new(), + xcm_assets: vec![], + safe_xcm_version: None, + } + } +} + +impl ExtBuilder { + pub fn with_evm_accounts(mut self, accounts: BTreeMap) -> Self { + self.evm_accounts = accounts; + self + } + + pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self { + self.collators = collators; + self + } + + pub fn with_delegations(mut self, delegations: Vec<(AccountId, AccountId, Balance)>) -> Self { + self.delegations = delegations + .into_iter() + .map(|d| (d.0, d.1, d.2, Percent::zero())) + .collect(); + self + } + + pub fn with_crowdloan_fund(mut self, crowdloan_fund: Balance) -> Self { + self.crowdloan_fund = crowdloan_fund; + self + } + + pub fn with_mappings(mut self, mappings: Vec<(NimbusId, AccountId)>) -> Self { + self.mappings = mappings; + self + } + + #[allow(dead_code)] + pub fn with_inflation(mut self, inflation: InflationInfo) -> Self { + self.inflation = inflation; + self + } + + pub fn with_xcm_assets(mut self, xcm_assets: Vec) -> Self { + self.xcm_assets = xcm_assets; + self + } + + pub fn with_safe_xcm_version(mut self, safe_xcm_version: u32) -> Self { + self.safe_xcm_version = Some(safe_xcm_version); + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_parachain_staking::GenesisConfig:: { + candidates: self.collators, + delegations: self.delegations, + inflation_config: self.inflation, + collator_commission: Perbill::from_percent(20), + parachain_bond_reserve_percent: Percent::from_percent(30), + blocks_per_round: 2 * HOURS, + num_selected_candidates: 8, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_crowdloan_rewards::GenesisConfig:: { + funded_amount: self.crowdloan_fund, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_author_mapping::GenesisConfig:: { + mappings: self.mappings, + } + .assimilate_storage(&mut t) + .unwrap(); + + let genesis_config = pallet_evm_chain_id::GenesisConfig:: { + chain_id: self.chain_id, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: self.evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_ethereum::GenesisConfig:: { + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_xcm::GenesisConfig:: { + safe_xcm_version: self.safe_xcm_version, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = pallet_transaction_payment::GenesisConfig:: { + multiplier: Multiplier::from(10u128), + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + let xcm_assets = self.xcm_assets.clone(); + ext.execute_with(|| { + // If any xcm assets specified, we register them here + for xcm_asset_initialization in xcm_assets { + let asset_id: AssetId = xcm_asset_initialization.asset_type.clone().into(); + AssetManager::register_foreign_asset( + root_origin(), + xcm_asset_initialization.asset_type, + xcm_asset_initialization.metadata, + 1, + xcm_asset_initialization.is_sufficient, + ) + .unwrap(); + for (account, balance) in xcm_asset_initialization.balances { + moonriver_runtime::Assets::mint( + origin_of(AssetManager::account_id()), + asset_id.into(), + account, + balance, + ) + .unwrap(); + } + } + System::set_block_number(1); + }); + ext + } +} + +pub const CHAIN_ID: u64 = 1281; +pub const ALICE: [u8; 20] = [4u8; 20]; +pub const ALICE_NIMBUS: [u8; 32] = [4u8; 32]; +pub const BOB: [u8; 20] = [5u8; 20]; +pub const CHARLIE: [u8; 20] = [6u8; 20]; +pub const DAVE: [u8; 20] = [7u8; 20]; +pub const EVM_CONTRACT: [u8; 20] = [8u8; 20]; + +pub fn origin_of(account_id: AccountId) -> ::RuntimeOrigin { + ::RuntimeOrigin::signed(account_id) +} + +pub fn inherent_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::none() +} + +pub fn root_origin() -> ::RuntimeOrigin { + ::RuntimeOrigin::root() +} + +/// Mock the inherent that sets validation data in ParachainSystem, which +/// contains the `relay_chain_block_number`, which is used in `author-filter` as a +/// source of randomness to filter valid authors at each block. +pub fn set_parachain_inherent_data() { + use cumulus_primitives_core::PersistedValidationData; + use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; + let (relay_parent_storage_root, relay_chain_state) = + RelayStateSproofBuilder::default().into_state_root_and_proof(); + let vfp = PersistedValidationData { + relay_parent_number: 1u32, + relay_parent_storage_root, + ..Default::default() + }; + let parachain_inherent_data = ParachainInherentData { + validation_data: vfp, + relay_chain_state: relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + assert_ok!(RuntimeCall::ParachainSystem( + cumulus_pallet_parachain_system::Call::::set_validation_data { + data: parachain_inherent_data + } + ) + .dispatch(inherent_origin())); +} + +pub fn unchecked_eth_tx(raw_hex_tx: &str) -> UncheckedExtrinsic { + let converter = TransactionConverter; + converter.convert_transaction(ethereum_transaction(raw_hex_tx)) +} + +pub fn ethereum_transaction(raw_hex_tx: &str) -> pallet_ethereum::Transaction { + let bytes = hex::decode(raw_hex_tx).expect("Transaction bytes."); + let transaction = ethereum::EnvelopedDecodable::decode(&bytes[..]); + assert!(transaction.is_ok()); + transaction.unwrap() +} diff --git a/tracing/2900/runtime/moonriver/tests/evm_tracing.rs b/tracing/2900/runtime/moonriver/tests/evm_tracing.rs new file mode 100644 index 00000000..86aaab37 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/evm_tracing.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonriver EVM tracing Integration Tests + +mod common; + +#[cfg(test)] +#[cfg(feature = "evm-tracing")] +mod tests { + use super::common::*; + + use pallet_evm::AddressMapping; + use sp_core::H160; + + use moonbeam_rpc_primitives_debug::runtime_decl_for_debug_runtime_api::DebugRuntimeApi; + use std::str::FromStr; + + #[test] + fn debug_runtime_api_trace_transaction() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * MOVR), + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * MOVR, + } + .into(), + ); + let transaction = ethereum_transaction(VALID_ETH_TX); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_transaction( + vec![non_eth_uxt.clone(), eth_uxt, non_eth_uxt.clone()], + &transaction, + &block + ) + .is_ok()); + }); + } + + #[test] + fn debug_runtime_api_trace_block() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_balances(vec![ + (alith, 2_000 * MOVR), + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .build() + .execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * MOVR, + } + .into(), + ); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let eth_tx = ethereum_transaction(VALID_ETH_TX); + let eth_extrinsic_hash = eth_tx.hash(); + let block = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + }; + assert!(Runtime::trace_block( + vec![non_eth_uxt.clone(), eth_uxt.clone(), non_eth_uxt, eth_uxt], + vec![eth_extrinsic_hash, eth_extrinsic_hash], + &block + ) + .is_ok()); + }); + } +} diff --git a/tracing/2900/runtime/moonriver/tests/integration_test.rs b/tracing/2900/runtime/moonriver/tests/integration_test.rs new file mode 100644 index 00000000..f3b32732 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/integration_test.rs @@ -0,0 +1,2664 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonriver Runtime Integration Tests + +#![cfg(test)] + +mod common; +use common::*; + +use fp_evm::{Context, IsPrecompileResult}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchClass, + traits::{ + fungible::Inspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo, + StorageInfoTrait, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + StorageHasher, Twox128, +}; +use moonbeam_xcm_benchmarks::weights::XcmWeight; +use moonriver_runtime::{ + asset_config::ForeignAssetInstance, + xcm_config::{CurrencyId, SelfReserve}, + AssetId, OpenTechCommitteeCollective, PolkadotXcm, Precompiles, RuntimeBlockWeights, + TransactionPayment, TreasuryCouncilCollective, XTokens, XcmTransactor, + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, +}; +use nimbus_primitives::NimbusId; +use pallet_evm::PrecompileSet; +use pallet_evm_precompileset_assets_erc20::{ + AccountIdAssetIdConversion, SELECTOR_LOG_APPROVAL, SELECTOR_LOG_TRANSFER, +}; +use pallet_transaction_payment::Multiplier; +use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights}; +use parity_scale_codec::Encode; +use polkadot_parachain::primitives::Sibling; +use precompile_utils::{ + precompile_set::{is_precompile_or_fail, IsActivePrecompile}, + prelude::*, + testing::*, +}; +use sha3::{Digest, Keccak256}; +use sp_core::{ByteArray, Pair, H160, U256}; +use sp_runtime::{ + traits::{Convert, Dispatchable}, + BuildStorage, DispatchError, ModuleError, +}; +use std::str::from_utf8; +use xcm::latest::prelude::*; +use xcm::{VersionedAssets, VersionedLocation}; +use xcm_builder::{ParentIsPreset, SiblingParachainConvertsVia}; +use xcm_executor::traits::ConvertLocation; + +type BatchPCall = pallet_evm_precompile_batch::BatchPrecompileCall; +type CrowdloanRewardsPCall = + pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompileCall; +type XcmUtilsPCall = pallet_evm_precompile_xcm_utils::XcmUtilsPrecompileCall< + Runtime, + moonriver_runtime::xcm_config::XcmExecutorConfig, +>; +type XtokensPCall = pallet_evm_precompile_xtokens::XtokensPrecompileCall; +type ForeignAssetsPCall = pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSetCall< + Runtime, + ForeignAssetInstance, +>; +type XcmTransactorV2PCall = + pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call; + +const BASE_FEE_GENESIS: u128 = 100 * GIGAWEI; + +#[test] +fn xcmp_queue_controller_origin_is_root() { + // important for the XcmExecutionManager impl of PauseExecution which uses root origin + // to suspend/resume XCM execution in xcmp_queue::on_idle + assert_ok!( + ::ControllerOrigin::ensure_origin(root_origin()) + ); +} + +#[test] +fn verify_pallet_prefixes() { + fn is_pallet_prefix(name: &str) { + // Compares the unhashed pallet prefix in the `StorageInstance` implementation by every + // storage item in the pallet P. This pallet prefix is used in conjunction with the + // item name to get the unique storage key: hash(PalletPrefix) + hash(StorageName) + // https://github.com/paritytech/substrate/blob/master/frame/support/procedural/src/pallet/ + // expand/storage.rs#L389-L401 + assert_eq!( + ::PalletInfo::name::

(), + Some(name) + ); + } + // TODO: use StorageInfoTrait from https://github.com/paritytech/substrate/pull/9246 + // This is now available with polkadot-v0.9.9 dependencies + is_pallet_prefix::("System"); + is_pallet_prefix::("Utility"); + is_pallet_prefix::("ParachainSystem"); + is_pallet_prefix::("TransactionPayment"); + is_pallet_prefix::("ParachainInfo"); + is_pallet_prefix::("EthereumChainId"); + is_pallet_prefix::("EVM"); + is_pallet_prefix::("Ethereum"); + is_pallet_prefix::("ParachainStaking"); + is_pallet_prefix::("MaintenanceMode"); + is_pallet_prefix::("Scheduler"); + is_pallet_prefix::( + "OpenTechCommitteeCollective", + ); + is_pallet_prefix::("Treasury"); + is_pallet_prefix::("AuthorInherent"); + is_pallet_prefix::("AuthorFilter"); + is_pallet_prefix::("CrowdloanRewards"); + is_pallet_prefix::("AuthorMapping"); + is_pallet_prefix::("Identity"); + is_pallet_prefix::("XcmpQueue"); + is_pallet_prefix::("CumulusXcm"); + is_pallet_prefix::("DmpQueue"); + is_pallet_prefix::("PolkadotXcm"); + is_pallet_prefix::("Assets"); + is_pallet_prefix::("XTokens"); + is_pallet_prefix::("AssetManager"); + is_pallet_prefix::("Migrations"); + is_pallet_prefix::("XcmTransactor"); + is_pallet_prefix::("ProxyGenesisCompanion"); + is_pallet_prefix::("MoonbeamOrbiters"); + is_pallet_prefix::("TreasuryCouncilCollective"); + is_pallet_prefix::("MoonbeamLazyMigrations"); + is_pallet_prefix::("RelayStorageRoots"); + + let prefix = |pallet_name, storage_name| { + let mut res = [0u8; 32]; + res[0..16].copy_from_slice(&Twox128::hash(pallet_name)); + res[16..32].copy_from_slice(&Twox128::hash(storage_name)); + res.to_vec() + }; + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"Now".to_vec(), + prefix: prefix(b"Timestamp", b"Now"), + max_values: Some(1), + max_size: Some(8), + }, + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"DidUpdate".to_vec(), + prefix: prefix(b"Timestamp", b"DidUpdate"), + max_values: Some(1), + max_size: Some(1), + } + ] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"TotalIssuance".to_vec(), + prefix: prefix(b"Balances", b"TotalIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"InactiveIssuance".to_vec(), + prefix: prefix(b"Balances", b"InactiveIssuance"), + max_values: Some(1), + max_size: Some(16), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Account".to_vec(), + prefix: prefix(b"Balances", b"Account"), + max_values: None, + max_size: Some(100), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Locks".to_vec(), + prefix: prefix(b"Balances", b"Locks"), + max_values: None, + max_size: Some(1287), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Reserves".to_vec(), + prefix: prefix(b"Balances", b"Reserves"), + max_values: None, + max_size: Some(1037), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Holds".to_vec(), + prefix: prefix(b"Balances", b"Holds"), + max_values: None, + max_size: Some(55), + }, + StorageInfo { + pallet_name: b"Balances".to_vec(), + storage_name: b"Freezes".to_vec(), + prefix: prefix(b"Balances", b"Freezes"), + max_values: None, + max_size: Some(37), + }, + ] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Proxies".to_vec(), + prefix: prefix(b"Proxy", b"Proxies"), + max_values: None, + max_size: Some(845), + }, + StorageInfo { + pallet_name: b"Proxy".to_vec(), + storage_name: b"Announcements".to_vec(), + prefix: prefix(b"Proxy", b"Announcements"), + max_values: None, + max_size: Some(1837), + } + ] + ); + assert_eq!( + ::storage_info(), + vec![StorageInfo { + pallet_name: b"MaintenanceMode".to_vec(), + storage_name: b"MaintenanceMode".to_vec(), + prefix: prefix(b"MaintenanceMode", b"MaintenanceMode"), + max_values: Some(1), + max_size: None, + },] + ); + assert_eq!( + ::storage_info(), + vec![ + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRoot".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRoot"), + max_values: None, + max_size: Some(44), + }, + StorageInfo { + pallet_name: b"RelayStorageRoots".to_vec(), + storage_name: b"RelayStorageRootKeys".to_vec(), + prefix: prefix(b"RelayStorageRoots", b"RelayStorageRootKeys"), + max_values: Some(1), + max_size: Some(121), + }, + ] + ); +} + +#[test] +fn test_collectives_storage_item_prefixes() { + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"TreasuryCouncilCollective".to_vec()); + } + + for StorageInfo { pallet_name, .. } in + ::storage_info() + { + assert_eq!(pallet_name, b"OpenTechCommitteeCollective".to_vec()); + } +} + +#[test] +fn collective_set_members_root_origin_works() { + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert_ok!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + // OpenTechCommitteeCollective + assert_ok!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::root(), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + )); + }); +} + +#[test] +fn collective_set_members_general_admin_origin_works() { + use moonriver_runtime::{ + governance::custom_origins::Origin as CustomOrigin, OriginCaller, Utility, + }; + + ExtBuilder::default().build().execute_with(|| { + let root_caller = ::RuntimeOrigin::root(); + let alice = AccountId::from(ALICE); + + // TreasuryCouncilCollective + let _ = Utility::dispatch_as( + root_caller.clone(), + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + // OpenTechCommitteeCollective + let _ = Utility::dispatch_as( + root_caller, + Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)), + Box::new( + pallet_collective::Call::::set_members { + new_members: vec![alice, AccountId::from(BOB)], + prime: Some(alice), + old_count: 2, + } + .into(), + ), + ); + + assert_eq!( + System::events() + .into_iter() + .filter_map(|r| { + match r.event { + RuntimeEvent::Utility(pallet_utility::Event::DispatchedAs { result }) + if result.is_ok() => + { + Some(true) + } + _ => None, + } + }) + .collect::>() + .len(), + 2 + ) + }); +} + +#[test] +fn collective_set_members_signed_origin_does_not_work() { + let alice = AccountId::from(ALICE); + ExtBuilder::default().build().execute_with(|| { + // TreasuryCouncilCollective + assert!(TreasuryCouncilCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + // OpenTechCommitteeCollective + assert!(OpenTechCommitteeCollective::set_members( + ::RuntimeOrigin::signed(alice), + vec![AccountId::from(ALICE), AccountId::from(BOB)], + Some(AccountId::from(ALICE)), + 2 + ) + .is_err()); + }); +} + +#[test] +fn verify_pallet_indices() { + fn is_pallet_index(index: usize) { + assert_eq!( + ::PalletInfo::index::

(), + Some(index) + ); + } + // System support + is_pallet_index::(0); + is_pallet_index::(1); + is_pallet_index::(3); + is_pallet_index::(4); + // Monetary + is_pallet_index::(10); + is_pallet_index::(11); + // Consensus support + is_pallet_index::(20); + is_pallet_index::(21); + is_pallet_index::(22); + is_pallet_index::(23); + is_pallet_index::(24); + // Handy utilities + is_pallet_index::(30); + is_pallet_index::(31); + is_pallet_index::(32); + is_pallet_index::(33); + is_pallet_index::(34); + is_pallet_index::(35); + is_pallet_index::(37); + // Ethereum compatibility + is_pallet_index::(50); + is_pallet_index::(51); + is_pallet_index::(52); + // Governance + is_pallet_index::(60); + // is_pallet_index::(61); Removed + // Council + // is_pallet_index::(70); Removed + // is_pallet_index::(71); Removed + is_pallet_index::(72); + is_pallet_index::(73); + // Treasury + is_pallet_index::(80); + // Crowdloan + is_pallet_index::(90); + // XCM Stuff + is_pallet_index::(100); + is_pallet_index::(101); + is_pallet_index::(102); + is_pallet_index::(103); + is_pallet_index::(104); + is_pallet_index::(105); + is_pallet_index::(106); + is_pallet_index::(107); +} + +#[test] +fn verify_reserved_indices() { + use frame_metadata::*; + let metadata = moonriver_runtime::Runtime::metadata(); + let metadata = match metadata.1 { + RuntimeMetadata::V14(metadata) => metadata, + _ => panic!("metadata has been bumped, test needs to be updated"), + }; + // 40: Sudo + // 53: BaseFee + // 108: pallet_assets:: + let reserved = vec![40, 53, 108]; + let existing = metadata + .pallets + .iter() + .map(|p| p.index) + .collect::>(); + assert!(reserved.iter().all(|index| !existing.contains(index))); +} + +#[test] +fn verify_proxy_type_indices() { + assert_eq!(moonriver_runtime::ProxyType::Any as u8, 0); + assert_eq!(moonriver_runtime::ProxyType::NonTransfer as u8, 1); + assert_eq!(moonriver_runtime::ProxyType::Governance as u8, 2); + assert_eq!(moonriver_runtime::ProxyType::Staking as u8, 3); + assert_eq!(moonriver_runtime::ProxyType::CancelProxy as u8, 4); + assert_eq!(moonriver_runtime::ProxyType::Balances as u8, 5); + assert_eq!(moonriver_runtime::ProxyType::AuthorMapping as u8, 6); + assert_eq!(moonriver_runtime::ProxyType::IdentityJudgement as u8, 7); +} + +#[test] +fn join_collator_candidates() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 20_000 * MOVR), + (AccountId::from(BOB), 20_000 * MOVR), + (AccountId::from(CHARLIE), 10_100 * MOVR), + (AccountId::from(DAVE), 10_000 * MOVR), + ]) + .with_collators(vec![ + (AccountId::from(ALICE), 10_000 * MOVR), + (AccountId::from(BOB), 10_000 * MOVR), + ]) + .with_delegations(vec![ + (AccountId::from(CHARLIE), AccountId::from(ALICE), 50 * MOVR), + (AccountId::from(CHARLIE), AccountId::from(BOB), 50 * MOVR), + ]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(ALICE)), + 10_000 * MOVR, + 2u32 + ), + pallet_parachain_staking::Error::::CandidateExists + ); + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 10_000 * MOVR, + 2u32 + ), + pallet_parachain_staking::Error::::DelegatorExists + ); + assert!(System::events().is_empty()); + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(DAVE)), + 10_000 * MOVR, + 2u32 + )); + assert_eq!( + last_event(), + RuntimeEvent::ParachainStaking( + pallet_parachain_staking::Event::JoinedCollatorCandidates { + account: AccountId::from(DAVE), + amount_locked: 10_000 * MOVR, + new_total_amt_locked: 30_100 * MOVR + } + ) + ); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(ALICE)); + assert_eq!(candidates.0[0].amount, 10_050 * MOVR); + assert_eq!(candidates.0[1].owner, AccountId::from(BOB)); + assert_eq!(candidates.0[1].amount, 10_050 * MOVR); + assert_eq!(candidates.0[2].owner, AccountId::from(DAVE)); + assert_eq!(candidates.0[2].amount, 10_000 * MOVR); + }); +} + +#[test] +fn transfer_through_evm_to_stake() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 20_000 * MOVR)]) + .build() + .execute_with(|| { + // Charlie has no balance => fails to stake + assert_noop!( + ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 10_000 * MOVR, + 2u32 + ), + DispatchError::Module(ModuleError { + index: 20, + error: [8, 0, 0, 0], + message: Some("InsufficientBalance") + }) + ); + // Alice transfer from free balance 20000 MOVR to Bob + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 20_000 * MOVR, + )); + assert_eq!(Balances::free_balance(AccountId::from(BOB)), 20_000 * MOVR); + + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + // Bob transfers 10000 MOVR to Charlie via EVM + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(CHARLIE), + input: vec![], + value: (10_000 * MOVR).into(), + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + assert_eq!( + Balances::free_balance(AccountId::from(CHARLIE)), + 10_000 * MOVR, + ); + + // Charlie can stake now + assert_ok!(ParachainStaking::join_candidates( + origin_of(AccountId::from(CHARLIE)), + 10_000 * MOVR, + 2u32, + ),); + let candidates = ParachainStaking::candidate_pool(); + assert_eq!(candidates.0[0].owner, AccountId::from(CHARLIE)); + assert_eq!(candidates.0[0].amount, 10_000 * MOVR); + }); +} + +#[test] +fn reward_block_authors() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 100 extra tokens for her mapping deposit + (AccountId::from(ALICE), 20_100 * MOVR), + (AccountId::from(BOB), 10_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 10_000 * MOVR)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + for x in 2..1199 { + run_to_block(x, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + } + // no rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 10_100 * MOVR, + ); + assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 9500 * MOVR,); + run_to_block(1201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 11547666666208000000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9557333332588000000000, + ); + }); +} + +#[test] +fn reward_block_authors_with_parachain_bond_reserved() { + ExtBuilder::default() + .with_balances(vec![ + // Alice gets 100 extra tokens for her mapping deposit + (AccountId::from(ALICE), 20_100 * MOVR), + (AccountId::from(BOB), 10_000 * MOVR), + (AccountId::from(CHARLIE), MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 10_000 * MOVR)]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + assert_ok!(ParachainStaking::set_parachain_bond_account( + root_origin(), + AccountId::from(CHARLIE), + ),); + + run_to_block(1199, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + // no collator rewards doled out yet + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 10_100 * MOVR, + ); + assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 9500 * MOVR,); + + // 30% reserved for parachain bond + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 452515000000000000000, + ); + + run_to_block(1201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + // rewards minted and distributed + assert_eq!( + Balances::usable_balance(AccountId::from(ALICE)), + 11117700475903800000000, + ); + assert_eq!( + Balances::usable_balance(AccountId::from(BOB)), + 9535834523343675000000, + ); + // 30% reserved for parachain bond again + assert_eq!( + Balances::usable_balance(AccountId::from(CHARLIE)), + 910802725000000000000, + ); + }); +} + +#[test] +fn initialize_crowdloan_addresses_with_batch_and_pay() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 48 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * MOVR); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * MOVR); + let expected = RuntimeEvent::Utility(pallet_utility::Event::BatchCompleted); + assert_eq!(last_event(), expected); + // This one should fail, as we already filled our data + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch { + calls: vec![RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![([4u8; 32].into(), Some(AccountId::from(ALICE)), 432000)] + } + )] + }) + .dispatch(root_origin()) + ); + let expected_fail = RuntimeEvent::Utility(pallet_utility::Event::BatchInterrupted { + index: 0, + error: DispatchError::Module(ModuleError { + index: 90, + error: [8, 0, 0, 0], + message: None, + }), + }); + assert_eq!(last_event(), expected_fail); + // Claim 1 block. + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(CHARLIE)))); + assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(DAVE)))); + + let vesting_period = 48 * WEEKS as u128; + let per_block = (1_050_000 * MOVR) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (450_000 * MOVR) + per_block + ); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (450_000 * MOVR) + per_block + ); + // The total claimed reward should be equal to the account balance at this point. + assert_eq!( + Balances::balance(&AccountId::from(CHARLIE)), + (450_000 * MOVR) + per_block + ); + assert_eq!( + Balances::balance(&AccountId::from(DAVE)), + (450_000 * MOVR) + per_block + ); + assert_noop!( + CrowdloanRewards::claim(origin_of(AccountId::from(ALICE))), + pallet_crowdloan_rewards::Error::::NoAssociatedClaim + ); + }); +} + +#[test] +fn initialize_crowdloan_address_and_change_with_relay_key_sig() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + + let (pair1, _) = sp_core::sr25519::Pair::generate(); + let (pair2, _) = sp_core::sr25519::Pair::generate(); + + let public1 = pair1.public(); + let public2 = pair2.public(); + + // signature: + // WRAP_BYTES|| NetworkIdentifier|| new_account || previous_account || WRAP_BYTES + let mut message = pallet_crowdloan_rewards::WRAPPED_BYTES_PREFIX.to_vec(); + message.append(&mut b"moonriver-".to_vec()); + message.append(&mut AccountId::from(DAVE).encode()); + message.append(&mut AccountId::from(CHARLIE).encode()); + message.append(&mut pallet_crowdloan_rewards::WRAPPED_BYTES_POSTFIX.to_vec()); + let signature1 = pair1.sign(&message); + let signature2 = pair2.sign(&message); + + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + // two relay accounts pointing at the same reward account + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public1.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + public2.into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 900_000 * MOVR); + + // this should fail, as we are only providing one signature + assert_noop!( + CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![(public1.into(), signature1.clone().into())] + ), + pallet_crowdloan_rewards::Error::::InsufficientNumberOfValidProofs + ); + + // this should be valid + assert_ok!(CrowdloanRewards::change_association_with_relay_keys( + origin_of(AccountId::from(CHARLIE)), + AccountId::from(DAVE), + AccountId::from(CHARLIE), + vec![ + (public1.into(), signature1.into()), + (public2.into(), signature2.into()) + ] + )); + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(DAVE)) + .unwrap() + .claimed_reward, + (900_000 * MOVR) + ); + }); +} + +#[test] +fn claim_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + assert!(CrowdloanRewards::initialized()); + + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * MOVR); + // 30 percent initial payout + assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * MOVR); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Alice uses the crowdloan precompile to claim through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + + // Construct the call data (selector, amount) + let mut call_data = Vec::::from([0u8; 4]); + call_data[0..4].copy_from_slice(&Keccak256::digest(b"claim()")[0..4]); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let vesting_period = 4 * WEEKS as u128; + let per_block = (1_050_000 * MOVR) / vesting_period; + + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)) + .unwrap() + .claimed_reward, + (450_000 * MOVR) + per_block + ); + }) +} + +#[test] +fn is_contributor_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Assert precompile reports Bob is not a contributor + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(AccountId::from(BOB).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(false); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::is_contributor { + contributor: Address(AccountId::from(CHARLIE).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(true); + }) +} + +#[test] +fn reward_info_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + let expected_total: U256 = (1_500_000 * MOVR).into(); + let expected_claimed: U256 = (450_000 * MOVR).into(); + + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, + crowdloan_precompile_address, + CrowdloanRewardsPCall::reward_info { + contributor: Address(AccountId::from(CHARLIE).into()), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns((expected_total, expected_claimed)); + }) +} + +#[test] +fn update_reward_address_via_precompile() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_crowdloan_fund(3_000_000 * MOVR) + .build() + .execute_with(|| { + // set parachain inherent data + set_parachain_inherent_data(); + let init_block = CrowdloanRewards::init_vesting_block(); + // This matches the previous vesting + let end_block = init_block + 4 * WEEKS; + // Batch calls always succeed. We just need to check the inner event + assert_ok!( + RuntimeCall::Utility(pallet_utility::Call::::batch_all { + calls: vec![ + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [4u8; 32].into(), + Some(AccountId::from(CHARLIE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::initialize_reward_vec { + rewards: vec![( + [5u8; 32].into(), + Some(AccountId::from(DAVE)), + 1_500_000 * MOVR + )] + } + ), + RuntimeCall::CrowdloanRewards( + pallet_crowdloan_rewards::Call::::complete_initialization { + lease_ending_block: end_block + } + ) + ] + }) + .dispatch(root_origin()) + ); + + let crowdloan_precompile_address = H160::from_low_u64_be(2049); + + // Charlie uses the crowdloan precompile to update address through the EVM + let gas_limit = 100000u64; + let gas_price: U256 = BASE_FEE_GENESIS.into(); + + // Construct the input data to check if Bob is a contributor + let mut call_data = Vec::::from([0u8; 36]); + call_data[0..4] + .copy_from_slice(&Keccak256::digest(b"update_reward_address(address)")[0..4]); + call_data[16..36].copy_from_slice(&ALICE); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(CHARLIE), + target: crowdloan_precompile_address, + input: call_data, + value: U256::zero(), // No value sent in EVM + gas_limit, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: None, + nonce: None, // Use the next nonce + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + assert!(CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)).is_none()); + assert_eq!( + CrowdloanRewards::accounts_payable(&AccountId::from(ALICE)) + .unwrap() + .claimed_reward, + (450_000 * MOVR) + ); + }) +} + +fn run_with_system_weight(w: Weight, mut assertions: F) +where + F: FnMut() -> (), +{ + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); +} + +#[test] +#[rustfmt::skip] +fn length_fee_is_sensible() { + use sp_runtime::testing::TestXt; + + // tests that length fee is sensible for a few hypothetical transactions + ExtBuilder::default().build().execute_with(|| { + let call = frame_system::Call::remark:: { remark: vec![] }; + let uxt: TestXt<_, ()> = TestXt::new(call, Some((1u64, ()))); + + let calc_fee = |len: u32| -> Balance { + moonriver_runtime::TransactionPayment::query_fee_details(uxt.clone(), len) + .inclusion_fee + .expect("fee should be calculated") + .len_fee + }; + + // editorconfig-checker-disable + // left: cost of length fee, right: size in bytes + // /------------- proportional component: O(N * 1B) + // | /- exponential component: O(N ** 3) + // | | + assert_eq!( 1_000_000_001, calc_fee(1)); + assert_eq!( 10_000_001_000, calc_fee(10)); + assert_eq!( 100_001_000_000, calc_fee(100)); + assert_eq!( 1_001_000_000_000, calc_fee(1_000)); + assert_eq!( 11_000_000_000_000, calc_fee(10_000)); // inflection point + assert_eq!( 1_100_000_000_000_000, calc_fee(100_000)); + assert_eq!( 1_001_000_000_000_000_000, calc_fee(1_000_000)); // one MOVR, ~ 1MB + assert_eq!( 1_000_010_000_000_000_000_000, calc_fee(10_000_000)); + assert_eq!(1_000_000_100_000_000_000_000_000, calc_fee(100_000_000)); + // editorconfig-checker-enable + }); +} + +#[test] +fn multiplier_can_grow_from_zero() { + use frame_support::traits::Get; + + let minimum_multiplier = moonriver_runtime::MinimumMultiplier::get(); + let target = moonriver_runtime::TargetBlockFullness::get() + * RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = + moonriver_runtime::SlowAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) +} + +#[test] +fn ethereum_invalid_transaction() { + ExtBuilder::default().build().execute_with(|| { + // Ensure an extrinsic not containing enough gas limit to store the transaction + // on chain is rejected. + assert_eq!( + Executive::apply_extrinsic(unchecked_eth_tx(INVALID_ETH_TX)), + Err( + sp_runtime::transaction_validity::TransactionValidityError::Invalid( + sp_runtime::transaction_validity::InvalidTransaction::Custom(0u8) + ) + ) + ); + }); +} + +#[test] +fn initial_gas_fee_is_correct() { + use fp_evm::FeeCalculator; + + ExtBuilder::default().build().execute_with(|| { + let multiplier = TransactionPayment::next_fee_multiplier(); + assert_eq!(multiplier, Multiplier::from(10u128)); + + assert_eq!( + TransactionPaymentAsGasPrice::min_gas_price(), + ( + 12_500_000_000u128.into(), + Weight::from_parts(25_000_000u64, 0) + ) + ); + }); +} + +#[test] +fn min_gas_fee_is_correct() { + use fp_evm::FeeCalculator; + use frame_support::traits::Hooks; + + ExtBuilder::default().build().execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::put(Multiplier::from(0)); + TransactionPayment::on_finalize(System::block_number()); // should trigger min to kick in + + let multiplier = TransactionPayment::next_fee_multiplier(); + assert_eq!(multiplier, Multiplier::from(1u128)); + + assert_eq!( + TransactionPaymentAsGasPrice::min_gas_price(), + ( + 1_250_000_000u128.into(), + Weight::from_parts(25_000_000u64, 0) + ) + ); + }); +} + +#[test] +fn transfer_ed_0_substrate() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), (1 * MOVR) + (1 * WEI)), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // Substrate transfer + assert_ok!(Balances::transfer_allow_death( + origin_of(AccountId::from(ALICE)), + AccountId::from(BOB), + 1 * MOVR, + )); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI); + }); +} + +#[test] +fn transfer_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * MOVR) + (21_000 * BASE_FEE_GENESIS)) + (1 * WEI), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * MOVR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(BASE_FEE_GENESIS), + max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENESIS)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // 1 WEI is left in the account + assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI,); + }); +} + +#[test] +fn refund_ed_0_evm() { + ExtBuilder::default() + .with_balances(vec![ + ( + AccountId::from(ALICE), + ((1 * MOVR) + (21_777 * BASE_FEE_GENESIS)), + ), + (AccountId::from(BOB), 0), + ]) + .build() + .execute_with(|| { + // EVM transfer that zeroes ALICE + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(ALICE), + target: H160::from(BOB), + input: Vec::new(), + value: (1 * MOVR).into(), + gas_limit: 21_777u64, + max_fee_per_gas: U256::from(BASE_FEE_GENESIS), + max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENESIS)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // ALICE is refunded + assert_eq!( + Balances::free_balance(AccountId::from(ALICE)), + 777 * BASE_FEE_GENESIS, + ); + }); +} + +#[test] +fn author_does_not_receive_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * MOVR) + (21_000 * (500 * GIGAWEI)), + )]) + .build() + .execute_with(|| { + // Some block author as seen by pallet-evm. + let author = AccountId::from(>::find_author()); + // Currently the default impl of the evm uses `deposit_into_existing`. + // If we were to use this implementation, and for an author to receive eventual tips, + // the account needs to be somehow initialized, otherwise the deposit would fail. + Balances::make_free_balance_be(&author, 100 * MOVR); + + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * MOVR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(300 * GIGAWEI), + max_priority_fee_per_gas: Some(U256::from(200 * GIGAWEI)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + // Author free balance didn't change. + assert_eq!(Balances::free_balance(author), 100 * MOVR,); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_with_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * MOVR) + (21_000 * (2 * BASE_FEE_GENESIS)), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * MOVR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(2u128 * BASE_FEE_GENESIS), + max_priority_fee_per_gas: Some(U256::from(2u128 * BASE_FEE_GENESIS)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + let fee = ((2 * BASE_FEE_GENESIS) * 21_000) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonriver_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn total_issuance_after_evm_transaction_without_priority_fee() { + ExtBuilder::default() + .with_balances(vec![( + AccountId::from(BOB), + (1 * MOVR) + (21_000 * (2 * BASE_FEE_GENESIS)), + )]) + .build() + .execute_with(|| { + let issuance_before = ::Currency::total_issuance(); + // EVM transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::::call { + source: H160::from(BOB), + target: H160::from(ALICE), + input: Vec::new(), + value: (1 * MOVR).into(), + gas_limit: 21_000u64, + max_fee_per_gas: U256::from(BASE_FEE_GENESIS), + max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENESIS)), + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let issuance_after = ::Currency::total_issuance(); + let fee = ((1 * BASE_FEE_GENESIS) * 21_000) as f64; + // 80% was burned. + let expected_burn = (fee * 0.8) as u128; + assert_eq!(issuance_after, issuance_before - expected_burn,); + // 20% was sent to treasury. + let expected_treasury = (fee * 0.2) as u128; + assert_eq!(moonriver_runtime::Treasury::pot(), expected_treasury); + }); +} + +#[test] +fn root_can_change_default_xcm_vers() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let source_id: moonriver_runtime::AssetId = source_location.clone().into(); + // Default XCM version is not set yet, so xtokens should fail because it does not + // know with which version to send + assert_noop!( + XTokens::transfer( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest.clone())), + WeightLimit::Limited(4000000000.into()) + ), + orml_xtokens::Error::::XcmExecutionFailed + ); + + // Root sets the defaultXcm + assert_ok!(PolkadotXcm::force_default_xcm_version( + root_origin(), + Some(2) + )); + + // Now transferring does not fail + assert_ok!(XTokens::transfer( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + Box::new(xcm::VersionedLocation::V4(dest)), + WeightLimit::Limited(4000000000.into()) + )); + }) +} + +#[test] +fn asset_can_be_registered() { + ExtBuilder::default().build().execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonriver_runtime::AssetId = source_location.clone().into(); + let asset_metadata = AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }; + assert_ok!(AssetManager::register_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + source_location, + asset_metadata, + 1u128, + true + )); + assert!(AssetManager::asset_id_type(source_id).is_some()); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_supply_and_balance() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Assert the asset has been created with the correct supply + assert_eq!( + moonriver_runtime::Assets::total_supply(relay_asset_id), + 1_000 * MOVR + ); + + // Access totalSupply through precompile. Important that the context is correct + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::total_supply {}, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * MOVR)); + + // Access balanceOf through precompile + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(ALICE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(1000 * MOVR)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Transfer tokens from Aice to Bob, 400 MOVR. + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::transfer { + to: Address(BOB.into()), + value: { 400 * MOVR }.into(), + }, + ) + .expect_cost(24377) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * MOVR)), + )) + .execute_returns(true); + + // Make sure BOB has 400 MOVR + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(BOB.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * MOVR)); + }); +} + +#[test] +fn xcm_asset_erc20_precompiles_approve() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .build() + .execute_with(|| { + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Aprove Bob for spending 400 MOVR from Alice + Precompiles::new() + .prepare_test( + ALICE, + asset_precompile_address, + ForeignAssetsPCall::approve { + spender: Address(BOB.into()), + value: { 400 * MOVR }.into(), + }, + ) + .expect_cost(14429) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + solidity::encode_event_data(U256::from(400 * MOVR)), + )) + .execute_returns(true); + + // Transfer tokens from Alice to Charlie by using BOB as origin + Precompiles::new() + .prepare_test( + BOB, + asset_precompile_address, + ForeignAssetsPCall::transfer_from { + from: Address(ALICE.into()), + to: Address(CHARLIE.into()), + value: { 400 * MOVR }.into(), + }, + ) + .expect_cost(29748) + .expect_log(log3( + asset_precompile_address, + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + solidity::encode_event_data(U256::from(400 * MOVR)), + )) + .execute_returns(true); + + // Make sure CHARLIE has 400 MOVR + Precompiles::new() + .prepare_test( + CHARLIE, + asset_precompile_address, + ForeignAssetsPCall::balance_of { + who: Address(CHARLIE.into()), + }, + ) + .expect_cost(2000) + .expect_no_logs() + .execute_returns(U256::from(400 * MOVR)); + }); +} + +#[test] +fn xtokens_precompiles_transfer() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // We have the assetId that corresponds to the relay chain registered + let relay_asset_id: moonriver_runtime::AssetId = + AssetType::Xcm(xcm::v3::Location::parent()).into(); + + // Its address is + let asset_precompile_address = Runtime::asset_id_to_account( + FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, + relay_asset_id, + ); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // We use the address of the asset as an identifier of the asset we want to transferS + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer { + currency_address: Address(asset_precompile_address.into()), + amount: 500_000_000_000_000u128.into(), + destination: destination.clone(), + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()) + }) +} + +#[test] +fn xtokens_precompiles_transfer_multiasset() { + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + let xtokens_precompile_address = H160::from_low_u64_be(2052); + + // Alice has 1000 tokens. She should be able to send through precompile + let destination = Location::new( + 1, + [Junction::AccountId32 { + network: None, + id: [1u8; 32], + }], + ); + + // This time we transfer it through TransferMultiAsset + // Instead of the address, we encode directly the multilocation referencing the asset + Precompiles::new() + .prepare_test( + ALICE, + xtokens_precompile_address, + XtokensPCall::transfer_multiasset { + // We want to transfer the relay token + asset: Location::parent(), + amount: 500_000_000_000_000u128.into(), + destination, + weight: 4_000_000, + }, + ) + .expect_cost(57639) + .expect_no_logs() + .execute_returns(()); + }) +} + +#[test] +fn make_sure_polkadot_xcm_cannot_be_called() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let assets: Assets = [Asset { + id: AssetId(moonriver_runtime::xcm_config::SelfLocation::get()), + fun: Fungible(1000), + }] + .to_vec() + .into(); + assert_noop!( + RuntimeCall::PolkadotXcm(pallet_xcm::Call::::reserve_transfer_assets { + dest: Box::new(VersionedLocation::V4(dest.clone())), + beneficiary: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets)), + fee_asset_item: 0, + }) + .dispatch(::RuntimeOrigin::signed( + AccountId::from(ALICE) + )), + frame_system::Error::::CallFiltered + ); + }); +} + +#[test] +fn transactor_cannot_use_more_than_max_weight() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: moonriver_runtime::AssetId = source_location.clone().into(); + assert_ok!(XcmTransactor::register( + root_origin(), + AccountId::from(ALICE), + 0, + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000.into(), + None + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + root_origin(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + 1 + )); + + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonriver_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + vec![], + // 2000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + assert_noop!( + XcmTransactor::transact_through_derivative( + origin_of(AccountId::from(ALICE)), + moonriver_runtime::xcm_config::Transactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsCurrencyId(CurrencyId::ForeignAsset(source_id)), + fee_amount: None + }, + vec![], + // 20000 is the max + TransactWeights { + transact_required_weight_at_most: 17001.into(), + overall_weight: None + }, + false + ), + pallet_xcm_transactor::Error::::MaxWeightTransactReached + ); + }) +} + +#[test] +fn transact_through_signed_precompile_works_v2() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::parent(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .expect_cost(17559) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn transact_through_signed_cannot_send_to_local_chain() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_safe_xcm_version(2) + .build() + .execute_with(|| { + // Destination + let dest = Location::here(); + + let fee_payer_asset = Location::parent(); + + let bytes = vec![1u8, 2u8, 3u8]; + + let total_weight = 1_000_000_000u64; + + let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_transactor_v2_precompile_address, + XcmTransactorV2PCall::transact_through_signed_multilocation { + dest, + fee_asset: fee_payer_asset, + weight: 4_000_000, + call: bytes.into(), + fee_amount: u128::from(total_weight).into(), + overall_weight: total_weight, + }, + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error:") + && from_utf8(&output).unwrap().contains("ErrorValidating") + }); + }); +} + +#[test] +fn call_xtokens_with_fee() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_safe_xcm_version(2) + .with_xcm_assets(vec![XcmAssetInitialization { + asset_type: AssetType::Xcm(xcm::v3::Location::parent()), + metadata: AssetRegistrarMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + is_frozen: false, + }, + balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)], + is_sufficient: true, + }]) + .build() + .execute_with(|| { + let source_location = AssetType::Xcm(xcm::v3::Location::parent()); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + }; + let source_id: moonriver_runtime::AssetId = source_location.clone().into(); + + let before_balance = + moonriver_runtime::Assets::balance(source_id, &AccountId::from(ALICE)); + + // We are able to transfer with fee + assert_ok!(XTokens::transfer_with_fee( + origin_of(AccountId::from(ALICE)), + CurrencyId::ForeignAsset(source_id), + 100_000_000_000_000, + 100, + Box::new(xcm::VersionedLocation::V4(dest.clone())), + WeightLimit::Limited(4000000000.into()) + ),); + + let after_balance = + moonriver_runtime::Assets::balance(source_id, &AccountId::from(ALICE)); + // At least these much (plus fees) should have been charged + assert_eq!(before_balance - 100_000_000_000_000 - 100, after_balance); + }); +} + +#[test] +fn test_xcm_utils_ml_tp_account() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_address_parent: H160 = + ParentIsPreset::::convert_location(&Location::parent()) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: Location::parent(), + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parent)); + + let parachain_2000_multilocation = Location::new(1, [Parachain(2000)]); + let expected_address_parachain: H160 = + SiblingParachainConvertsVia::::convert_location( + ¶chain_2000_multilocation, + ) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: parachain_2000_multilocation, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_parachain)); + + let alice_in_parachain_2000_location = Location::new( + 1, + [ + Parachain(2000), + AccountKey20 { + network: None, + key: ALICE, + }, + ], + ); + let expected_address_alice_in_parachain_2000 = + xcm_builder::HashedDescription::< + AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&alice_in_parachain_2000_location) + .unwrap() + .into(); + + Precompiles::new() + .prepare_test( + ALICE, + xcm_utils_precompile_address, + XcmUtilsPCall::multilocation_to_address { + location: alice_in_parachain_2000_location, + }, + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(Address(expected_address_alice_in_parachain_2000)); + }); +} + +#[test] +fn test_xcm_utils_weight_message() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let expected_weight = + XcmWeight::::clear_origin().ref_time(); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + let input = XcmUtilsPCall::weight_message { + message: message.into(), + }; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(0) + .expect_no_logs() + .execute_returns(expected_weight); + }); +} + +#[test] +fn test_xcm_utils_get_units_per_second() { + ExtBuilder::default().build().execute_with(|| { + let xcm_utils_precompile_address = H160::from_low_u64_be(2060); + let location = SelfReserve::get(); + + let input = XcmUtilsPCall::get_units_per_second { location }; + + let expected_units = + WEIGHT_REF_TIME_PER_SECOND as u128 * moonriver_runtime::currency::WEIGHT_FEE; + + Precompiles::new() + .prepare_test(ALICE, xcm_utils_precompile_address, input) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(expected_units); + }); +} + +#[test] +fn precompile_existence() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let precompile_addresses: std::collections::BTreeSet<_> = vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054, + 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, + 2069, 2070, 2071, 2072, 2073, + ] + .into_iter() + .map(H160::from_low_u64_be) + .collect(); + + for i in 0..3000 { + let address = H160::from_low_u64_be(i); + + if precompile_addresses.contains(&address) { + assert!( + is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return true", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_some(), + "execute({},..) should return Some(_)", + i + ); + } else { + assert!( + !is_precompile_or_fail::(address, 100_000u64).expect("to be ok"), + "is_precompile({}) should return false", + i + ); + + assert!( + precompiles + .execute(&mut MockHandle::new( + address, + Context { + address, + caller: H160::zero(), + apparent_value: U256::zero() + } + ),) + .is_none(), + "execute({},..) should return None", + i + ); + } + } + }); +} + +#[test] +fn removed_precompiles() { + ExtBuilder::default().build().execute_with(|| { + let precompiles = Precompiles::new(); + let removed_precompiles = [1025, 2051, 2062, 2063]; + + for i in 1..3000 { + let address = H160::from_low_u64_be(i); + + if !is_precompile_or_fail::(address, 100_000u64).expect("to be ok") { + continue; + } + + if !removed_precompiles.contains(&i) { + assert!( + match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} should be an active precompile" + ); + continue; + } + + assert!( + !match precompiles.is_active_precompile(address, 100_000u64) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + }, + "{i} shouldn't be an active precompile" + ); + + precompiles + .prepare_test(Alice, address, []) + .execute_reverts(|out| out == b"Removed precompile"); + } + }) +} + +#[test] +fn deal_with_fees_handles_tip() { + use frame_support::traits::OnUnbalanced; + use moonriver_runtime::{DealWithFees, Treasury}; + + ExtBuilder::default().build().execute_with(|| { + // This test checks the functionality of the `DealWithFees` trait implementation in the runtime. + // It simulates a scenario where a fee and a tip are issued to an account and ensures that the + // treasury receives the correct amount (20% of the total), and the rest is burned (80%). + // + // The test follows these steps: + // 1. It issues a fee of 100 and a tip of 1000. + // 2. It checks the total supply before the fee and tip are dealt with, which should be 1_100. + // 3. It checks that the treasury's balance is initially 0. + // 4. It calls `DealWithFees::on_unbalanceds` with the fee and tip. + // 5. It checks that the treasury's balance is now 220 (20% of the fee and tip). + // 6. It checks that the total supply has decreased by 880 (80% of the fee and tip), indicating + // that this amount was burned. + let fee = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(100); + let tip = as frame_support::traits::fungible::Balanced< + AccountId, + >>::issue(1000); + + let total_supply_before = Balances::total_issuance(); + assert_eq!(total_supply_before, 1_100); + assert_eq!(Balances::free_balance(&Treasury::account_id()), 0); + + DealWithFees::on_unbalanceds(vec![fee, tip].into_iter()); + + // treasury should have received 20% + assert_eq!(Balances::free_balance(&Treasury::account_id()), 220); + + // verify 80% burned + let total_supply_after = Balances::total_issuance(); + assert_eq!(total_supply_before - total_supply_after, 880); + }); +} + +#[test] +fn evm_revert_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + // Batch a transfer followed by an invalid call to batch. + // Thus BatchAll will revert the transfer. + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + input: BatchPCall::batch_all { + to: vec![Address(BOB.into()), Address(batch_precompile_address)].into(), + value: vec![U256::from(1 * MOVR), U256::zero()].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: U256::from(BASE_FEE_GENESIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 0, "there should be no transfer event"); + }); +} + +#[test] +fn evm_success_keeps_substrate_events() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .build() + .execute_with(|| { + let batch_precompile_address = H160::from_low_u64_be(2056); + + assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call { + source: ALICE.into(), + target: batch_precompile_address, + input: BatchPCall::batch_all { + to: vec![Address(BOB.into())].into(), + value: vec![U256::from(1 * MOVR)].into(), + call_data: vec![].into(), + gas_limit: vec![].into() + } + .into(), + value: U256::zero(), // No value sent in EVM + gas_limit: 500_000, + max_fee_per_gas: U256::from(BASE_FEE_GENESIS), + max_priority_fee_per_gas: None, + nonce: Some(U256::from(0)), + access_list: Vec::new(), + }) + .dispatch(::RuntimeOrigin::root())); + + let transfer_count = System::events() + .iter() + .filter(|r| match r.event { + RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true, + _ => false, + }) + .count(); + + assert_eq!(transfer_count, 1, "there should be 1 transfer event"); + }); +} + +#[cfg(test)] +mod fee_tests { + use super::*; + use frame_support::{ + traits::ConstU128, + weights::{ConstantMultiplier, WeightToFee}, + }; + use moonriver_runtime::{ + currency, LengthToFee, MinimumMultiplier, RuntimeBlockWeights, SlowAdjustingFeeUpdate, + TargetBlockFullness, TransactionPayment, + }; + use sp_core::Get; + use sp_runtime::FixedPointNumber; + + fn run_with_system_weight(w: Weight, mut assertions: F) + where + F: FnMut() -> (), + { + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + System::set_block_consumed_resources(w, 0); + assertions() + }); + } + + #[test] + fn test_multiplier_can_grow_from_zero() { + let minimum_multiplier = MinimumMultiplier::get(); + let target = TargetBlockFullness::get() + * RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap(); + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target * 101 / 100, || { + let next = SlowAdjustingFeeUpdate::::convert(minimum_multiplier); + assert!( + next > minimum_multiplier, + "{:?} !>= {:?}", + next, + minimum_multiplier + ); + }) + } + + #[test] + fn test_fee_calculation() { + let base_extrinsic = RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let multiplier = sp_runtime::FixedU128::from_float(0.999000000000000000); + let extrinsic_len = 100u32; + let extrinsic_weight = Weight::from_parts(5_000u64, 1); + let tip = 42u128; + type WeightToFeeImpl = ConstantMultiplier>; + type LengthToFeeImpl = LengthToFee; + + // base_fee + (multiplier * extrinsic_weight_fee) + extrinsic_length_fee + tip + let expected_fee = WeightToFeeImpl::weight_to_fee(&base_extrinsic) + + multiplier.saturating_mul_int(WeightToFeeImpl::weight_to_fee(&extrinsic_weight)) + + LengthToFeeImpl::weight_to_fee(&(Weight::from_parts(extrinsic_len as u64, 1))) + + tip; + + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into(); + t.execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::::set(multiplier); + let actual_fee = TransactionPayment::compute_fee( + extrinsic_len, + &frame_support::dispatch::DispatchInfo { + class: DispatchClass::Normal, + pays_fee: frame_support::dispatch::Pays::Yes, + weight: extrinsic_weight, + }, + tip, + ); + + assert_eq!( + expected_fee, + actual_fee, + "The actual fee did not match the expected fee, diff {}", + actual_fee - expected_fee + ); + }); + } +} diff --git a/tracing/2900/runtime/moonriver/tests/runtime_apis.rs b/tracing/2900/runtime/moonriver/tests/runtime_apis.rs new file mode 100644 index 00000000..020ae32f --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/runtime_apis.rs @@ -0,0 +1,396 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonriver Runtime Api Integration Tests + +mod common; +use common::*; + +use fp_evm::GenesisAccount; +use frame_support::assert_ok; +use nimbus_primitives::NimbusId; +use pallet_evm::{Account as EVMAccount, AddressMapping, FeeCalculator}; +use sp_core::{ByteArray, H160, H256, U256}; + +use fp_rpc::runtime_decl_for_ethereum_runtime_rpc_api::EthereumRuntimeRPCApi; +use moonbeam_rpc_primitives_txpool::runtime_decl_for_tx_pool_runtime_api::TxPoolRuntimeApi; +use nimbus_primitives::runtime_decl_for_nimbus_api::NimbusApi; +use std::{collections::BTreeMap, str::FromStr}; + +#[test] +fn ethereum_runtime_rpc_api_chain_id() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Runtime::chain_id(), CHAIN_ID); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_basic() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * MOVR)]) + .build() + .execute_with(|| { + assert_eq!( + Runtime::account_basic(H160::from(ALICE)), + EVMAccount { + balance: U256::from(2_000 * MOVR), + nonce: U256::zero() + } + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_gas_price() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!( + Runtime::gas_price(), + TransactionPaymentAsGasPrice::min_gas_price().0 + ); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_account_code_at() { + let address = H160::from(EVM_CONTRACT); + let code: Vec = vec![1, 2, 3, 4, 5]; + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: code.clone(), + nonce: Default::default(), + storage: Default::default(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::account_code_at(address), code); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_author() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + assert_eq!(Runtime::author(), H160::from(ALICE)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_storage_at() { + let address = H160::from(EVM_CONTRACT); + let mut key = [0u8; 32]; + key[31..32].copy_from_slice(&[6u8][..]); + let mut value = [0u8; 32]; + value[31..32].copy_from_slice(&[7u8][..]); + let item = H256::from_slice(&key[..]); + let mut storage: BTreeMap = BTreeMap::new(); + storage.insert(H256::from_slice(&key[..]), item); + ExtBuilder::default() + .with_evm_accounts({ + let mut map = BTreeMap::new(); + map.insert( + address, + GenesisAccount { + balance: U256::zero(), + code: Vec::new(), + nonce: Default::default(), + storage: storage.clone(), + }, + ); + map + }) + .build() + .execute_with(|| { + assert_eq!(Runtime::storage_at(address, U256::from(6)), item); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_call() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 2_000 * MOVR), + ]) + .build() + .execute_with(|| { + let execution_result = Runtime::call( + H160::from(ALICE), // from + H160::from(BOB), // to + Vec::new(), // data + U256::from(1000u64), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_create() { + ExtBuilder::default() + .with_balances(vec![(AccountId::from(ALICE), 2_000 * MOVR)]) + .build() + .execute_with(|| { + let execution_result = Runtime::create( + H160::from(ALICE), // from + vec![0, 1, 1, 0], // data + U256::zero(), // value + U256::from(100000u64), // gas_limit + None, // max_fee_per_gas + None, // max_priority_fee_per_gas + None, // nonce + false, // estimate + None, // access_list + ); + assert!(execution_result.is_ok()); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_transaction_statuses() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 2_000 * MOVR), + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + // set_author(NimbusId::from_slice(&ALICE_NIMBUS)); + let result = + Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)).expect("Apply result."); + assert_eq!(result, Ok(())); + rpc_run_to_block(2); + let statuses = + Runtime::current_transaction_statuses().expect("Transaction statuses result."); + assert_eq!(statuses.len(), 1); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_block() { + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + // set_author(NimbusId::from_slice(&ALICE_NIMBUS)); + rpc_run_to_block(2); + let block = Runtime::current_block().expect("Block result."); + assert_eq!(block.header.number, U256::from(1u8)); + }); +} + +#[test] +fn ethereum_runtime_rpc_api_current_receipts() { + let alith = ::AddressMapping::into_account_id( + H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac") + .expect("internal H160 is valid; qed"), + ); + ExtBuilder::default() + .with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .with_balances(vec![ + (alith, 2_000 * MOVR), + (AccountId::from(ALICE), 2_000 * MOVR), + (AccountId::from(BOB), 1_000 * MOVR), + ]) + .with_delegations(vec![( + AccountId::from(BOB), + AccountId::from(ALICE), + 500 * MOVR, + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + // set_author(NimbusId::from_slice(&ALICE_NIMBUS)); + let result = + Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX)).expect("Apply result."); + assert_eq!(result, Ok(())); + rpc_run_to_block(2); + let receipts = Runtime::current_receipts().expect("Receipts result."); + assert_eq!(receipts.len(), 1); + }); +} + +#[test] +fn txpool_runtime_api_extrinsic_filter() { + ExtBuilder::default().build().execute_with(|| { + let non_eth_uxt = UncheckedExtrinsic::new_unsigned( + pallet_balances::Call::::transfer_allow_death { + dest: AccountId::from(BOB), + value: 1 * MOVR, + } + .into(), + ); + let eth_uxt = unchecked_eth_tx(VALID_ETH_TX); + let txpool = >::extrinsic_filter( + vec![eth_uxt.clone(), non_eth_uxt.clone()], + vec![unchecked_eth_tx(VALID_ETH_TX), non_eth_uxt], + ); + assert_eq!(txpool.ready.len(), 1); + assert_eq!(txpool.future.len(), 1); + }); +} + +#[test] +fn can_author_when_selected_is_empty() { + ExtBuilder::default() + .with_balances(vec![ + (AccountId::from(ALICE), 20_000_000 * MOVR), + (AccountId::from(BOB), 10_000_000 * MOVR), + ]) + .with_collators(vec![(AccountId::from(ALICE), 2_000_000 * MOVR)]) + .with_mappings(vec![( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + AccountId::from(ALICE), + )]) + .build() + .execute_with(|| { + set_parachain_inherent_data(); + run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }; + + // Base case: ALICE can author blocks when she is the only candidate + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Remove ALICE from candidate pool, leaving the candidate_pool empty + assert_ok!(ParachainStaking::go_offline(origin_of(AccountId::from( + ALICE + )))); + + // Need to fast forward to right before the next session, which is when selected candidates + // will be updated. We want to test the creation of the first block of the next session. + run_to_block(1799, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1799, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + + // Check that it works as expected after session update + run_to_block(1800, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap())); + + assert_eq!(ParachainStaking::candidate_pool().0.len(), 0); + + let slot_number = 0; + let parent = Header { + digest: Default::default(), + extrinsics_root: Default::default(), + number: 1800, + parent_hash: Default::default(), + state_root: Default::default(), + }; + + let can_author_block = Runtime::can_author( + NimbusId::from_slice(&ALICE_NIMBUS).unwrap(), + slot_number, + &parent, + ); + + assert!(can_author_block); + }); +} diff --git a/tracing/2900/runtime/moonriver/tests/xcm_mock/mod.rs b/tracing/2900/runtime/moonriver/tests/xcm_mock/mod.rs new file mode 100644 index 00000000..8d5d6e8c --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/xcm_mock/mod.rs @@ -0,0 +1,273 @@ +// Copyright 2021 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 . + +pub mod parachain; +pub mod relay_chain; +pub mod statemine_like; + +use cumulus_primitives_core::ParaId; +use pallet_xcm_transactor::relay_indices::*; +use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{AccountId32, BuildStorage}; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +use polkadot_runtime_parachains::configuration::{ + GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, +}; +use polkadot_runtime_parachains::paras::{ + GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, +}; +use sp_core::{H160, U256}; +use std::{collections::BTreeMap, str::FromStr}; + +pub const PARAALICE: [u8; 20] = [1u8; 20]; +pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); + +pub fn para_a_account() -> AccountId32 { + ParaId::from(1).into_account_truncating() +} + +pub fn para_b_account() -> AccountId32 { + ParaId::from(2).into_account_truncating() +} + +pub fn para_a_account_20() -> parachain::AccountId { + ParaId::from(1).into_account_truncating() +} + +pub fn evm_account() -> H160 { + H160::from_str("1000000000000000000000000000000000000001").unwrap() +} + +pub fn mock_para_genesis_info() -> ParaGenesisArgs { + ParaGenesisArgs { + genesis_head: vec![1u8].into(), + validation_code: vec![1u8].into(), + para_kind: ParaKind::Parachain, + } +} + +pub fn mock_relay_config() -> HostConfiguration { + HostConfiguration:: { + hrmp_channel_max_capacity: u32::MAX, + hrmp_channel_max_total_size: u32::MAX, + hrmp_max_parachain_inbound_channels: 10, + hrmp_max_parachain_outbound_channels: 10, + hrmp_channel_max_message_size: u32::MAX, + // Changed to avoid aritmetic errors within hrmp_close + max_downward_message_size: 100_000u32, + ..Default::default() + } +} + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_parachain! { + pub struct ParaC { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(3), + } +} + +decl_test_parachain! { + pub struct Statemine { + Runtime = statemine_like::Runtime, + XcmpMessageHandler = statemine_like::MsgQueue, + DmpMessageHandler = statemine_like::MsgQueue, + new_ext = statemine_ext(4), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(vec![1, 2, 3, 4]), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + (3, ParaC), + (4, Statemine), + ], + } +} + +pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; + +pub const INITIAL_EVM_BALANCE: u128 = 0; +pub const INITIAL_EVM_NONCE: u32 = 1; + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(PARAALICE.into(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm_transactor::GenesisConfig:: { + // match relay runtime construct_runtime order in xcm_mock::relay_chain + relay_indices: RelayChainIndices { + hrmp: 6u8, + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + ..Default::default() + }, + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // EVM accounts are self-sufficient. + let mut evm_accounts = BTreeMap::new(); + evm_accounts.insert( + evm_account(), + fp_evm::GenesisAccount { + nonce: U256::from(INITIAL_EVM_NONCE), + balance: U256::from(INITIAL_EVM_BALANCE), + storage: Default::default(), + code: vec![ + 0x00, // STOP + ], + }, + ); + + let genesis_config = pallet_evm::GenesisConfig:: { + accounts: evm_accounts, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn statemine_ext(para_id: u32) -> sp_io::TestExternalities { + use statemine_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (RELAYALICE.into(), INITIAL_BALANCE), + (RELAYBOB.into(), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext(paras: Vec) -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(RELAYALICE, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras + .iter() + .map(|¶_id| (para_id.into(), mock_para_genesis_info())) + .collect(); + + let genesis_config = ConfigurationGenesisConfig:: { + config: mock_relay_config(), + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let genesis_config = ParasGenesisConfig:: { + paras: para_genesis, + ..Default::default() + }; + genesis_config.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type Hrmp = polkadot_runtime_parachains::hrmp::Pallet; + +pub type StatemineBalances = pallet_balances::Pallet; +pub type StatemineChainPalletXcm = pallet_xcm::Pallet; +pub type StatemineAssets = pallet_assets::Pallet; + +pub type ParachainPalletXcm = pallet_xcm::Pallet; +pub type Assets = pallet_assets::Pallet; + +pub type Treasury = pallet_treasury::Pallet; +pub type AssetManager = pallet_asset_manager::Pallet; +pub type XTokens = orml_xtokens::Pallet; +pub type RelayBalances = pallet_balances::Pallet; +pub type ParaBalances = pallet_balances::Pallet; +pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/tracing/2900/runtime/moonriver/tests/xcm_mock/parachain.rs b/tracing/2900/runtime/moonriver/tests/xcm_mock/parachain.rs new file mode 100644 index 00000000..201b4796 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/xcm_mock/parachain.rs @@ -0,0 +1,1094 @@ +// Copyright 2021 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 . + +//! Parachain runtime mock. + +use frame_support::{ + construct_runtime, + dispatch::GetDispatchInfo, + ensure, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU32, Everything, Get, InstanceFilter, Nothing, PalletInfoAccess, + }, + weights::Weight, + PalletId, +}; +use frame_system::{pallet_prelude::BlockNumberFor, EnsureNever, EnsureRoot}; +use pallet_xcm::migration::v1::VersionUncheckedMigrateToV1; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, + Permill, +}; +use sp_std::{convert::TryFrom, prelude::*}; +use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; + +use cumulus_primitives_core::relay_chain::HrmpChannelId; +use orml_traits::parameter_type_with_key; +use pallet_ethereum::PostLogContent; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use xcm::latest::{ + AssetId as XcmAssetId, Error as XcmError, ExecuteXcm, + Junction::{PalletInstance, Parachain}, + Location, NetworkId, Outcome, Xcm, +}; +use xcm_builder::{ + AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, + FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, + NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; + +#[cfg(feature = "runtime-benchmarks")] +use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; +pub use moonriver_runtime::xcm_config::AssetType; +use scale_info::TypeInfo; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; + +pub type AccountId = moonbeam_core_primitives::AccountId; +pub type Balance = u128; +pub type AssetId = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 0; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +pub type ForeignAssetInstance = (); + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 1; // Does not really matter as this will be only called by root + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + AccountKey20Aliases, + // Generate remote accounts according to polkadot standards + xcm_builder::HashedDescription< + AccountId, + xcm_builder::DescribeFamily, + >, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + SignedAccountKey20AsNative, +); + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); + pub MaxInstructions: u32 = 100; +} + +// Instructing how incoming xcm assets will be handled +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + // Do a simple punn to convert an AccountId32 Location into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleports. + NoChecking, + // We dont track any teleports + (), +>; + +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + IsConcrete, + // We can convert the Locations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + +// We use both transactors +pub type AssetTransactors = (LocalAssetTransactor, ForeignFungiblesTransactor); + +pub type XcmRouter = super::ParachainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + /// Xcm fees will go to the treasury account + pub XcmFeesAccount: AccountId = Treasury::account_id(); +} + +/// This is the struct that will handle the revenue from xcm fees +pub type XcmFeesToAccount_ = xcm_primitives::XcmFeesToAccount< + Assets, + ( + ConvertedConcreteId< + AssetId, + Balance, + xcm_primitives::AsAssetType, + JustTry, + >, + ), + AccountId, + XcmFeesAccount, +>; + +parameter_types! { + // We cannot skip the native trader for some specific tests, so we will have to work with + // a native trader that charges same number of units as weight + pub ParaTokensPerSecond: (XcmAssetId, u128, u128) = ( + AssetId(SelfReserve::get()), + 1000000000000, + 0, + ); +} + +parameter_types! { + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub SelfReserve: Location = Location { + parents:0, + interior: [ + PalletInstance(::index() as u8) + ].into() + }; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +use frame_system::RawOrigin; +use sp_runtime::traits::PostDispatchInfoOf; +use sp_runtime::DispatchErrorWithPostInfo; +use xcm_executor::traits::CallDispatcher; +moonbeam_runtime_common::impl_moonbeam_xcm_call!(); + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + // We use three traders + // When we receive either representation of the self-reserve asset, + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + FixedRateOfFungible, + xcm_primitives::FirstAssetTrader, + ); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type CallDispatcher = MoonbeamCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum CurrencyId { + SelfReserve, + ForeignAsset(AssetId), +} + +// How to convert from CurrencyId to Location +pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert> + for CurrencyIdToLocation +where + AssetXConverter: MaybeEquivalence, +{ + fn convert(currency: CurrencyId) -> Option { + match currency { + CurrencyId::SelfReserve => { + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it + let multi: Location = SelfReserve::get(); + Some(multi) + } + CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), + } + } +} + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxAssetsForTransfer: usize = 2; + pub SelfLocation: Location = Location::here(); + pub SelfLocationAbsolute: Location = Location { + parents:1, + interior: [ + Parachain(MsgQueue::parachain_id().into()) + ].into() + }; +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + match (location.parents, location.first_interior()) { + (1, Some(Parachain(4u32))) => Some(50u128), + _ => None, + } + }; +} + +// The XCM message wrapper wrapper +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdConvert = + CurrencyIdToLocation>; + type XcmExecutor = XcmExecutor; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: Balance = 0; + pub const SpendPeriod: u32 = 0; + pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); + pub const MaxApprovals: u32 = 100; + pub TreasuryAccount: AccountId = Treasury::account_id(); +} + +impl pallet_treasury::Config for Runtime { + type PalletId = TreasuryId; + type Currency = Balances; + type ApproveOrigin = EnsureRoot; + type RejectOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = (); + type BurnDestination = (); + type MaxApprovals = MaxApprovals; + type WeightInfo = (); + type SpendFunds = (); + type ProposalBondMaximum = (); + type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot + type AssetKind = (); + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = ConstU32<0>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ArgumentsBenchmarkHelper; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} + +// Pallet to provide the version, used to test runtime upgrade version changes +#[frame_support::pallet] +pub mod mock_version_changer { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_version)] + pub(super) type CurrentVersion = StorageValue<_, XcmVersion, ValueQuery>; + + impl Get for Pallet { + fn get() -> XcmVersion { + Self::current_version() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + VersionChanged(XcmVersion), + } + + impl Pallet { + pub fn set_version(version: XcmVersion) { + CurrentVersion::::put(version); + Self::deposit_event(Event::VersionChanged(version)); + } + } +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl mock_version_changer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +pub type LocalOriginToLocation = + xcm_primitives::SignedToAccountId20; + +parameter_types! { + pub MatcherLocation: Location = Location::here(); +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = frame_support::traits::Nothing; + type XcmExecutor = XcmExecutor; + // Do not allow teleports + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // We use a custom one to test runtime ugprades + type AdvertisedXcmVersion = XcmVersioner; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +// We instruct how to register the Assets +// In this case, we tell it to Create an Asset in pallet-assets +pub struct AssetRegistrar; +use frame_support::pallet_prelude::DispatchResult; +impl pallet_asset_manager::AssetRegistrar for AssetRegistrar { + fn create_foreign_asset( + asset: AssetId, + min_balance: Balance, + metadata: AssetMetadata, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset, + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset, + metadata.name, + metadata.symbol, + metadata.decimals, + false, + ) + } + + fn destroy_foreign_asset(asset: AssetId) -> DispatchResult { + // Mark the asset as destroying + Assets::start_destroy(RuntimeOrigin::root(), asset.into())?; + + Ok(()) + } + + fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight { + RuntimeCall::Assets( + pallet_assets::Call::::start_destroy { + id: asset.into(), + }, + ) + .get_dispatch_info() + .weight + } +} + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, +} + +impl pallet_asset_manager::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetRegistrarMetadata = AssetMetadata; + type ForeignAssetType = AssetType; + type AssetRegistrar = AssetRegistrar; + type ForeignAssetModifierOrigin = EnsureRoot; + type WeightInfo = (); +} + +// 1 KSM should be enough +parameter_types! { + pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); +} + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Transactor = MockTransactors; + type DerivativeAddressRegistrationOrigin = EnsureRoot; + type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; + type CurrencyId = CurrencyId; + type AccountIdToLocation = xcm_primitives::AccountIdToLocation; + type CurrencyIdToLocation = + CurrencyIdToLocation>; + type SelfLocation = SelfLocation; + type Weigher = xcm_builder::FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type XcmSender = XcmRouter; + type BaseXcmWeight = BaseXcmWeight; + type AssetTransactor = AssetTransactors; + type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; + type WeightInfo = (); + type HrmpManipulatorOrigin = EnsureRoot; + type HrmpOpenOrigin = EnsureRoot; + type MaxHrmpFee = xcm_builder::Case; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 1000; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +use sp_core::U256; + +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; +/// Block storage limit in bytes. Set to 40 KB. +const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(u64::MAX); + pub WeightPerGas: Weight = Weight::from_parts(1, 0); + pub GasLimitPovSizeRatio: u64 = { + let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); + block_gas_limit.saturating_div(MAX_POV_SIZE) + }; + pub GasLimitStorageGrowthRatio: u64 = + BlockGasLimit::get().min(u64::MAX.into()).low_u64().saturating_div(BLOCK_STORAGE_LIMIT); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + + type CallOrigin = pallet_evm::EnsureAddressRoot; + type WithdrawOrigin = pallet_evm::EnsureAddressNever; + + type AddressMapping = pallet_evm::IdentityAddressMapping; + type Currency = Balances; + type Runner = pallet_evm::runner::stack::Runner; + + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = (); + type PrecompilesValue = (); + type ChainId = (); + type BlockGasLimit = BlockGasLimit; + type OnChargeTransaction = (); + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type SuicideQuickClearLimit = ConstU32<0>; + type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type Timestamp = Timestamp; + type WeightInfo = pallet_evm::weights::SubstrateWeight; +} + +pub struct NormalFilter; +impl frame_support::traits::Contains for NormalFilter { + fn contains(c: &RuntimeCall) -> bool { + match c { + _ => true, + } + } +} + +// We need to use the encoding from the relay mock runtime +#[derive(Encode, Decode)] +pub enum RelayCall { + #[codec(index = 5u8)] + // the index should match the position of the module in `construct_runtime!` + Utility(UtilityCall), + #[codec(index = 6u8)] + // the index should match the position of the module in `construct_runtime!` + Hrmp(HrmpCall), +} + +#[derive(Encode, Decode)] +pub enum UtilityCall { + #[codec(index = 1u8)] + AsDerivative(u16), +} + +// HRMP call encoding, needed for xcm transactor pallet +#[derive(Encode, Decode)] +pub enum HrmpCall { + #[codec(index = 0u8)] + InitOpenChannel(ParaId, u32, u32), + #[codec(index = 1u8)] + AcceptOpenChannel(ParaId), + #[codec(index = 2u8)] + CloseChannel(HrmpChannelId), + #[codec(index = 6u8)] + CancelOpenRequest(HrmpChannelId, u32), +} + +#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub enum MockTransactors { + Relay, +} + +impl xcm_primitives::XcmTransact for MockTransactors { + fn destination(self) -> Location { + match self { + MockTransactors::Relay => Location::parent(), + } + } +} + +impl xcm_primitives::UtilityEncodeCall for MockTransactors { + fn encode_call(self, call: xcm_primitives::UtilityAvailableCalls) -> Vec { + match self { + MockTransactors::Relay => match call { + xcm_primitives::UtilityAvailableCalls::AsDerivative(a, b) => { + let mut call = + RelayCall::Utility(UtilityCall::AsDerivative(a.clone())).encode(); + call.append(&mut b.clone()); + call + } + }, + } + } +} + +pub struct MockHrmpEncoder; +impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { + fn hrmp_encode_call( + call: xcm_primitives::HrmpAvailableCalls, + ) -> Result, xcm::latest::Error> { + match call { + xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( + HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), + ) + .encode()), + xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { + Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) + } + xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { + Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) + } + } + } +} + +parameter_types! { + pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +} + +impl pallet_ethereum::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type StateRoot = pallet_ethereum::IntermediateStateRoot; + type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; +} + +parameter_types! { + pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); +} + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +pub enum ProxyType { + NotAllowed = 0, + Any = 1, +} + +impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} + +impl InstanceFilter for ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + match self { + ProxyType::NotAllowed => false, + ProxyType::Any => true, + } + } + fn is_superset(&self, _o: &Self) -> bool { + false + } +} + +impl Default for ProxyType { + fn default() -> Self { + Self::NotAllowed + } +} + +parameter_types! { + pub const ProxyCost: u64 = 1; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyCost; + type ProxyDepositFactor = ProxyCost; + type MaxProxies = ConstU32<32>; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ProxyCost; + type AnnouncementDepositFactor = ProxyCost; +} + +pub struct EthereumXcmEnsureProxy; +impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { + fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { + // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies + let def: pallet_proxy::ProxyDefinition = + pallet_proxy::Pallet::::find_proxy( + &delegator, + &delegatee, + Some(ProxyType::Any), + ) + .map_err(|_| "proxy error: expected `ProxyType::Any`")?; + // We only allow to use it for delay zero proxies, as the call will iMmediatly be executed + ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); + Ok(()) + } +} + +impl pallet_ethereum_xcm::Config for Runtime { + type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; + type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; + type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; + type ReservedXcmpWeight = ReservedXcmpWeight; + type EnsureProxy = EthereumXcmEnsureProxy; + type ControllerOrigin = EnsureRoot; +} + +type Block = frame_system::mocking::MockBlockU32; + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_msg_queue, + XcmVersioner: mock_version_changer, + + PolkadotXcm: pallet_xcm, + Assets: pallet_assets, + CumulusXcm: cumulus_pallet_xcm, + XTokens: orml_xtokens, + AssetManager: pallet_asset_manager, + XcmTransactor: pallet_xcm_transactor, + Treasury: pallet_treasury, + Proxy: pallet_proxy, + + Timestamp: pallet_timestamp, + EVM: pallet_evm, + Ethereum: pallet_ethereum, + EthereumXcm: pallet_ethereum_xcm, + } +); + +pub(crate) fn para_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::tokens::{PayFromAccount, UnityAssetBalanceConversion}; +use frame_support::traits::{OnFinalize, OnInitialize, OnRuntimeUpgrade}; +pub(crate) fn on_runtime_upgrade() { + VersionUncheckedMigrateToV1::::on_runtime_upgrade(); +} + +pub(crate) fn para_roll_to(n: BlockNumber) { + while System::block_number() < n { + PolkadotXcm::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + PolkadotXcm::on_initialize(System::block_number()); + } +} diff --git a/tracing/2900/runtime/moonriver/tests/xcm_mock/relay_chain.rs b/tracing/2900/runtime/moonriver/tests/xcm_mock/relay_chain.rs new file mode 100644 index 00000000..40bff5e1 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/xcm_mock/relay_chain.rs @@ -0,0 +1,412 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, IdentityLookup}, + AccountId32, +}; + +use frame_support::weights::{Weight, WeightMeter}; +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{ + configuration, dmp, hrmp, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, paras, shared, +}; +use sp_runtime::transaction_validity::TransactionPriority; +use sp_runtime::Permill; +use xcm::latest::prelude::*; +use xcm_builder::{ + Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, + FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + WithComputedOrigin, +}; +use xcm_executor::{Config, XcmExecutor}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type BlockNumber = BlockNumberFor; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); + type PalletsOrigin = OriginCaller; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub KsmLocation: Location = Here.into(); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: Option = None; + pub UniversalLocation: InteriorLocation = Here; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, + // Not enabled in the relay per se, but we enable it to test + // the transact_through_signed extrinsic + Account32Hash, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); + pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub MatcherLocation: Location = Location::here(); +} + +pub type XcmRouter = super::RelayChainXcmRouter; + +pub type XcmBarrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, +); + +parameter_types! { + pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); + pub Statemine: Location = Parachain(4).into(); + pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); +} + +pub type TrustedTeleporters = xcm_builder::Case; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + +/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this +/// is more to satisfy type requirements rather than to test anything. +pub struct TestNextSessionRotation; + +impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { + fn average_session_length() -> u32 { + 10 + } + + fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } + + fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { + (None, Weight::zero()) + } +} + +impl paras::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = TestNextSessionRotation; + type QueueFootprinter = (); + type OnNewHead = (); + type AssignCoretime = (); +} + +impl dmp::Config for Runtime {} + +impl hrmp::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type WeightInfo = TestHrmpWeightInfo; + type ChannelManager = frame_system::EnsureRoot; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlockU32; + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + ProcessXcmMessage::, RuntimeCall>::process_message( + message, + Junction::Parachain(para.into()), + meter, + id, + ) + } +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); + type QueuePausedQuery = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + MessageQueue: pallet_message_queue, + XcmPallet: pallet_xcm, + Utility: pallet_utility, + Hrmp: hrmp, + Dmp: dmp, + Paras: paras, + Configuration: configuration, + } +); + +pub(crate) fn relay_events() -> Vec { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| Some(e)) + .collect::>() +} + +use frame_support::traits::{OnFinalize, OnInitialize}; +pub(crate) fn relay_roll_to(n: BlockNumber) { + while System::block_number() < n { + XcmPallet::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + XcmPallet::on_initialize(System::block_number()); + } +} + +/// A weight info that is only suitable for testing. +pub struct TestHrmpWeightInfo; + +impl hrmp::WeightInfo for TestHrmpWeightInfo { + fn hrmp_accept_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn force_clean_hrmp(_: u32, _: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_close(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_process_hrmp_open(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_cancel_open_request(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_close_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn hrmp_init_open_channel() -> Weight { + Weight::from_parts(1, 0) + } + fn clean_open_channel_requests(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn force_open_hrmp_channel(_: u32) -> Weight { + Weight::from_parts(1, 0) + } + fn establish_system_channel() -> Weight { + Weight::from_parts(1, 0) + } + + fn poke_channel_deposits() -> Weight { + Weight::from_parts(1, 0) + } +} diff --git a/tracing/2900/runtime/moonriver/tests/xcm_mock/statemine_like.rs b/tracing/2900/runtime/moonriver/tests/xcm_mock/statemine_like.rs new file mode 100644 index 00000000..b3d5e778 --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/xcm_mock/statemine_like.rs @@ -0,0 +1,575 @@ +// Copyright 2021 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 . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, parameter_types, + traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + weights::Weight, +}; +use frame_system::{EnsureRoot, EnsureSigned}; + +use sp_core::H256; +use sp_runtime::{ + traits::{ConstU32, Hash, IdentityLookup}, + AccountId32, +}; + +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_parachain::primitives::Sibling; +use sp_std::convert::TryFrom; +use xcm::latest::prelude::*; +use xcm::VersionedXcm; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; +use xcm_simulator::{ + DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, + XcmpMessageHandlerT as XcmpMessageHandler, +}; +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type AssetId = u128; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); +} + +// Required for runtime benchmarks +pallet_assets::runtime_benchmarks_enabled! { + pub struct BenchmarkHelper; + impl pallet_assets::BenchmarkHelper for BenchmarkHelper + where + AssetIdParameter: From, + { + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + (id as u128).into() + } + } +} + +parameter_types! { + pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) + // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; + pub const ExecutiveBody: BodyId = BodyId::Executive; + pub const AssetAccountDeposit: Balance = 0; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type AssetAccountDeposit = AssetAccountDeposit; + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<656>; + type AssetIdParameter = AssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = BenchmarkHelper; + } +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); + pub Local: Location = Here.into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = + (AssetId(KsmLocation::get()), 1, 1); +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// Means for transacting assets besides the native currency on this chain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ConvertedConcreteId< + AssetId, + Balance, + AsPrefixedGeneralIndex, + JustTry, + >, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only want to allow teleports of known assets. We use non-zero issuance as an indication + // that this asset is known. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `RuntimeOrigin::signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); + pub const MaxInstructions: u32 = 100; +} + +pub struct ParentOrParentsExecutivePlurality; +impl Contains for ParentOrParentsExecutivePlurality { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (1, []) + | ( + 1, + [Plurality { + id: BodyId::Executive, + .. + }] + ) + ) + } +} + +pub struct ParentOrSiblings; +impl Contains for ParentOrSiblings { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (1, []) | (1, [_])) + } +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution + AllowUnpaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, +); + +parameter_types! { + pub MatcherLocation: Location = Location::here(); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = + orml_xcm_support::MultiNativeAsset; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type CallDispatcher = RuntimeCall; + type AssetLocker = (); + type AssetExchanger = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + + type TransactionalProcessor = (); +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +pub type XcmRouter = super::ParachainXcmRouter; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type AdminOrigin = frame_system::EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = Location::new(1, [Parachain(sender.into())]); + let mut id = [0u8; 32]; + id.copy_from_slice(hash.as_ref()); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut id, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => { + (Err(error.clone()), Event::Fail(Some(hash), error)) + } + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => { + (Ok(used), Event::Fail(Some(hash), error)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = VersionedXcm::::decode(&mut &data[..]) + .map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + } + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + } + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x, + &mut id, + limit, + Weight::zero(), + ); + + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + } + } + limit + } + } +} +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 +#[frame_support::pallet] +pub mod mock_statemine_prefix { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn current_prefix)] + pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; + + impl Get for Pallet { + fn get() -> Location { + Self::current_prefix() + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // Changed Prefix + PrefixChanged(Location), + } + + impl Pallet { + pub fn set_prefix(prefix: Location) { + CurrentPrefix::::put(&prefix); + Self::deposit_event(Event::PrefixChanged(prefix)); + } + } +} + +impl mock_statemine_prefix::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +type Block = frame_system::mocking::MockBlockU32; +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + PolkadotXcm: pallet_xcm, + CumulusXcm: cumulus_pallet_xcm, + MsgQueue: mock_msg_queue, + Assets: pallet_assets, + PrefixChanger: mock_statemine_prefix, + + } +); diff --git a/tracing/2900/runtime/moonriver/tests/xcm_tests.rs b/tracing/2900/runtime/moonriver/tests/xcm_tests.rs new file mode 100644 index 00000000..0eab574b --- /dev/null +++ b/tracing/2900/runtime/moonriver/tests/xcm_tests.rs @@ -0,0 +1,3998 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Moonriver Runtime Xcm Tests + +mod xcm_mock; +use frame_support::{ + assert_ok, + traits::{PalletInfo, PalletInfoAccess}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + BoundedVec, +}; +use sp_core::ConstU32; +use sp_runtime::traits::MaybeEquivalence; +use xcm::latest::prelude::{ + AccountId32, AccountKey20, All, BuyExecution, ClearOrigin, DepositAsset, GeneralIndex, + Junction, Junctions, Limited, Location, OriginKind, PalletInstance, Parachain, QueryResponse, + Reanchorable, Response, WeightLimit, WithdrawAsset, Xcm, +}; +use xcm::{VersionedLocation, WrapVersion}; +use xcm_executor::traits::ConvertLocation; +use xcm_mock::parachain; +use xcm_mock::relay_chain; +use xcm_mock::*; +use xcm_simulator::TestExt; +mod common; +use cumulus_primitives_core::relay_chain::HrmpChannelId; +use pallet_xcm_transactor::{ + Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, +}; +use xcm_primitives::{UtilityEncodeCall, DEFAULT_PROOF_SIZE}; + +// Send a relay asset (like DOT) to a parachain A +#[test] +fn receive_relay_asset_from_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // Register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // Verify that parachain received the asset + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +// Send relay asset (like DOT) back from Parachain A to relaychain +#[test] +fn send_relay_asset_to_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register relay asset in paraA + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // free execution + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // First send relay chain asset to Parachain like in previous test + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Lets gather the balance before sending back money + let mut balance_before_sending = 0; + Relay::execute_with(|| { + balance_before_sending = RelayBalances::free_balance(&RELAYALICE); + }); + + // We now send back some money to the relay + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: RELAYALICE.into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 123, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The balances in paraAlice should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); + + // Balances in the relay should have been received + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); + }); +} + +#[test] +fn send_relay_asset_to_para_b() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Register asset in paraA. Free execution + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // First send relay chain asset to Parachain A like in previous test + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); + + // Now send relay asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances should have been substracted + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 23); + }); + + // Para B balances should have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_to_para_b() { + MockNet::reset(); + + // This represents the asset in paraA + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register asset in paraB. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset from para A to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Native token is substracted in paraA + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Asset is minted in paraB + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); +} + +#[test] +fn send_para_a_asset_from_para_b_to_para_c() { + MockNet::reset(); + + // Represents para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in parachain B. Free execution + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location.clone(), + 0u128, + 0 + )); + }); + + // Register para A asset in parachain C. Free execution + ParaC::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send para A asset from para B to para C + let dest = Location { + parents: 1, + interior: [ + Parachain(3), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // The message passed through parachainA so we needed to pay since its the native token + ParaC::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 96); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a() { + MockNet::reset(); + + // Para A asset + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + // Register para A asset in para B + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send para A asset to para B + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Balances have been substracted + ParaA::execute_with(|| { + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // Para B balances have been credited + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // Send back para A asset to para A + let dest = Location { + parents: 1, + interior: [ + Parachain(1), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaB::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + // Para A asset has been credited + ParaA::execute_with(|| { + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a_with_new_reanchoring() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // Free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + ParaB::execute_with(|| { + // Free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // This time we will force the new reanchoring by manually sending the + // Message through polkadotXCM pallet + + let dest = Location { + parents: 1, + interior: [Parachain(1)].into(), + }; + + let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); + + let message = xcm::VersionedXcm::<()>::V4(Xcm(vec![ + WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), + ClearOrigin, + BuyExecution { + fees: (reanchored_para_a_balances, 100).into(), + weight_limit: Limited(80.into()), + }, + DepositAsset { + assets: All.into(), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: PARAALICE, + }], + ), + }, + ])); + ParaB::execute_with(|| { + // Send a message to the sovereign account in ParaA to withdraw + // and deposit asset + assert_ok!(ParachainPalletXcm::send( + parachain::RuntimeOrigin::root(), + Box::new(dest.into()), + Box::new(message), + )); + }); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // This time we will force the new reanchoring by manually sending the + // Message through polkadotXCM pallet + + let dest = Location { + parents: 1, + interior: [Parachain(1)].into(), + }; + + let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); + + let message = xcm::VersionedXcm::<()>::V4(Xcm(vec![ + WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), + ClearOrigin, + BuyExecution { + fees: (reanchored_para_a_balances, 100).into(), + weight_limit: Limited(80.into()), + }, + DepositAsset { + assets: All.into(), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: PARAALICE, + }], + ), + }, + ])); + ParaB::execute_with(|| { + // Send a message to the sovereign account in ParaA to withdraw + // and deposit asset + assert_ok!(ParachainPalletXcm::send( + parachain::RuntimeOrigin::root(), + Box::new(dest.into()), + Box::new(message), + )); + }); + + ParaA::execute_with(|| { + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn receive_relay_asset_with_trader() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // We are sending 100 tokens from relay. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Therefore with no refund, we should receive 10 tokens less + // Native trader fails for this, and we use the asset trader + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 100).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // non-free execution, not full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // In destination chain, we only need 4 weight + // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // We are sending 100 tokens from para A. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Since we set 10 weight in destination chain, 25 will be charged upfront + // 15 of those will be refunded, while 10 will go to treasury as the true weight used + // will be 4 + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader_and_fee() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // With these units per second, 80K weight convrets to 1 asset unit + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 12500000u128, + 0 + )); + }); + + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + + // we use transfer_with_fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) + )); + }); + ParaA::execute_with(|| { + // 100 tokens transferred plus 1 taken from fees + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 - 1 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received because trully the xcm instruction does not cost + // what it is specified + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 101); + }); +} + +#[test] +fn error_when_not_paying_enough() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + // This time we are gonna put a rather high number of units per second + // we know later we will divide by 1e12 + // Lets put 1e6 as units per second + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 2500000000000u128, + 0 + )); + }); + + // We are sending 100 tokens from relay. + // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 + // Therefore with no refund, we should receive 10 tokens less + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 5).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // amount not received as it is not paying enough + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + }); +} + +#[test] +fn transact_through_derivative_multilocation() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + encoded, + // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000003000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + false + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_derivative_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + // Let's construct the call to know how much weight it is going to require + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_derivative( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::MockTransactors::Relay, + 0, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee weight mapping + fee_amount: Some(overall_weight as u128) + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + let event_found: Option = parachain::para_events() + .iter() + .find_map(|event| match event.clone() { + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { + .. + }) => Some(event.clone()), + _ => None, + }); + // Assert that the events do not contain the assets being trapped + assert!(event_found.is_none()); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn transact_through_sovereign() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000003100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000003000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000003000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(®istered_address) == 0); + }); +} + +#[test] +fn transact_through_sovereign_with_custom_fee_weight_refund() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 1u128, + 0 + )); + }); + + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 4000009100u128).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009100); + }); + + // Register address + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::register( + parachain::RuntimeOrigin::root(), + PARAALICE.into(), + 0, + )); + }); + + // Send to registered address + let registered_address = derivative_account_id(para_a_account(), 0); + let dest = Location { + parents: 1, + interior: [AccountId32 { + network: None, + id: registered_address.clone().into(), + }] + .into(), + }; + + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 4000009000); + }); + + // What we will do now is transfer this relay tokens from the derived account to the sovereign + // again + Relay::execute_with(|| { + // free execution,x full amount received + assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); + 0 + }); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + let total_weight = 4000009000u64; + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + Some(PARAALICE.into()), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + // 1-1 fee-weight mapping + fee_amount: Some(total_weight as u128) + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // free execution,x full amount received + // 4000005186 refunded + 100 transferred = 4000005286 + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000005286); + + assert_eq!(RelayBalances::free_balance(®istered_address), 0); + }); +} + +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_relay() { + MockNet::reset(); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A and set XCM version to 1 + ParaA::execute_with(|| { + parachain::XcmVersioner::set_version(1); + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + let response = Response::Version(2); + let querier: Location = ([]/* Here */).into(); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: Weight::zero(), + querier: Some(querier), + }]); + // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force + // it directly here + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + Relay::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(RelayChainPalletXcm::force_default_xcm_version( + relay_chain::RuntimeOrigin::root(), + Some(2) + )); + + // Wrap version, which sets VersionedStorage + // This is necessary because the mock router does not use wrap_version, but + // this is not necessary in prod + assert_ok!(::wrap_version( + &Parachain(1).into(), + mock_message + )); + + // Transfer assets. Since it is an unknown destination, it will query for version + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + + // Let's advance the relay. This should trigger the subscription message + relay_chain::relay_roll_to(2); + + // queries should have been updated + assert!(RelayChainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 1, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the version change + assert!(relay_chain::relay_events().contains(&expected_supported_version)); + }); + + // ParaA changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaA::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().iter().any(|e| matches!( + e, + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { + result: 2, + .. + }) + ))); + }); + + // This event should have been seen in the relay + let expected_supported_version_2: relay_chain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 0, + interior: [Parachain(1)].into(), + }, + version: 2, + } + .into(); + + Relay::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(relay_chain::relay_events().contains(&expected_supported_version_2)); + }); +} + +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_para_b() { + MockNet::reset(); + + let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(¶_a_balances).expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + let response = Response::Version(2); + let querier: Location = [] /* Here */ + .into(); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: Weight::zero(), + querier: Some(querier), + }]); + + ParaA::execute_with(|| { + // advertised version + parachain::XcmVersioner::set_version(2); + }); + + ParaB::execute_with(|| { + // Let's try with v0 + parachain::XcmVersioner::set_version(0); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + ParaA::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(ParachainPalletXcm::force_default_xcm_version( + parachain::RuntimeOrigin::root(), + Some(2) + )); + // Wrap version, which sets VersionedStorage + assert_ok!(::wrap_version( + &Location::new(1, [Parachain(2)]).into(), + mock_message + )); + + parachain::para_roll_to(2); + + // queries should have been updated + assert!(ParachainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: parachain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 1, + interior: [Parachain(2)].into(), + }, + version: 0, + } + .into(); + + ParaA::execute_with(|| { + // Assert that the events vector contains the version change + assert!(parachain::para_events().contains(&expected_supported_version)); + }); + + // Let's ensure talking in v0 works + let dest = Location { + parents: 1, + interior: [ + Parachain(2), + AccountKey20 { + network: None, + key: PARAALICE.into(), + }, + ] + .into(), + }; + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) + )); + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // ParaB changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaB::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().iter().any(|e| matches!( + e, + parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { + result: 2, + .. + }) + ))); + }); + + // This event should have been seen in para A + let expected_supported_version_2: parachain::RuntimeEvent = + pallet_xcm::Event::SupportedVersionChanged { + location: Location { + parents: 1, + interior: [Parachain(2)].into(), + }, + version: 2, + } + .into(); + + // Para A should have received the version change + ParaA::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(parachain::para_events().contains(&expected_supported_version_2)); + }); +} + +#[test] +fn receive_asset_with_no_sufficients_not_possible_if_non_existent_account() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should not have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 0); + }); + + // Send native token to fresh_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + fresh_account.into(), + 100 + )); + }); + + // Re-send tokens + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { + MockNet::reset(); + + let fresh_account = [2u8; 20]; + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: fresh_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // parachain should have received assets + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &fresh_account.into()), 123); + }); +} + +#[test] +fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { + MockNet::reset(); + + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + // Evm account is self sufficient + ParaA::execute_with(|| { + assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + // Evm account sufficient ref count increased by 1. + ParaA::execute_with(|| { + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); + }); + + ParaA::execute_with(|| { + // Remove the account from the evm context. + parachain::EVM::remove_account(&evm_account()); + // Evm account sufficient ref count decreased by 1. + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); + }); +} + +#[test] +fn empty_account_should_not_be_reset() { + MockNet::reset(); + + // Test account has nonce 1 on genesis. + let mut sufficient_account = [0u8; 20]; + sufficient_account[0..20].copy_from_slice(&evm_account()[..]); + + let evm_account_id = parachain::AccountId::from(sufficient_account); + + let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_id: parachain::AssetId = source_location.clone().into(); + let asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + // register relay asset in parachain A + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata, + 1u128, + false + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + // Send native token to evm_account + ParaA::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + evm_account_id, + 100 + )); + }); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: sufficient_account, + } + .into(); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new(VersionedLocation::V4(dest.clone()).clone().into()), + Box::new(([] /* Here */, 123).into()), + 0, + )); + }); + + ParaA::execute_with(|| { + // Empty the assets from the account. + // As this makes the account go below the `min_balance`, the account is considered dead + // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. + assert_ok!(parachain::Assets::transfer( + parachain::RuntimeOrigin::signed(evm_account_id), + source_id, + PARAALICE.into(), + 123 + )); + // Verify account asset balance is Zero. + assert_eq!( + parachain::Assets::balance(source_id, &evm_account_id.into()), + 0 + ); + // Because we no longer have consumer references, we can set the balance to Zero. + // This would reset the account if our ED were to be > than Zero. + assert_ok!(ParaBalances::force_set_balance( + parachain::RuntimeOrigin::root(), + evm_account_id, + 0, + )); + // Verify account native balance is Zero. + assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); + // Remove the account from the evm context. + // This decreases the sufficients reference by 1 and now is Zero. + parachain::EVM::remove_account(&evm_account()); + // Verify reference count. + let account = parachain::System::account(evm_account_id); + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // revert account.sufficients to 0 + assert_eq!(account.sufficients, 1); + assert_eq!(account.consumers, 0); + assert_eq!(account.providers, 1); + // We expect the account to be alive in a Zero ED context. + // TODO: since the suicided logic was introduced an smart contract account + // is not deleted completely until it's data is deleted. Data deletion + // will be implemented in a future release + // the following needs to be 1 + assert_eq!(parachain::System::account_nonce(evm_account_id), 2); + }); +} + +#[test] +fn test_statemine_like() { + MockNet::reset(); + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + let statemine_asset_a_balances = Location::new( + 1, + [ + Parachain(4), + PalletInstance(5), + xcm::latest::prelude::GeneralIndex(0u128), + ], + ); + let source_location = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemine_asset_a_balances) + .expect("convert to v3"), + ); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"StatemineToken".to_vec(), + symbol: b"StatemineToken".to_vec(), + decimals: 12, + }; + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + source_location.clone(), + asset_metadata.clone(), + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + source_location, + 0u128, + 0 + )); + }); + + Statemine::execute_with(|| { + // Set new prefix + statemine_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + assert_ok!(StatemineAssets::create( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 1 + )); + + assert_ok!(StatemineAssets::mint( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 0, + RELAYALICE, + 300000000000000 + )); + + // This is needed, since the asset is created as non-sufficient + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Actually send relay asset to parachain + let dest: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send with new prefix + assert_ok!(StatemineChainPalletXcm::reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new(VersionedLocation::V4(dest).clone().into()), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + xcm::latest::prelude::GeneralIndex(0), + ], + 123 + ) + .into() + ), + 0, + )); + }); + + ParaA::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 123); + }); +} + +#[test] +fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemine asset + let statemine_asset = Location::new( + 1, + [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + ); + let statemine_location_asset = parachain::AssetType::Xcm( + xcm_builder::V4V3LocationConverter::convert(&statemine_asset).expect("convert to v3"), + ); + let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); + + let asset_metadata_statemine_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemine_location_asset.clone(), + asset_metadata_statemine_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemine_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send relay chain asset to Alice in Parachain A + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_relay) + .clone() + .into() + ), + Box::new(([] /* Here */, 200).into()), + 0, + )); + }); + + Statemine::execute_with(|| { + // Set new prefix + statemine_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemineAssets::create( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemineAssets::mint( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Send some native statemine tokens to sovereign for fees. + // We can't pay fees with USDC as the asset is minted as non-sufficient. + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 100000000000000 + )); + + // Send statemine USDC asset to Alice in Parachain A + let parachain_beneficiary_from_statemint: Location = AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + // Send with new prefix + assert_ok!(StatemineChainPalletXcm::reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_from_statemint) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + )); + }); + + let statemine_beneficiary = Location { + parents: 1, + interior: [ + Parachain(4), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ] + .into(), + }; + + ParaA::execute_with(|| { + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemine_asset_id, &PARAALICE.into()), + 125 + ); + + // Alice has received 200 Relay assets + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + Statemine::execute_with(|| { + // Check that BOB's balance is empty before the transfer + assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![]); + }); + + // Transfer USDC from Parachain A to Statemine using Relay asset as fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemine_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(statemine_beneficiary)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + }); + + ParaA::execute_with(|| { + // Alice has 100 USDC less + assert_eq!( + Assets::balance(source_statemine_asset_id, &PARAALICE.into()), + 25 + ); + + // Alice has 100 relay asset less + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemine::execute_with(|| { + // Check that BOB received 100 USDC on statemine + assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); +} + +#[test] +fn transact_through_signed_multilocation() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4000.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000004100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000004100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000004000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + false + )); + }); + + Relay::execute_with(|| { + assert!(RelayBalances::free_balance(¶_a_account()) == 100); + + assert!(RelayBalances::free_balance(&derived) == 0); + }); +} + +#[test] +fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + ParaA::execute_with(|| { + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_multilocation = parachain::SelfLocation::get(); + descend_origin_multilocation + .append_with(signed_origin) + .unwrap(); + + // To convert it to what the relay will see instead of us + descend_origin_multilocation + .reanchor(&Location::parent(), &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::Account32Hash::< + relay_chain::KusamaNetwork, + relay_chain::AccountId, + >::convert_location(&descend_origin_multilocation) + .unwrap(); + + Relay::execute_with(|| { + // free execution, full amount received + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(RelayBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(RelayBalances::free_balance(¶_a_account()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let total_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(total_weight.into())) + }, + true + )); + }); + + Relay::execute_with(|| { + // 100 transferred + assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); + + // 4000005186 refunded + assert_eq!(RelayBalances::free_balance(&derived), 4000005186); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + assert!(ParaBalances::free_balance(&derived) == 0); + + assert!(ParaBalances::free_balance(¶_a_account_20()) == 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_refund() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000009100u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000009100); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + // Then call bytes + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + // 100 to sovereign + dest: para_a_account_20(), + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + let overall_weight = 4000009000u64; + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: Some(overall_weight as u128) + }, + encoded, + // 4000000000 for transfer + 9000 for XCM + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: Some(Limited(overall_weight.into())) + }, + true + )); + }); + + ParaB::execute_with(|| { + // Check the derived account was refunded + assert_eq!(ParaBalances::free_balance(&derived), 8993); + + // Check the transfer was executed + assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact { + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + // 4000000000 for transfer + 4000 for XCM + // 1-1 to fee + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer went through + assert!( + ParaBalances::free_balance(&PARAALICE.into()) + == parachain_b_alice_balances_before + 100 + ); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let mut parachain_b_alice_balances_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer wasn't executed + assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); + }); +} + +#[test] +fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { + MockNet::reset(); + let mut ancestry = Location::parent(); + + let para_b_location = Location::new(1, [Parachain(2)]); + + let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + // ParaB + Box::new(xcm::VersionedLocation::V4(para_b_location.clone())), + // Para charges 1000 for every instruction, and we have 3, so 3 + 3.into(), + 20000000000.into(), + // 4 instructions in transact through signed + Some(4.into()) + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(para_b_balances.clone())), + parachain::ParaTokensPerSecond::get().1 as u128, + )); + ancestry = parachain::UniversalLocation::get().into(); + }); + + // Let's construct the Junction that we will append with DescendOrigin + let signed_origin: Junctions = [AccountKey20 { + network: None, + key: PARAALICE, + }] + .into(); + + let mut descend_origin_location = parachain::SelfLocation::get(); + descend_origin_location.append_with(signed_origin).unwrap(); + + // To convert it to what the paraB will see instead of us + descend_origin_location + .reanchor(¶_b_location, &ancestry.interior) + .unwrap(); + + let derived = xcm_builder::HashedDescription::< + parachain::AccountId, + xcm_builder::DescribeFamily, + >::convert_location(&descend_origin_location) + .unwrap(); + + let transfer_recipient = evm_account(); + let mut transfer_recipient_balance_before = 0; + ParaB::execute_with(|| { + assert_ok!(ParaBalances::transfer_allow_death( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + derived.clone(), + 4000000104u128, + )); + // derived account has all funds + assert!(ParaBalances::free_balance(&derived) == 4000000104); + // sovereign account has 0 funds + assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); + + transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); + + // Add proxy ALICE -> derived + let _ = parachain::Proxy::add_proxy_delegate( + &PARAALICE.into(), + derived, + parachain::ProxyType::Any, + 0, + ); + }); + + // Encode the call. Balances transact to para_a_account + // First index + let mut encoded: Vec = Vec::new(); + let index = + ::PalletInfo::index::() + .unwrap() as u8; + + encoded.push(index); + + use sp_core::U256; + // Let's do a EVM transfer + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), + value: U256::from(100), + input: BoundedVec::< + u8, + ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> + >::try_from(vec![]).unwrap(), + access_list: None, + }); + + // Then call bytes + let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { + transact_as: PARAALICE.into(), + xcm_transaction: eth_tx, + } + .encode(); + encoded.append(&mut call_bytes); + + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_signed( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(xcm::VersionedLocation::V4(para_b_location)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + para_b_balances + ))), + fee_amount: None + }, + encoded, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + ParaB::execute_with(|| { + // Make sure the EVM transfer was executed + assert!( + ParaBalances::free_balance(&transfer_recipient.into()) + == transfer_recipient_balance_before + 100 + ); + }); +} + +#[test] +fn hrmp_init_accept_through_root() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_b_account(), + 1000u128 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp init channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: 2u32.into(), + proposed_max_capacity: 1, + proposed_max_message_size: 1 + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { + sender: 1u32.into(), + recipient: 2u32.into(), + proposed_max_capacity: 1u32, + proposed_max_message_size: 1u32, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); + ParaB::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp accept channel + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: 1u32.into() + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { + sender: 1u32.into(), + recipient: 2u32.into(), + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +#[test] +fn hrmp_close_works() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(RelayBalances::transfer_allow_death( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 1000u128 + )); + assert_ok!(Hrmp::force_open_hrmp_channel( + relay_chain::RuntimeOrigin::root(), + 1u32.into(), + 2u32.into(), + 1u32, + 1u32 + )); + assert_ok!(Hrmp::force_process_hrmp_open( + relay_chain::RuntimeOrigin::root(), + 1u32 + )); + }); + + ParaA::execute_with(|| { + let total_fee = 1_000u128; + let total_weight: u64 = 1_000_000_000; + let tx_weight: u64 = 500_000_000; + // Root can send hrmp close + assert_ok!(XcmTransactor::hrmp_manage( + parachain::RuntimeOrigin::root(), + HrmpOperation::Close(HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into() + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + TransactWeights { + transact_required_weight_at_most: tx_weight.into(), + overall_weight: Some(Limited(total_weight.into())) + } + )); + }); + Relay::execute_with(|| { + let expected_event: relay_chain::RuntimeEvent = + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { + by_parachain: 1u32.into(), + channel_id: HrmpChannelId { + sender: 1u32.into(), + recipient: 2u32.into(), + }, + } + .into(); + assert!(relay_chain::relay_events().contains(&expected_event)); + }); +} + +use parity_scale_codec::{Decode, Encode}; +use sp_io::hashing::blake2_256; + +// Helper to derive accountIds +pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { + let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); + sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") +} diff --git a/tracing/2900/rust-toolchain b/tracing/2900/rust-toolchain new file mode 100644 index 00000000..e7f84ae3 --- /dev/null +++ b/tracing/2900/rust-toolchain @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.74.0" +components = [ "rustfmt", "clippy", "rust-src" ] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/tracing/2900/shared/primitives/ext/Cargo.toml b/tracing/2900/shared/primitives/ext/Cargo.toml new file mode 100644 index 00000000..fc6bbf5e --- /dev/null +++ b/tracing/2900/shared/primitives/ext/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "moonbeam-primitives-ext" +version = '0.1.0' +authors = ['PureStake'] +edition = '2018' +homepage = 'https://moonbeam.network' +license = 'GPL-3.0-only' +repository = 'https://github.com/PureStake/moonbeam/' + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethereum-types = { workspace = true } + +# Moonbeam +evm-tracing-events = { workspace = true } + +# Substrate +parity-scale-codec = { workspace = true } +sp-runtime-interface = { workspace = true } +sp-externalities = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "ethereum-types/std", + "evm-tracing-events/std", + "sp-runtime-interface/std", + "sp-externalities/std", + "sp-std/std", +] \ No newline at end of file diff --git a/tracing/2900/shared/primitives/ext/src/lib.rs b/tracing/2900/shared/primitives/ext/src/lib.rs new file mode 100644 index 00000000..2e0fe897 --- /dev/null +++ b/tracing/2900/shared/primitives/ext/src/lib.rs @@ -0,0 +1,82 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Environmental-aware externalities for EVM tracing in Wasm runtime. This enables +//! capturing the - potentially large - trace output data in the host and keep +//! a low memory footprint in `--execution=wasm`. +//! +//! - The original trace Runtime Api call is wrapped `using` environmental (thread local). +//! - Arguments are scale-encoded known types in the host. +//! - Host functions will decode the input and emit an event `with` environmental. + +#![cfg_attr(not(feature = "std"), no_std)] +use sp_runtime_interface::runtime_interface; + +use parity_scale_codec::Decode; +use sp_std::vec::Vec; + +use evm_tracing_events::{Event, EvmEvent, GasometerEvent, RuntimeEvent, StepEventFilter}; + +#[runtime_interface] +pub trait MoonbeamExt { + fn raw_step(&mut self, _data: Vec) {} + + fn raw_gas(&mut self, _data: Vec) {} + + fn raw_return_value(&mut self, _data: Vec) {} + + fn call_list_entry(&mut self, _index: u32, _value: Vec) {} + + fn call_list_new(&mut self) {} + + // New design, proxy events. + /// An `Evm` event proxied by the Moonbeam runtime to this host function. + /// evm -> moonbeam_runtime -> host. + fn evm_event(&mut self, event: Vec) { + if let Ok(event) = EvmEvent::decode(&mut &event[..]) { + Event::Evm(event).emit(); + } + } + + /// A `Gasometer` event proxied by the Moonbeam runtime to this host function. + /// evm_gasometer -> moonbeam_runtime -> host. + fn gasometer_event(&mut self, event: Vec) { + if let Ok(event) = GasometerEvent::decode(&mut &event[..]) { + Event::Gasometer(event).emit(); + } + } + + /// A `Runtime` event proxied by the Moonbeam runtime to this host function. + /// evm_runtime -> moonbeam_runtime -> host. + fn runtime_event(&mut self, event: Vec) { + if let Ok(event) = RuntimeEvent::decode(&mut &event[..]) { + Event::Runtime(event).emit(); + } + } + + /// Allow the tracing module in the runtime to know how to filter Step event + /// content, as cloning the entire data is expensive and most of the time + /// not necessary. + fn step_event_filter(&self) -> StepEventFilter { + evm_tracing_events::step_event_filter().unwrap_or_default() + } + + /// An event to create a new CallList (currently a new transaction when tracing a block). + #[version(2)] + fn call_list_new(&mut self) { + Event::CallListNew().emit(); + } +} diff --git a/tracing/2900/shared/primitives/rpc/debug/Cargo.toml b/tracing/2900/shared/primitives/rpc/debug/Cargo.toml new file mode 100644 index 00000000..a81a24e7 --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/debug/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "moonbeam-rpc-primitives-debug" +authors = [ "PureStake" ] +edition = "2018" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +repository = "https://github.com/PureStake/moonbeam/" +version = "0.1.0" + +[dependencies] +environmental = { workspace = true } +ethereum = { workspace = true, features = [ "with-codec" ] } +ethereum-types = { workspace = true } +hex = { workspace = true, optional = true, features = [ "serde" ] } +serde = { workspace = true, optional = true, features = [ "derive" ] } + +# Substrate +parity-scale-codec = { workspace = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[features] +default = [ "std" ] + +before_700 = [] +_700_to_1200 = [] +runtime-2900 = [] + +std = [ + "parity-scale-codec/std", + "environmental/std", + "ethereum-types/std", + "ethereum/std", + "hex", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/tracing/2900/shared/primitives/rpc/debug/src/lib.rs b/tracing/2900/shared/primitives/rpc/debug/src/lib.rs new file mode 100644 index 00000000..5be13130 --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/debug/src/lib.rs @@ -0,0 +1,97 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use parity_scale_codec::{Decode, Encode}; + +#[cfg(feature = "before_700")] +use ethereum::Transaction; +#[cfg(feature = "_700_to_1200")] +use ethereum::TransactionV0 as Transaction; +#[cfg(all(not(feature = "before_700"), not(feature = "_700_to_1200")))] +use ethereum::TransactionV2 as Transaction; + +use ethereum_types::{H160, H256, U256}; +use sp_std::vec::Vec; + +#[cfg(feature = "runtime-2900")] +sp_api::decl_runtime_apis! { + #[api_version(5)] + pub trait DebugRuntimeApi { + fn trace_transaction( + extrinsics: Vec, + transaction: &Transaction, + header: &Block::Header, + ) -> Result<(), sp_runtime::DispatchError>; + + fn trace_block( + extrinsics: Vec, + known_transactions: Vec, + header: &Block::Header, + ) -> Result<(), sp_runtime::DispatchError>; + } +} + +#[cfg(all( + not(feature = "before_700"), + not(feature = "_700_to_1200"), + not(feature = "runtime-2900") +))] +sp_api::decl_runtime_apis! { + #[api_version(4)] + pub trait DebugRuntimeApi { + fn trace_transaction( + extrinsics: Vec, + transaction: &Transaction, + ) -> Result<(), sp_runtime::DispatchError>; + + fn trace_block( + extrinsics: Vec, + known_transactions: Vec, + ) -> Result<(), sp_runtime::DispatchError>; + } +} + +#[cfg(any(feature = "before_700", feature = "_700_to_1200"))] +sp_api::decl_runtime_apis! { + pub trait DebugRuntimeApi { + fn trace_transaction( + extrinsics: Vec, + transaction: &Transaction, + ) -> Result<(), sp_runtime::DispatchError>; + + fn trace_block( + extrinsics: Vec, + known_transactions: Vec, + ) -> Result<(), sp_runtime::DispatchError>; + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode)] +pub enum TracerInput { + None, + Blockscout, + CallTracer, +} + +/// DebugRuntimeApi V2 result. Trace response is stored in client and runtime api call response is +/// empty. +#[derive(Debug)] +pub enum Response { + Single, + Block, +} diff --git a/tracing/2900/shared/primitives/rpc/evm-tracing-events/Cargo.toml b/tracing/2900/shared/primitives/rpc/evm-tracing-events/Cargo.toml new file mode 100644 index 00000000..d94d74a4 --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/evm-tracing-events/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "evm-tracing-events" +version = '0.1.0' +authors = ['PureStake'] +edition = '2018' +homepage = 'https://moonbeam.network' +license = 'GPL-3.0-only' +repository = 'https://github.com/PureStake/moonbeam/' + +[dependencies] +parity-scale-codec = { workspace = true } +sp-runtime-interface = { workspace = true } + +ethereum = { workspace = true, features = ["with-codec"] } +ethereum-types = { workspace = true } + +environmental = { workspace = true } + +evm = { workspace = true, features = ["with-codec"] } +evm-runtime = { workspace = true } +evm-gasometer = { workspace = true } + +[features] +default = ["std"] + +evm-tracing = ["evm/tracing", "evm-runtime/tracing", "evm-gasometer/tracing"] +std = [ + "parity-scale-codec/std", + "ethereum/std", + "ethereum-types/std", + "environmental/std", + "evm/std", + "evm-runtime/std", + "evm-gasometer/std", +] + +runtime-1600 = [] \ No newline at end of file diff --git a/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/evm.rs b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/evm.rs new file mode 100644 index 00000000..a5ac04ee --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/evm.rs @@ -0,0 +1,258 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +extern crate alloc; + +use alloc::vec::Vec; +use ethereum_types::{H160, H256, U256}; +use evm::ExitReason; +use parity_scale_codec::{Decode, Encode}; + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Transfer { + /// Source address. + pub source: H160, + /// Target address. + pub target: H160, + /// Transfer value. + pub value: U256, +} + +impl From for Transfer { + fn from(i: evm_runtime::Transfer) -> Self { + Self { + source: i.source, + target: i.target, + value: i.value, + } + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode)] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Legacy { + /// Caller of the create. + caller: H160, + }, + /// Create scheme of `CREATE2`. + Create2 { + /// Caller of the create. + caller: H160, + /// Code hash. + code_hash: H256, + /// Salt. + salt: H256, + }, + /// Create at a fixed location. + Fixed(H160), +} + +impl From for CreateScheme { + fn from(i: evm_runtime::CreateScheme) -> Self { + match i { + evm_runtime::CreateScheme::Legacy { caller } => Self::Legacy { caller }, + evm_runtime::CreateScheme::Create2 { + caller, + code_hash, + salt, + } => Self::Create2 { + caller, + code_hash, + salt, + }, + evm_runtime::CreateScheme::Fixed(address) => Self::Fixed(address), + } + } +} + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +pub enum EvmEvent { + Call { + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: super::Context, + }, + Create { + caller: H160, + address: H160, + scheme: CreateScheme, + value: U256, + init_code: Vec, + target_gas: Option, + }, + Suicide { + address: H160, + target: H160, + balance: U256, + }, + Exit { + reason: ExitReason, + return_value: Vec, + }, + TransactCall { + caller: H160, + address: H160, + value: U256, + data: Vec, + gas_limit: u64, + }, + TransactCreate { + caller: H160, + value: U256, + init_code: Vec, + gas_limit: u64, + address: H160, + }, + TransactCreate2 { + caller: H160, + value: U256, + init_code: Vec, + salt: H256, + gas_limit: u64, + address: H160, + }, + PrecompileSubcall { + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: super::Context, + }, +} + +#[cfg(feature = "evm-tracing")] +impl<'a> From> for EvmEvent { + fn from(i: evm::tracing::Event<'a>) -> Self { + match i { + evm::tracing::Event::Call { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => Self::Call { + code_address, + transfer: if let Some(transfer) = transfer { + Some(transfer.clone().into()) + } else { + None + }, + input: input.to_vec(), + target_gas, + is_static, + context: context.clone().into(), + }, + evm::tracing::Event::Create { + caller, + address, + scheme, + value, + init_code, + target_gas, + } => Self::Create { + caller, + address, + scheme: scheme.into(), + value, + init_code: init_code.to_vec(), + target_gas, + }, + evm::tracing::Event::Suicide { + address, + target, + balance, + } => Self::Suicide { + address, + target, + balance, + }, + evm::tracing::Event::Exit { + reason, + return_value, + } => Self::Exit { + reason: reason.clone(), + return_value: return_value.to_vec(), + }, + evm::tracing::Event::TransactCall { + caller, + address, + value, + data, + gas_limit, + } => Self::TransactCall { + caller, + address, + value, + data: data.to_vec(), + gas_limit, + }, + evm::tracing::Event::TransactCreate { + caller, + value, + init_code, + gas_limit, + address, + } => Self::TransactCreate { + caller, + value, + init_code: init_code.to_vec(), + gas_limit, + address, + }, + evm::tracing::Event::TransactCreate2 { + caller, + value, + init_code, + salt, + gas_limit, + address, + } => Self::TransactCreate2 { + caller, + value, + init_code: init_code.to_vec(), + salt, + gas_limit, + address, + }, + #[cfg(feature = "runtime-1600")] + evm::tracing::Event::PrecompileSubcall { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => Self::PrecompileSubcall { + code_address, + transfer: if let Some(transfer) = transfer { + Some(transfer.clone().into()) + } else { + None + }, + input: input.to_vec(), + target_gas, + is_static, + context: context.clone().into(), + }, + } + } +} diff --git a/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/gasometer.rs b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/gasometer.rs new file mode 100644 index 00000000..33a6f724 --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/gasometer.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +use parity_scale_codec::{Decode, Encode}; + +#[derive(Debug, Default, Copy, Clone, Encode, Decode, PartialEq, Eq)] +pub struct Snapshot { + pub gas_limit: u64, + pub memory_gas: u64, + pub used_gas: u64, + pub refunded_gas: i64, +} + +impl Snapshot { + pub fn gas(&self) -> u64 { + self.gas_limit - self.used_gas - self.memory_gas + } +} + +impl From for Snapshot { + fn from(snapshot: evm_gasometer::Snapshot) -> Self { + Self { + gas_limit: snapshot.gas_limit, + memory_gas: snapshot.memory_gas, + used_gas: snapshot.used_gas, + refunded_gas: snapshot.refunded_gas, + } + } +} + +impl From> for Snapshot { + fn from(snapshot_opt: Option) -> Self { + if let Some(snapshot) = snapshot_opt { + snapshot.into() + } else { + Snapshot::default() + } + } +} + +#[derive(Debug, Copy, Clone, Encode, Decode, PartialEq, Eq)] +pub enum GasometerEvent { + RecordCost { + cost: u64, + snapshot: Snapshot, + }, + RecordRefund { + refund: i64, + snapshot: Snapshot, + }, + RecordStipend { + stipend: u64, + snapshot: Snapshot, + }, + RecordDynamicCost { + gas_cost: u64, + memory_gas: u64, + gas_refund: i64, + snapshot: Snapshot, + }, + RecordTransaction { + cost: u64, + snapshot: Snapshot, + }, +} + +#[cfg(feature = "evm-tracing")] +impl From for GasometerEvent { + fn from(i: evm_gasometer::tracing::Event) -> Self { + match i { + evm_gasometer::tracing::Event::RecordCost { cost, snapshot } => Self::RecordCost { + cost, + snapshot: snapshot.into(), + }, + evm_gasometer::tracing::Event::RecordRefund { refund, snapshot } => { + Self::RecordRefund { + refund, + snapshot: snapshot.into(), + } + } + evm_gasometer::tracing::Event::RecordStipend { stipend, snapshot } => { + Self::RecordStipend { + stipend, + snapshot: snapshot.into(), + } + } + evm_gasometer::tracing::Event::RecordDynamicCost { + gas_cost, + memory_gas, + gas_refund, + snapshot, + } => Self::RecordDynamicCost { + gas_cost, + memory_gas, + gas_refund, + snapshot: snapshot.into(), + }, + evm_gasometer::tracing::Event::RecordTransaction { cost, snapshot } => { + Self::RecordTransaction { + cost, + snapshot: snapshot.into(), + } + } + } + } +} diff --git a/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/lib.rs b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/lib.rs new file mode 100644 index 00000000..fcc8f3d8 --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/lib.rs @@ -0,0 +1,284 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! A Proxy in this context is an environmental trait implementor meant to be used for capturing +//! EVM trace events sent to a Host function from the Runtime. Works like: +//! - Runtime Api call `using` environmental. +//! - Runtime calls a Host function with some scale-encoded Evm event. +//! - Host function emits an additional event to this Listener. +//! - Proxy listens for the event and format the actual trace response. +//! +//! There are two proxy types: `Raw` and `CallList`. +//! - `Raw` - used for opcode-level traces. +//! - `CallList` - used for block tracing (stack of call stacks) and custom tracing outputs. +//! +//! The EVM event types may contain references and not implement Encode/Decode. +//! This module provide mirror types and conversion into them from the original events. + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +pub mod evm; +pub mod gasometer; +pub mod runtime; + +pub use self::evm::EvmEvent; +pub use gasometer::GasometerEvent; +pub use runtime::RuntimeEvent; + +use ::evm::Opcode; +use alloc::vec::Vec; +use ethereum_types::{H160, U256}; +use parity_scale_codec::{Decode, Encode}; +use sp_runtime_interface::pass_by::PassByCodec; + +environmental::environmental!(listener: dyn Listener + 'static); + +pub fn using R>(l: &mut (dyn Listener + 'static), f: F) -> R { + listener::using(l, f) +} + +/// Allow to configure which data of the Step event +/// we want to keep or discard. Not discarding the data requires cloning the data +/// in the runtime which have a significant cost for each step. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode, Default, PassByCodec)] +pub struct StepEventFilter { + pub enable_stack: bool, + pub enable_memory: bool, +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)] +pub enum Event { + Evm(evm::EvmEvent), + Gasometer(gasometer::GasometerEvent), + Runtime(runtime::RuntimeEvent), + CallListNew(), +} + +impl Event { + /// Access the global reference and call it's `event` method, passing the `Event` itself as + /// argument. + /// + /// This only works if we are `using` a global reference to a `Listener` implementor. + pub fn emit(self) { + listener::with(|listener| listener.event(self)); + } +} + +/// Main trait to proxy emitted messages. +/// Used 2 times : +/// - Inside the runtime to proxy the events throught the host functions +/// - Inside the client to forward those events to the client listener. +pub trait Listener { + fn event(&mut self, event: Event); + + /// Allow the runtime to know which data should be discarded and not cloned. + /// WARNING: It is only called once when the runtime tracing is instanciated to avoid + /// performing many ext calls. + fn step_event_filter(&self) -> StepEventFilter; +} + +pub fn step_event_filter() -> Option { + let mut filter = None; + listener::with(|listener| filter = Some(listener.step_event_filter())); + filter +} + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Context { + /// Execution address. + pub address: H160, + /// Caller of the EVM. + pub caller: H160, + /// Apparent value of the EVM. + pub apparent_value: U256, +} + +impl From for Context { + fn from(i: evm_runtime::Context) -> Self { + Self { + address: i.address, + caller: i.caller, + apparent_value: i.apparent_value, + } + } +} + +/// Converts an Opcode into its name, stored in a `Vec`. +pub fn opcodes_string(opcode: Opcode) -> Vec { + let tmp; + let out = match opcode { + Opcode(0) => "Stop", + Opcode(1) => "Add", + Opcode(2) => "Mul", + Opcode(3) => "Sub", + Opcode(4) => "Div", + Opcode(5) => "SDiv", + Opcode(6) => "Mod", + Opcode(7) => "SMod", + Opcode(8) => "AddMod", + Opcode(9) => "MulMod", + Opcode(10) => "Exp", + Opcode(11) => "SignExtend", + Opcode(16) => "Lt", + Opcode(17) => "Gt", + Opcode(18) => "Slt", + Opcode(19) => "Sgt", + Opcode(20) => "Eq", + Opcode(21) => "IsZero", + Opcode(22) => "And", + Opcode(23) => "Or", + Opcode(24) => "Xor", + Opcode(25) => "Not", + Opcode(26) => "Byte", + Opcode(27) => "Shl", + Opcode(28) => "Shr", + Opcode(29) => "Sar", + Opcode(32) => "Keccak256", + Opcode(48) => "Address", + Opcode(49) => "Balance", + Opcode(50) => "Origin", + Opcode(51) => "Caller", + Opcode(52) => "CallValue", + Opcode(53) => "CallDataLoad", + Opcode(54) => "CallDataSize", + Opcode(55) => "CallDataCopy", + Opcode(56) => "CodeSize", + Opcode(57) => "CodeCopy", + Opcode(58) => "GasPrice", + Opcode(59) => "ExtCodeSize", + Opcode(60) => "ExtCodeCopy", + Opcode(61) => "ReturnDataSize", + Opcode(62) => "ReturnDataCopy", + Opcode(63) => "ExtCodeHash", + Opcode(64) => "BlockHash", + Opcode(65) => "Coinbase", + Opcode(66) => "Timestamp", + Opcode(67) => "Number", + Opcode(68) => "Difficulty", + Opcode(69) => "GasLimit", + Opcode(70) => "ChainId", + Opcode(80) => "Pop", + Opcode(81) => "MLoad", + Opcode(82) => "MStore", + Opcode(83) => "MStore8", + Opcode(84) => "SLoad", + Opcode(85) => "SStore", + Opcode(86) => "Jump", + Opcode(87) => "JumpI", + Opcode(88) => "GetPc", + Opcode(89) => "MSize", + Opcode(90) => "Gas", + Opcode(91) => "JumpDest", + Opcode(96) => "Push1", + Opcode(97) => "Push2", + Opcode(98) => "Push3", + Opcode(99) => "Push4", + Opcode(100) => "Push5", + Opcode(101) => "Push6", + Opcode(102) => "Push7", + Opcode(103) => "Push8", + Opcode(104) => "Push9", + Opcode(105) => "Push10", + Opcode(106) => "Push11", + Opcode(107) => "Push12", + Opcode(108) => "Push13", + Opcode(109) => "Push14", + Opcode(110) => "Push15", + Opcode(111) => "Push16", + Opcode(112) => "Push17", + Opcode(113) => "Push18", + Opcode(114) => "Push19", + Opcode(115) => "Push20", + Opcode(116) => "Push21", + Opcode(117) => "Push22", + Opcode(118) => "Push23", + Opcode(119) => "Push24", + Opcode(120) => "Push25", + Opcode(121) => "Push26", + Opcode(122) => "Push27", + Opcode(123) => "Push28", + Opcode(124) => "Push29", + Opcode(125) => "Push30", + Opcode(126) => "Push31", + Opcode(127) => "Push32", + Opcode(128) => "Dup1", + Opcode(129) => "Dup2", + Opcode(130) => "Dup3", + Opcode(131) => "Dup4", + Opcode(132) => "Dup5", + Opcode(133) => "Dup6", + Opcode(134) => "Dup7", + Opcode(135) => "Dup8", + Opcode(136) => "Dup9", + Opcode(137) => "Dup10", + Opcode(138) => "Dup11", + Opcode(139) => "Dup12", + Opcode(140) => "Dup13", + Opcode(141) => "Dup14", + Opcode(142) => "Dup15", + Opcode(143) => "Dup16", + Opcode(144) => "Swap1", + Opcode(145) => "Swap2", + Opcode(146) => "Swap3", + Opcode(147) => "Swap4", + Opcode(148) => "Swap5", + Opcode(149) => "Swap6", + Opcode(150) => "Swap7", + Opcode(151) => "Swap8", + Opcode(152) => "Swap9", + Opcode(153) => "Swap10", + Opcode(154) => "Swap11", + Opcode(155) => "Swap12", + Opcode(156) => "Swap13", + Opcode(157) => "Swap14", + Opcode(158) => "Swap15", + Opcode(159) => "Swap16", + Opcode(160) => "Log0", + Opcode(161) => "Log1", + Opcode(162) => "Log2", + Opcode(163) => "Log3", + Opcode(164) => "Log4", + Opcode(176) => "JumpTo", + Opcode(177) => "JumpIf", + Opcode(178) => "JumpSub", + Opcode(180) => "JumpSubv", + Opcode(181) => "BeginSub", + Opcode(182) => "BeginData", + Opcode(184) => "ReturnSub", + Opcode(185) => "PutLocal", + Opcode(186) => "GetLocal", + Opcode(225) => "SLoadBytes", + Opcode(226) => "SStoreBytes", + Opcode(227) => "SSize", + Opcode(240) => "Create", + Opcode(241) => "Call", + Opcode(242) => "CallCode", + Opcode(243) => "Return", + Opcode(244) => "DelegateCall", + Opcode(245) => "Create2", + Opcode(250) => "StaticCall", + Opcode(252) => "TxExecGas", + Opcode(253) => "Revert", + Opcode(254) => "Invalid", + Opcode(255) => "SelfDestruct", + Opcode(n) => { + tmp = alloc::format!("Unknown({})", n); + &tmp + } + }; + out.as_bytes().to_vec() +} diff --git a/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/runtime.rs b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/runtime.rs new file mode 100644 index 00000000..d9ba718d --- /dev/null +++ b/tracing/2900/shared/primitives/rpc/evm-tracing-events/src/runtime.rs @@ -0,0 +1,156 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +extern crate alloc; + +use super::{opcodes_string, Context, StepEventFilter}; +use alloc::vec::Vec; +use ethereum_types::{H160, H256, U256}; +pub use evm::{ExitError, ExitReason, ExitSucceed, Opcode}; +use parity_scale_codec::{Decode, Encode}; + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Stack { + pub data: Vec, + pub limit: u64, +} + +impl From<&evm::Stack> for Stack { + fn from(i: &evm::Stack) -> Self { + Self { + data: i.data().clone(), + limit: i.limit() as u64, + } + } +} + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct Memory { + pub data: Vec, + pub effective_len: U256, + pub limit: u64, +} + +impl From<&evm::Memory> for Memory { + fn from(i: &evm::Memory) -> Self { + Self { + data: i.data().clone(), + effective_len: i.effective_len(), + limit: i.limit() as u64, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode)] +pub enum Capture { + /// The machine has exited. It cannot be executed again. + Exit(E), + /// The machine has trapped. It is waiting for external information, and can + /// be executed again. + Trap(T), +} + +pub type Trap = Vec; // Should hold the marshalled Opcode. + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +pub enum RuntimeEvent { + Step { + context: Context, + // This needs to be marshalled in the runtime no matter what. + opcode: Vec, + // We can use ExitReason with `with-codec` feature, + position: Result, + stack: Option, + memory: Option, + }, + StepResult { + result: Result<(), Capture>, + return_value: Vec, + }, + SLoad { + address: H160, + index: H256, + value: H256, + }, + SStore { + address: H160, + index: H256, + value: H256, + }, +} + +#[cfg(feature = "evm-tracing")] +impl RuntimeEvent { + pub fn from_evm_event<'a>(i: evm_runtime::tracing::Event<'a>, filter: StepEventFilter) -> Self { + match i { + evm_runtime::tracing::Event::Step { + context, + opcode, + position, + stack, + memory, + } => Self::Step { + context: context.clone().into(), + opcode: opcodes_string(opcode), + position: match position { + Ok(position) => Ok(*position as u64), + Err(e) => Err(e.clone()), + }, + stack: if filter.enable_stack { + Some(stack.into()) + } else { + None + }, + memory: if filter.enable_memory { + Some(memory.into()) + } else { + None + }, + }, + evm_runtime::tracing::Event::StepResult { + result, + return_value, + } => Self::StepResult { + result: match result { + Ok(_) => Ok(()), + Err(capture) => match capture { + evm::Capture::Exit(e) => Err(Capture::Exit(e.clone())), + evm::Capture::Trap(t) => Err(Capture::Trap(opcodes_string(*t))), + }, + }, + return_value: return_value.to_vec(), + }, + evm_runtime::tracing::Event::SLoad { + address, + index, + value, + } => Self::SLoad { + address, + index, + value, + }, + evm_runtime::tracing::Event::SStore { + address, + index, + value, + } => Self::SStore { + address, + index, + value, + }, + } + } +} diff --git a/tracing/2900/shared/runtime/evm_tracer/Cargo.toml b/tracing/2900/shared/runtime/evm_tracer/Cargo.toml new file mode 100644 index 00000000..1b724f04 --- /dev/null +++ b/tracing/2900/shared/runtime/evm_tracer/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "moonbeam-evm-tracer" +authors = [ "PureStake" ] +edition = "2018" +homepage = "https://moonbeam.network" +license = "GPL-3.0-only" +repository = "https://github.com/PureStake/moonbeam/" +version = "0.1.0" + +[dependencies] +# In those shared code the branch is irrelevant as long as revers to a rust2018 workspace. +# Each version of the runtime will patch the Subtrate and Frontier dependencies to the correct +# git repo and branch. Since cargo don't want to patch with the same repo url, we use a dummy fork +# not used in the runtimes. + +# Moonbeam +evm-tracing-events = { workspace = true, features = [ "evm-tracing" ] } +moonbeam-primitives-ext = { workspace = true } + +# Substrate +parity-scale-codec = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Frontier +ethereum-types = { workspace = true } +evm = { workspace = true, features = [ "with-codec" ] } +evm-gasometer = { workspace = true } +evm-runtime = { workspace = true } +fp-evm = { workspace = true } +pallet-evm = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "parity-scale-codec/std", + "ethereum-types/std", + "evm-gasometer/std", + "evm-runtime/std", + "evm-tracing-events/std", + "evm/std", + "evm/with-serde", + "fp-evm/std", + "moonbeam-primitives-ext/std", + "pallet-evm/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/tracing/2900/shared/runtime/evm_tracer/src/lib.rs b/tracing/2900/shared/runtime/evm_tracer/src/lib.rs new file mode 100644 index 00000000..5d998775 --- /dev/null +++ b/tracing/2900/shared/runtime/evm_tracer/src/lib.rs @@ -0,0 +1,117 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Substrate EVM tracing. +//! +//! The purpose of this crate is enable tracing the EVM opcode execution and will be used by +//! both Dapp developers - to get a granular view on their transactions - and indexers to access +//! the EVM callstack (internal transactions). +//! +//! Proxies EVM messages to the host functions. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod tracer { + use evm_tracing_events::{EvmEvent, GasometerEvent, RuntimeEvent, StepEventFilter}; + use parity_scale_codec::Encode; + + use evm::tracing::{using as evm_using, EventListener as EvmListener}; + use evm_gasometer::tracing::{using as gasometer_using, EventListener as GasometerListener}; + use evm_runtime::tracing::{using as runtime_using, EventListener as RuntimeListener}; + use sp_std::{cell::RefCell, rc::Rc}; + + struct ListenerProxy(pub Rc>); + impl GasometerListener for ListenerProxy { + fn event(&mut self, event: evm_gasometer::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + impl RuntimeListener for ListenerProxy { + fn event(&mut self, event: evm_runtime::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + impl EvmListener for ListenerProxy { + fn event(&mut self, event: evm::tracing::Event) { + self.0.borrow_mut().event(event); + } + } + + pub struct EvmTracer { + step_event_filter: StepEventFilter, + } + + impl EvmTracer { + pub fn new() -> Self { + Self { + step_event_filter: moonbeam_primitives_ext::moonbeam_ext::step_event_filter(), + } + } + + /// Setup event listeners and execute provided closure. + /// + /// Consume the tracer and return it alongside the return value of + /// the closure. + pub fn trace R>(self, f: F) { + let wrapped = Rc::new(RefCell::new(self)); + + let mut gasometer = ListenerProxy(Rc::clone(&wrapped)); + let mut runtime = ListenerProxy(Rc::clone(&wrapped)); + let mut evm = ListenerProxy(Rc::clone(&wrapped)); + + // Each line wraps the previous `f` into a `using` call. + // Listening to new events results in adding one new line. + // Order is irrelevant when registering listeners. + let f = || runtime_using(&mut runtime, f); + let f = || gasometer_using(&mut gasometer, f); + let f = || evm_using(&mut evm, f); + f(); + } + + pub fn emit_new() { + moonbeam_primitives_ext::moonbeam_ext::call_list_new(); + } + } + + impl EvmListener for EvmTracer { + /// Proxies `evm::tracing::Event` to the host. + fn event(&mut self, event: evm::tracing::Event) { + let event: EvmEvent = event.into(); + let message = event.encode(); + moonbeam_primitives_ext::moonbeam_ext::evm_event(message); + } + } + + impl GasometerListener for EvmTracer { + /// Proxies `evm_gasometer::tracing::Event` to the host. + fn event(&mut self, event: evm_gasometer::tracing::Event) { + let event: GasometerEvent = event.into(); + let message = event.encode(); + moonbeam_primitives_ext::moonbeam_ext::gasometer_event(message); + } + } + + impl RuntimeListener for EvmTracer { + /// Proxies `evm_runtime::tracing::Event` to the host. + fn event(&mut self, event: evm_runtime::tracing::Event) { + let event = RuntimeEvent::from_evm_event(event, self.step_event_filter); + let message = event.encode(); + moonbeam_primitives_ext::moonbeam_ext::runtime_event(message); + } + } +} diff --git a/wasm/moonbase-runtime-2900-substitute-tracing.wasm b/wasm/moonbase-runtime-2900-substitute-tracing.wasm new file mode 100644 index 0000000000000000000000000000000000000000..7f8cc6cf45f9e143caa14b3f050cff92ebdbedf8 GIT binary patch literal 2111185 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

S1*18%(X!wrOfpA+{J}j4=*15BRCa7=JkY;qZsU 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

tc6WDA$wBHAD$yVo?-_`>{r~^}#kEkK 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%0AJRgnWweZY>g3GUUle!6xJd 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%0A2nkC%(DK58-Z{0s@QDbE7DPMnc!| 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%0DBva+I4H2FSJ++^Dlb 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%0AineKQePJC^6U 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�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 z zS@L-#$?bX;k~b{dl8eo%5RT1%-ebXMbWwhYhn0K4=Wo82im2w4>?|iNkDw}wI3_-T zp|W7$BY)07vdYI*3WzT=)w^`@WZLl*76Z#dqF`=IldDLvP*`&!n0aTRL z!_EAl{EX5jSwbE-=3-Fzxvln}_1lQ(85PAw=AfZ1wd!tYCQY1OqBQ(^5c4*U*g|En zAy_sN_R5i2sZLk6srG6n zbi`T6#uVdTrQ4}Zqf`Y9xKb6kdx$7_pTLwY?S!GFmAsZUmi3?xzJTXB$x<+_( zQ^WuK*d2S@=>qJBnS|%zK=&$@!hhJu8!%3T%+`w@J07)^1uVot@J42vRQ7}}?Qraz zi9Ttu{E~BDt}D)Rs5I~j!Y$u=AT-0JIX<&hn}0%Q{DlPQ4i6A}46B_GL&i)<{i%kW^kMh-Q}V#ktdP(f_Ua4C_%c`KVLF2s3tJ=Y#R|;c|)ddvn$hky zRq&SETnGYAthB6)k5!!pC}F+3>P`Oyu_(p~XLPodzCn#|K;gSp4MRp+YPqEXL6A;; z-Fvek)VZ0{0e83XLJWb*?ljv`Z#s45;mV0)P7WKoU#Vqj z17kfkC|+y$sPwBuPP#Wi0vaQAraI3I^6wW>!uvoR2fba&c#}lx+8Fzz98m&hdLns@ zm2Xqt9%EX&^`0EZ)CiPM#*LJW;OO!9qxYvAKqYC;JiqGMo(O*qfVFQ`;&{m9Fat&8 zGx$!zQ*Wmk^5|UzSjA?50VV*6_L*Pq;|`2DS9U-x`2_Vl7y#~(!MxwAH5Kv1M}|%X z-4uyVlC}(K&5(S1yVy>DfJkJoE{}0sDHwsAqD4R}{4G0p8N_Q5{TphX@E1|`|G5gO zSv@Y%)c`AOP|}ged?$Fnb_YA2A-a$6#v6-;tm4}_QV`i;Vbwxc)3>wiM{J7;L-=$A zWtH3=nxCSWh*4!$KsWz*I2A#bpz(kU)D_~{JiGInPF1PnA*hdhCom~Gqu;L#6&L4ZcP9xa4~HPidHT1^k3wnT$k zvU3i7TFopaBdyQh!4U+xD|}y=pS3|`n;^_@|6G=6um}^Pepu9nStH2HnhxRIe{`UgYnMpp2pZ9EQ0Z%NwcoU7THSl1_0tt(@p;rnuS;pxlkrUAagtq|d zU;&e3aW^yR0ds`wpqV}ji45E;#1uxRapF`pFMEJt9!qZ{(&3` zJ3^+yFcjlZsmCUOQh)%y)ubd%e@NZlEF%M=rWy->F^rT!S$k$`?gHrJ9B{9LzyZV= zp82~nHTIr$?r-PEmhNGT@6sM%DllGJT3%4dW*NaNNcX}9mu-L;Yapp63dYZ$a54s5 zLL6eYlp5Duc3j`6ULtyF%a|r|xINxPJ4VPcS%{bkVnl;;E|p#ZUX5qQtS1nO9dPrB z%d%09VaQHU{&nX*ouY4iX1f8anVNbaeo-Rv85dc7E!HjKnoB(j553-n_tVt^xuXi)v>s(OS!NCjRI-r+~>FW=|dF_ys^qP-J~ zKMN;0OeZtPLyT7e$3g%LJimiVqE6~9ePgsxg)(gk!A;W#;E!>#ELm{02y3NnanGOJ z)wao=FS*Zb7pT!Zt^D)|j>IOmm*JBN0vW#a)vMJ={HEX?aft93YAr}-utkexz{|jI zAB||HMa7e}-ot0~%+~qsbnThM98&On!#!7A7>XK?_ZGrT_bzWvPaz$GytFd?-y%{E zN{65q-(r)-$>ZpBNiV+FBh616x&PQ>nT6hK!*=By?RZt0fL5Z0*vRGMualH zasS)rFe`*-NY1yVkI2k8Vm@^=qo-_K_KKohF<%cc8yr>1gMKC7qEB-9*uu~Tifu_f z51`GhZ2Ds^zZzzSSLL%wf%FF4ug!eR&xo)4F#MBhE+y=!mb?P;sw1V#lU7-xV$k0H zIgHHE4C9*&bcCtg9z@+Sc|qS~-&h=W)%ADL)f%>&JmTh}@u!+A7Vu}>g4oX2x=Dx! zA(W850VRqkWWM3>&j+{&E!UKO(RKnT zP3o1cU5>8Mji(rpxZ7)@zGbNe<^py}lMBi6v@VET3Nh7b9^*EFuPMt#2%5! z+z~pE{uCcgByaXTupynLjnEx&jj&`#WPr`F!EADi+pLii9EUMInm&+FcAIIwu&b$1 z_9F@rcLzWqm0NPUt9ZD$f6sB}k1#4$56E$kgaW z=*-b=0GOm!|Iw-V(d#{vq6{cyHYp6<{uc9eaKK3Ug?9@Ref zUDT&rqTwlrhD>&CP0ocE>#`V-QFoV5FbY5T0HKIw^(M?q6($(ec1;AN zMVGmu1dK^g72Rwyr@OnmJ8#p;Y%*J}+q2amOKQ=fWuettYT>!U_J?z)2@mbF!vm{3 zpkik-vri~^=-PDEDWu%~&eUpYuJ2IMN1tEXqemNgchD^R4cT0RTn=?Q{V zgbh7*n};Ryd9|3vLmks! z|Ih2EX?&%svPkKwB#1;us%Vj+8%Ixp+{|DqCdTydxZS8AB4*WEYpu1mMx)VaG#Wh) zs@2Fjj^j9vdmL0N?u#Bn0zp*}Z20$KY8l-PVY(_nx8G9J-RAbmz<77Ni!uEU(=@yS z02MauQqJODVB-6SFII(vKOQC-Ds1?If(k3VbRiGbOB6)6|2vcCVni%JqL6a?Yqs!j z_3GyW{c!My1ot^B(oY~w!>g_ygTp@%hzAlS4xG;X4`#z!y;f^I?;ngn4TyjQgj6Fy zHbtn!ZvPBLH?BB!nM*I2E}MF!6e>=b^`0EsTbWT%iP!Cik+~KXt#|>uj{0k@=-_o3 z6}$9;>6$LT&zu-yS|23BhP4DAI6Frvuo^*sx3G5JV*;1-tz` zLXcAps=Y{)Ls3G5TzfrE{*M|#oE zANM`;H{9;>dksFf4sOU^+;AJ_HcZrEfGw7vd->G$g-0mtx<`fe7$m9+M_69LJj(o- z!VxV0knYjnf)mz1@M9hgfEs{VZ#WkaUlqh%n%_%x8Fv_V+z+% zZBtES({-uqGCug{sW{A;F@X&uti}bK22&~^7YzSlXg^tO0Hyx6ZQJI)G1E^rm;|+? zd&m}atc&~ZBTQJ@p|kGM&!H%h5H_?e++agG>u6n##B=*MO4KtUy}|_S_R}F+bl>E? zaE?k{ASAFJC-)tleRO6&I9p~5haQ!Iz(rVQi0Cs%(<_lc}!`C#z0CYnB4wq zarhNXkM7Zb}nP^WR}NLz;?X^z$`!5wWjPa6BsRs7y_4G zkk@O#EgfcJUAV&5ENlXtup}n|H4xx~<Mgydz^o}U}U}KQ%b%SKB8&>b-VQ|>g zk9SD3R-?C0L;7x29UN4v(TMal?l_~-vv6_5&t~++D~q@v-OMxYNYo+CT2+cfjV~J< zeT^%*GExjA>H0TrEDez?7T0u@LU1w8Na=ll^oKFVNCV#_-JGOvoM{>3NBKF&_8dO? z_H_P6zZ(4K&rklK=+9qLgTH(_zZ(2Mmi%PQW0-wl-Q7PuvU{+car>5bIVre}V$8HL z`Vj|6BvRLPr4o%A+oV&vvzQ}+IXV)GOE=#r#>{DBs~^Xh`6CXDu8)dx%u!du;ehVB zhd{@XYc4 z9BOtMuwmy7NC1vVI2=&=?jcb6AizBXd@Pb!i$phPm9uINrNh1Tm}kHXERtATJza6A zxFVrKkA=H&MLASlO@lSche?6!igKvBj_bY}4oC`!O7$=)a3gmom1x99-R8vH z`qJl0-PQvs3VW>S&I41rLm+Nhkzx!Ezlu_w19#Sof%zVV3i0OL?SyYDL7)`{iRxk? ziQoF*%~_G2izOIFkA%0}C!usmk3$qBs++`jEWk@I(H1P`E zZ8Ak4{3G7p!2HNG^TS~p`C<1J`4<5coUnYus6_;RP{L2(QVjFy(=YV5RYo6CMjbrk z`h=Dr{`k?~kiU{L=47r3T2CpAMajCvg7BQVhOj z3rA4?^uU}ntRVXtiEq(zgRZ>zqk*Fi`8D0o8|d3vB$AtbxEP0I@M^&kN$Bvz4p!Zr z^_LeNxByGg8Bkam!;4jS3b8roH>aey1S>g!g!vQ|9-%O9=*k;I3ftbdmn9?`;5&K+ ze3yn7Mz_**B$$=K;~(D(6C1wx+jx!3`EiJbM}NocfPe7NohXopsy*fNYJn6(EFqL- zl0@fZZb$cIC(UF-9Hp!>I`p z(oyjbzNIBD>hhh>(#6cEYd@RLema1#HwX}EoP|ik#TXC_a`Pj^#)_8PKLm^4=D+Ma zPh_f$m7m+6QuXK_GX1sM?w5%X!v6Ja0D%o|x%TOkQU{JHwIG35I@vz>rY_%1VMCZ3rrN(6{z3XS5Q~Pc zEY{$hk3`8gL=-=!!n2QHgb8J!0mqFoCLr<&_clP*h+zCv6v86XI2Un7eq01&%v5?N zto+l(MpSU}`T3h$J6vGp1K}Tj`=+=@XrI2WD7yVJ)_akXalkhfeKb+j(cd3~t{CF+ z&EmMiF=W2{n#c62Q?nhO9v|3`Y?hj(YCJ?VXL+k>mGDw0vlE!B(Ncsfzij@YJAE3oE5jBCd$8l z&Ps*Dhs@vJfBTk1@o&kt1k73GtT^uG0m!iYl~xdS^v6|P(XX`g9#?TiQKg+y3<8`D zC+aX!;aaVlp6Et3Qmw|d=+P{#ZZS04$fN_(wsCE9u|dU9o;Z&+!7atn6TCGNV)tp9&$F!t4O>D@uSMBzbu%Xm&Ny3_zuyJaE-To^H zQ~H!{n$d2YcJLra8dVcBH*=qc5M9;MPBa<~2ghledk2S^xfX^EBaLxrv`D*MjLHRu zDmd=6#ZOjZU2Bl2qd)Qw|6pj9s;X*Wb@)~NuO=*F+x)SiihsC#Rr3l_1*+WEFAibh z1u7%hA~ST}Q(l4phR*4VPV^yhW-fnMIRKx8Xe!)vDnUxQgS~0y9YxY~M#wtc{^lKS z>%cqk5BbuK)Fug-3I8ZkYfJxJpr~~-?V!4-z(6KB^mKlN2#395T=)scx#3F;#K)Qx9EgnkrRe^AkN=A3Q0+c@J`n z1X{`l5oo^~@2jN`^^@axaEpKV*Mo+4fPvv)rdY-zQyJk`1?f=L1(O?cs|h!ulY8fC z&ZtjYT)Ip5Dy%c?RKk4z?$$z(Suu+k2Q)Wk&D(3VqM_C z2U)t{k97c4R$xU4V*o)ZgFPHI!n_~2Auzasho2V&`QQjeu!5-Kiz38u@j#0X$_}kT zfx?tT5t0a_#u7BlXu${z11X^B&_xle;!%ntln`Ty9!xPs8Cd+_U{;0=x;O($6&0G zu%sC`1SjAELWnA71Z`143zt9g*Blnq#K=JxLl7eeETULqh>Z{`Kd=;!m+R$n?GS^T z))1RktJOtjvzh8&WmTT%#UCIZDt2*;TO;6N4F$yRx^{P-q_33Sq3OnsCYEo>0KlKX zMlP&qxic=XpG|`wvX5&%9lcB@q(NrVvk&|D$W$XlL`0?rxu)ISHrO@TPQM6OuACd) zWW1al=Vn+x?$(~ksYHZYMHsqVZ&I z9}yu|gYh!0lmmB~0iz&9wH1!L*@APCV#6qNVz&>Y?1jJZy_eGEHBV8Ty67mO;Wi%| zM%fZ2Apdj!PE#bxYRoE92r9s|)&H_q< z@peEa4!9VzA_uhwnRl=;NOt9N0jw#0dU!3bB?KXm(%s!D^(xZc-6{1bq`SLQN!yS-H*VKEI44O*K5M%@Rh!(3Fr_l+M2ZW0~@MZ zw}wyW^qDi(E=N>n@^j~<($@6moGKqbUke+m=1@pmFV)f(KW)+0^Mr--Cv};&DA!}D z;7+WY8>azZ2Dxz>3OfPGWU@XT;ifNwY(h{jPfg4)U@9ub=E7v#{@YGtME6{%+i3QQ z40(!zjYJ*&?Q;P#Xdt_}F~Z7xer{v2Vf(~x7-cf%nEUv&Py4hFSgF{seP&n2%=3H6 zN^&bO356w;5Pi5)RAm<@+T(1L)W8!J8^+qJ=JFE|qzJs|!%tDiJUyYOBczHjO$u{L zP={~;H6>HY6gC7sI=>%Oc_<+>8?D|hBIq$F<6iK-Ez9v0X{A5OPzt6QyG-L`GpY$)wu zVl{aW%A{0uTXKV|2MP}l5M@o#BBKFanGF?XsZrsX0{h`{n#jY`6L|o2FtM7v2W3(! zpx6Wg0-8WoMwg50i9S$w?o20h%B1y7IHAml1ZsbemHsrJK(wJp-Fl=9D?5vt6x~vPMSi*yzJ=YJjLzGA2JQmd6uU! zM92`U`ltiP=;Or^!}g>neR0$gVZ9L88%ya7OKyrB!EtfX%~C2ugZ($vS{aJSa3k9x z*`RJ8Wsu#}52F@RPf;b2YVRyz9aj@4Pk5w|Y7*HI93{2ZdLygb_F=8HKE<)s2cl)$ zOfuEJJecC4s@bY9=^43g+}Cu~vV?o*I8?XQ`qiyMX}1v)e}WW^u++*A_u^(5;R}Rhp)3(}mg&GH?lVX`rDZi2_h=v)KiH$zo zhFL$b8YUxn9uI%a%B%%%xrk=g1}Ymckc86-z#CYoOkS{HryEfGib95Rafy|nP=RAn6|O-%4gaQPV#7syUGsA^QRNDWm<)euw-<1xlIjp5s7ZQO__9?(>- z!r9@*h&71>0I+F_b_wE<4HMim0Y|OXgoFAzT124b0tnMFhCeSo8B z?0NyYn&5yz-mr!+Cq}yl5x7$q97S80HgREHpqq+Dbt8c!m>*S7YVo`;m=(#0L71l= z>VjIWR;#(YbFFYw#TKu4GS^KQW1Ar>&n65IEVj)9Z zK(k9egkqP=aZFg`8Nr`pgA%Yo6%zz7VAQKd1#1}r3oYI#D@2={sn!Mha5@eQbK^*! z9&Rpo%$s0;abk7Pd0Yk)>xpq8gk%kebw5$c zaUJUG2hFE#CxG)9ab1v*gMFo<2Mp$yR#riTnEzcENGeAWk9wd!?yFOKtl6L+%MJFm zPGkp0q<`P>JUePG?X$FQ#frJqrdk5BK!k)iPqlF_tIp_5-dz(F?OxU?fog~wcU~FS|OlW$*85MoICL)a1bf%8TRm#F1k)R&&)C0_Xaz*Yd0egWbw0oM} zQT8(_wi{B56BbB~6rubPY7|;`#hOe(^7XW;2TSGMKX2(W=?4VgQ%$7nX}bGz-W!5_ zC^<6_Bb$&PMnP_D1_E~5A(Z1xUSWXC0l^hYrFkZ8P2s?5ZkZZ2Apc6GaR?rRGv}y@4+-XFHY5IfkQM;zRvnRG4p|{+*bh3a8N$l*TF=|uOHe~eZ;v6L zLvUP5o_+H9mR^&Jj!p@>!k-nigJuBK2aX9^rTF><7k^3k*B)K@s!no-95nhDC!gRN z99ZPA=-;4VVSa^PJRguXE_?AN!Z+Jj$jO&x-IUSq`V5H)5~}o}2FOE}fqW>j`xxCH zePATbneEG<=-9=JzQAglG7y} {XtQ+Ebt>P_&GDGg}qX)ZP(FaVgKdT!jz7YLhz zEr1bL~;075l4y|Wn=|00d_8WNkz9o z1(1#dM<~mHROqmwWtT%iOKY10#o$<;&;@rSD~+ixrg9Fppzj%~4o3x<-l@&3;PEWR zQ`vDP4_XXh5TXdkILBlfTZ72}xT}$W?0iJrN7Y3mjfXvB?5Sn`nrf%BUW&u#OC+zD zE$}l%ny_<2MTaNbAD#va#J~*v&epSaDpi2~q*t2qKkdjHV0nGJfOH}3W}t2Y<}!$I z+N~5)PJkUAZs)be0CxKqXC6!~Hin42A(YPI-D?Yd38}p)uq-Qb3<@PLl#hd4@2uRp zifoB2C~r%qk|DImoN;LGL3o*qZ8$nsLC6JS(MB1j?xVgDuDj|~inO_8M`Y*Ii=97Z z^K)0~&Cy9O5}Mawa1Obr9&IQ^7023V)b$*BtBvax7?%{*lIOy1&plopNc~+0+R9=T91hN@(5Rl9< zos(<;a(zLDL1_f|+bmI>!C?oW#Z3lmF;=}<2gac=OjPgts%*+{Q`H)j!xw?pcCBk3 z_vZal5q<%V!}WVPCLJfGdxfxfdQRLHr_;(u@JI}vdb3dyx<7*=gh}xYZS_}%(2epA zhnp+gG_Wo%+J4P+DI=CyoZKMe3X=HFlimM5X~a$)K!~@j#iz9!>%&JQFpp zSs|b-A$>8*I&@l6)-c&pE+T8+ZD?d8HowIk+PlB4{|1FkCERX0E-UGU#+av~Ax7m# zR}+@}lH)FYKQm1bvIFX$Xf)_1s1>FGofTEzCyg_?&;QsHrWFPI$CFS}(HEtJZ<^40 zX>iC59?H1ck{;`m>j|1S2?Aar^EgEvWeQNUe8E?}u4WM}Gnbhmj>b7RM#z+G-)@ok z1tlC7PBCtrF)hw43wFoMD9mLrDNH$#X~EP-Ze~)&@bDHpkcj{D+G#NB2Ll`gx%Sqi zCV=?)Qf6%Tz7oo-vLyJAl7@wEv?M7g!%`JtfQKg?4W{?1^S(KaN5OI}H%Ph(zNyaX z-f&*jCNwh6o;-A!Ec*MGPF`8rFw-y3kdV2=pHXn^>Fb#~FNEjY1} zN>Onz? zKbUIfyS5JIPY((L4AqMWR2!BiFAuCN&CQLcB`PE!tdP_N$lOGM!5MJ zxW5HS(1r=+$IR&zWLPKGJ)77WGdb2IPhc5?OG!K1WQ~y|TFfjtjyfyh9i|LQ=zisH zvN$<3%TW3J$Re`2yvbX78W@tcLRscG7BeU&Vr~NN?_$x+z9;r^Xd?9OoS7PgUDVl~ zPmh#jDCp(Nm>bSRu_LA9H4c6;r~Pjt@dUOzp3>Yvm9=BR7q45!OQz@Yx8+m14}TqX zNad<<-+j4Kd4{B>5>{Sp%DRel_L3z)0_?C7zyt=BqNv|AF;kwxv;%Q}Vs=Hn6%n}# z+Of_C%3f^IJ&0}$X7u4PHUgp&Jrmu1RX`^sH|SX!5Q!=i&{u|pyyjD7*<+8y3bA)# z0fdgr!jzzD@_`hsDPi^wt_(KtDg@(7|F7vQ?6Dsoi@T-M29bP z-2+VR%4{fDyPi;FoJi^uvGDmiG4oA+7_>uag}cV~!;b3qJQjgN_gDR1W&OQ|6Sr}*ljH*o&s z7+4mFx)YWJ?4+L7QE2vwIzCC`>&zBAyothBS2=@qNVKMq?hhS5TyQS&9doA#i| zC0T_RrDo=&-5*WrL6~wfbkZnUoTgS8LNaJNlld%8(tfv9);p3l1RED=5qLT!L zar|6@vKJlcO1rvYt<4(*8Xio zPSx)zuld69W=Q>mi?4{OFFE_dYzj4w7-)@Da<4E)33;K zZM#kF8_N6Ep8VKo>0|l{va++ntr@4DQeYn72!XG50y-~rDQ9?L4^3O>}9aQZS;J) zr#1_|(S-u+V8Bx0&@?+R9J!W$j9X2Qa{LL&KezoG(QN;aO`}5%$@ia6MY}&NG4zt4F`4eGK@3aSJ9er=bUDQQuyZlMINr? z@f%Y~ivwPBctS9PLV+_xm=77n5Zcr1khr7wb1EXp@sjxWiY$`)+KMyWU)*n7s+H{2 z+($wVYK)QpN~5ufpi?RvFpm1=233%5wU-3EJAQW-V`wRC4V%71s)L% zZxTq1lyH}uGt9-%$ds7al>vBnxKBixK}emz_b)UJsq#6Ox-65AFEr&{)bXaacNU5B z?>NjUJ=?IiP47NOI=&uFUfC=W@MOf-_$j8VH8}oU$cWd~=Dt-(6#gSR=HdH!whRL?3CS_tZeT-0fDas4@X0{6O zl1_nhAAdY5;crm1TUjafnm(O_ufL2KV!1dsk;X5o)e%&wF&qu7L+GTA8mmwo#+~w} z#Vp(zt|Lo7*z1{)fsK4;a7*aq2$#Xc0qU3MI~+kZm43M}CMAWAi2XPBU{;kdHm8#j zra#PT>Q*;a>{}0d;Zuj9p<@8^S2M&ar*mh{sIG!zAq%=gIlw?4I?pjw;BMgdOb(%& zJ($~uhconN0GbV$zm;ic7GT+)mo0BZ9w4Xk?yvG?n}L{l)pN$rR5NMRRPaFvtD?Hl z5kl!0Oq<26R+j=yOZ28Kavc3dpq+2P{&t(pT)@7z#85vS3^&l4oDCs4gR8X5|I!dN zM_G9CuR3XKUFX5Iv^0?bzZ13q>U9CJ=E-E61Y#swWOD_&O+b?1yW7`w`3RmCLn=io zg&%x6W9AmQ>0rZ?eWZ1rrHtWBvwj8H;Z(fnZbOL^SqBvWN@q?Wu^Y<8ciuTDRdrH3)HPdAtI;Pun_! zVrm&2qUZYZjWg+x=->pJIuC1x&AT5oS^$F!(uDCPJ6H{^jfL8tT}xafuES6SL+?3q z_EZkhULbxMvT#E4TX zr)U#z66kH}@lDtmB-CRIi!x1GKu1yIASml8$z|+N)gTlCt))9iP-?Kb;W5`(-zt~e z)&f!V9r-BmGq)WU6jgC{z*`a}NRw}l@%Hqh{GKSvo`rzRAz;-)Rb$|x3z%JOMtm|u zUYc!}QM6otL5j1M!*C0cM3*~(rA-i8c2+ZMUu~>*EP+WAZZ-P7z?}C8{`JpO_6%q1 zd$Fe70fSaycpco2Ur(TKko@7G_JJJpraqvnl8|nsN_{htm4PWXma!@x!)Y*)>pj0- z)aw8Yoa2GtwOyg(slpZpQmH346o0t|+vn z3)mjx+mE%+o|s8vz)J;oE#7+Znl$bcjX{#S_ONy{fuUz!^@uv&h(h1_G>$)z38NFZ z1)%sdf*)|USR5w0j+Ri*%x<733rYy}8~o)C1v9~hGU{;~T-vYAzrl9Cw9i=Qj^zW9 zP>6N3Ja#_XP zYs?e}kqG-=GG1yiu*7LE&(-*G_*g)w;N$X}dK&L;i zgcx=#spw2RAJ&Y+(bv9(kC0N;i1N%kxr^e#6LqB7&Dbmn`U&s~D;6QjchYPJtf)^) z|E*0uv~rYM7^zt&&%Mb@hk)sdVGbX@l5(TRJ8VV^g}txCx4AOP2Z6VZSj zvZ3{m_;%s)^)!Q{p5S-mY2aA=JSMX zrR!1diHCaZ+7$z}N@49kmoByl| zT~5d7cmh?G%aN@ES*w2(w@%*j1>X5Io4&>cI8C3MoLYF<64h`Gjfox;wuu^$1#6C) z6ASv<-;fu*sG6Jl>-l-+YrsA@MpfQ9v+-_BIYHRD10HPzB6To-oYfUbzQ}uF_mbN zjS&`ya*F%Xobpgki5!I2pQA64#`u}$rwyLKf%Z3J>&7G4I_80*FN2JmF>^Kn+dmG+}k=GTp_#C+&wum^- zm&c;fmqg=ry{&%R%bO5dCM1jhE#L{-bs8V3_{GIaD4T=D4n{GbaFdA1u)7h8ZL24z zn!{T3hY-AL=F~=F9kvU0)nN;7A!1;HOgs=E)f#o6jXKm%2m{%V!RCLgnzjDiaa+1E zY1i$eFDu@XJFTNoG|^C-a<= z;91PG?ignZLzbT~(Cx(C zEh&d?9(^cUU*OqgP7{$F!F4t!+`PYGSIF+9Lng4P*E83 zoqP^Q8SsH76Hx{iP@$JQw;nG=@}lp99Ov1a;a0LMeXW4Tjc_X9Bmq(iNhV>Tat9TV z>b`S86IY207&Dx7H)M&(h;ic^9@cst(9*GWhwY1S$uPCO0c;$UEjQ| zB*KtWDfMLDv696vp{DKuhtB5u`%~%TK3nhRB}Y0FBac8jk`pe%Kv%RCfR;OLq?IyP zr?sc9u0w7;Px;$C{%5^H_rtl$)VpV#ynWuNXg{D5mTR`a|F#HxokT}7YZ9qW9aQ~y zinMA287=M9K~WaG46?aJps*kQL4+SYv;*?C zfgKBC4Uzp$EmLWTrL0Skqh#^Ao|XE@-4KkqK;JHC?QF%;48jT?;i{)V0eBjQ#!loyVA$9)g1fI+1y?@r9jd;GZ83zk46HHu0R}M<^ZYmFW=o1w=4|^jr~27G$1)?G_s&Q zvq_Hv&GBJ_XP&cNtnLbV&I0jSz`2zrK|iq9iFY5qx|Fhg<=LQ>T48!Vd?O(_=%ADr zWgmg4t6^bO0e#9~Vl>UhVx?>8soOhDscI>}UG<|_^`m+=EvwIFvAUFr%>(}Nb=>qZ zeS*Lg0O+UYgyb?868E)ZQ6;tzpDM0AL`)g~MgRR`jxn^1*T{W;^;#6j10nnC4&crt z#!G^d`4_!*lc@qExOT2lH4y@i1{%>I2015mGoUA6*Xr{E>wj{#iDs8Hxf9gy;pVi& z_(I=S6R@YXpTS`4aA$S{Gz%vNlSQ>@JGP)~F^NA|JZbe}Px6SXEYDeld*8W0E+FDV( z*Bq&;A`|{9h{~-3E(4j*2)HI#U&~$W?n%1vO?-KSE;n+RnjmE~HrA2Rv6NzTQD@N? zClD7-jIwnZ>fZQiX>lZpaRk!gw5mY>Ev1gT{U4U~oIBOUle^9BV2%<2m3-V_%_W1< ztW!-JSh0|Hi2OeU2Os|0(ZTr81 zvlM*YMTF)!;QBC1WQX_D)m12-!kr1eNEoi$qwQPa&b`$*>ONpdqv;Im79DI4fG8h4 zI2j($g7%Vb(jhX{SQPVNicyTR?E#|~+}*T`i#q}U=w=z=hl`>zflbHU1-22Y*5*q= znLO-J`V;_F=_BAOo9}d643VyvLWr7{kh}0mwT{w-k@86LiDyX!-p0XBb!{4C=#6b%h;PH;}yPWf>GGD9m3Fa>fg<#19iW2dM)`cCAxI z15cI@+4{%_XFd|a8=u}c^3=kp5&htETC05#V#>WE-GHQ5Hhtzn2Q3)?J@(DV=%faQ zkWPTLRpaC9A=wIr^WWscye(^G2FFgJdpyfK;-~>1-Ad&%Ya1Akaxuiygf_7NKR7?s zX1zUn5qQ(Ohq)kU;o-8jVxoP5|MnNq46r&{Sx$tb1^k&J$&wvbqA@FTP7N5>(D4^) zgZ@ldjGStVhK~P++}!>Gev)Q}WdEnyWLTT~^eqwJpAZXM61|Y;NHjT_QIruAQ7Od( zyRJen7$-w+Rc54=MM7wnmzAulP`X^-S55%8CifDIm4T1wEv!i%Bw@xA!ey9Bv9h3+ zw6La3kn0OU%SwJY)Mt(pH$8RJ=hP(V}|4ObMcA$XM?T!_GN=i z#_j%6?(lm9&MDG z2Y>{9TAgI3P}Xa)3R=~wIj85l)SM?0$QGI?6oh%Q{VdeLQL|ufL~1qXaq{n?wLh|g zF3=Fo47MW47}bs*#6P(U=q@Wba<6U;$8d8ibmZF+L7OMs* zc5yn!w|pdxsXYD?(mJ?Y8Q_>lZNmMjkE*g2irU3hwkXM+yQrw;e%zxcqEh=uocPQG z!XXlE!h%6OYqQAoSdtM4Bob+-2M_%$$R=0}QX{Kn#f7ce5JE`)S1O&RB}MLNH`vtj zaV5Q^)>pa*9DjnBjd;FTo&j*}ErMc#8IPLNiU#M19SLGI*u@gl4`A?kA|18-3MSAn zQ>v3ziz=YdIe^tGjF_Om)_q3n3Mf^jNi=& z00E!li~H^fYR_Eab!xN#yi5sfP3-Ii}^7k4XX2^CNGp)`;Cy1Ti%D=&?oOqp2 zTNGN=I!{Lr@Eyi7tr1uAyy{;rCR$u{Lcfkh-3i8 z4y}!)*{uVp5O&h`igN89G5}?xS&8R>MU4BINIObcE*!W@L`r?vs@#xo?w_C>#jO&) z4$n!hm-9UuO*#c9VCJsCS?pe>7AkoL4fVsL|N&^8!-i7>$_{ z`JtWOU>>KBgr7A>%*$U?q&6ei=*8Il}CwOF-(lDKAm@q zXA2B__F0EQih7kwB#HN{^bg&TO&yrdVxoB{?N7De9VoyCNmQyDx1w5&mtp^)NJ{B< z^5&bp#8+Qsu{G_~wsvh|O7F&?>oTc1tHeLumED1C(ISJ*i4ckTur4uHt3K7e#j8`< z7kukf60Uaz+=?ZKoBG?|L<`401B zN3{S1lq6s!Y@Ni|Q^@DfPEHJ9iQ!~q2pU1c*h;?R%VT!0YQ{mtbjcWn_Jh{0IKPVE ze;ha53v-1>4Gt|2u;MyS-CoVCUJMMo2)XZKD&8XnCg~G7T0Frc^M!VbC|x7tt0GFI1fb;lg&+Q$k%4PZ@-|-u%U(CC z;G%U~LLm{s@vp&xh4Q7`Yb_v9XantH+3>&4#ePe6ow}r|S9BCzrp^64=((e~+LDik zZ0e~pCYMQQPgVb$X|i#%AP~!i8Hp^|!bXo>=*2PEsKAYnTqeGCicSZrwRiczUO`)2 z(H*8^C?uCSztZgEf$@|&ke`$iK|`!&SfS_Gt`J3Nwc$WBoTlX)m9xViRzL-tGDQZ~ z9ioZul8qZU@{c=fTaC{r8xYS{C7eH~dd$gS+mIiWZZ+!|Hk!#(vaP`5jYYgvxkYe- z3{ThOmoQ$P<3$Pa4cD%|d@c86RX>uXDQUKpUy|1cL4Y@$f))n}RE97uE z!>lDp&fr^%7q%O_2y_7kIb7X0V~*H&xs_AyAih-Q(yl_)>b>kno)@9mUy+4F=8Lw1 z6{rZw9V9i$i$>*lT_c%|cnO#FNsb$@NkXyrdRY?bf$f-GLBf-EhrU`>7}TO8L3vT^ zbA@sFbT}Ec($8^_o++Z#^#twgg`;d_MLGW zJ$&-+$(9^d&G+0nF7;DFF|83ZW6zVm77Yp|u4q z#Ppx>;N3D5pJl%TiNT4kedOrkFw1W3*4Vhp;}1Bx9?`z#2%DrO+t6_E9QXr=>w^)J z-6oIQs9F6x!H)aD1K$=&=6B7_7goz}0d}0f;9!uXzW%lucAD+Q4j5Rdhq+_JN;5=-R&EO{agvusKlX1@R zK9lmu(BwjIs2=}>+!v}jt%WM%8Sr9;sP3*k6#2vqH7NozJ`k-W4K-QzP|`Hgpq1OI zjC`=i=Y!F=j=k?}SuZLgJJmwU`*$a0XzZv&y{II~u(Zk))GW^EBR4^bZL0=DtNwHh zm&~Dv_-v-Mg#kACY|RJUJOm0fg<8Orf=EiY>_hwJZ!o{nHWi4ZL%aHs^LM7f_9=Gk zre{Ds)Ue@JX6Yn72^&zK&H>g*OU(G~Y`unUdyS4`&jjaKtChuyVLh%2CR%SNr`1!! zeLg~9x~($P@0yk+N?F2oM(Gs#l08wsXtw<+zPpsrZs`4z08c#H3$ijAR1!v&_5zrv zDI?PkTBajaHF3j9yYmD2gR3^-TxuFKPb9s7NK8=J&}CdV&lDdMaZ(g#DX0dsbo_xd zAA(R8YPD0cxbU@C|4Wike^-Lw1)rK#T}kU-PNvgC1-7|&Sj~5qwP0NmR42Cm(qS%B zYzUI%WN^{R)t36d;|@L&TtGf3KxagRtN@#)-C1;dHxEM4`DMMB}77r+(@UWdn3=PGgPU z5%8(dR5Ug@CC>|z|3Q#x*_zV-3k!!&KPb~lD$2bS8k=)p3f!zB8G~hs1*MKGcg=QI zM+0O=%t^J>f0k^{6$if*jjcEl?N{MpoBQ9uWbj2I3M@gc2#!DoA12a&@lY6Ez2H^6*|y7y!^;( z-nv84-cXWzNQuk!(lV+I^$FeMkcM{2Ld;ZQ3jvkTX9rv)l+_TZ+zh&wp8O@=773Ii zJ`=SQ%mrGh<#x zre^7YA5pIdBefo3bt*KTN1n!u7ZIL4Epli?Mky{(Y9)?qNRB?De4=d{Ima9W#VNksjQE4^ypH+G2 zf27cdI635M*5-;n6|{Bw*Lm^?T0DBlnQo^rtzHopJJ8KM=q0Ieg5O74z_TYa{8=~$ z3rPq|daO?fjF~Y%e|=U`#YV{VLIGPrznyH;mC3+EFCzuFK7LxTNW+b}FiBUzW3)GO ztms;po}fG;0Z^>?>K`fM$R0!E%7#bzq|$OcD&o;0eM>g0+qMm$n37%*C#8Jqms-FJ zI$Qdkt#HLvuKQG#;-t;6=QnEJ13FRv#>+NZJ~AQmZB3dc7heDWd~XSedLDq6R6C6SzSZ z?gh(yrCZFdIb-V?=QEAP!DMj^#118Q!FGJ1(@XJry?7tf{tJ~40ZY{r?Wk=JdMy@3 zAPA8_lrrc+*tpx|PM)Y3YdHhmeMvnZVR&zaR6!_tij;o~t!coz3Y#g+-G4ca805lk zW1Eb4wYLV)DJZtr6P}%FAareUGBvqRjg?y6a+bJ6o)uhP6+994@X$nkvO*lImft0A z?HgIinDrz|u3`)ZBV;ZPUSqa(YfS)mv%G<(RzHgB#|v<}LV zIkaLALP?qDNC@pzkze8&@T=8E zPB*R`=~c8+x5-+Mv`^;ZQM|S(b)x1_Fwbeb^Q-Wv@A?9PF9!qNvMb=6hk9Ug%$aO5 zMA%757`L{_nQv|xwRVvHYXG)k)>-rhKrH;KM;fdH(c9AlX{JfzKwIEwQ3up&!4(<6;~P8rRc|Dm~eeA7Hh&2rf4s$_(} z(1QEgbAhK9I4g3==e%7vz*&Ex7Qfz=d>cU9DBV8eN!>Z{NR&oXA)~TPnjR&DtTqTt}A>YjQ%+A3v`K_?xv>2TQNwQ<9uyYd`06R{M5N*s} zl?6N8qD5()IH;U84cwh#33-++Kq$N|8KT^PbIo1>mUpZRjFJi{f^C8aQ|e>07RQS4 z!{&d+%lCC~vd)%vmhci6ymE5GYNwpOrceZ~6BZ)G{+>JVm44s{rXYT>ljPaxOA2AI zZyHxrZ#SjaBpCV@zGl0Me~r)zP>%N4zp|qMUM?~4@=P59i2|Mxc;#cV2<*rQrNXC- zh#o)1Ft?BJCdU^)*^Y6&PZ4|j^;P+XXYT@yjB8`GF>ajSPT9hpDO9ko2T$PR>8H)$ zf_%e24tkObMva_@Z_|@I^wbR2;^|*Re~piQZc+g~9h+Z!p)T-n#VM)3^t#!hQ`ia8 zPy`$@4QOcT;cg6WkDmPJD5T6sgEI;4rv=iWpbE-QatJR{(O>;ra+#gjLaVZ18}9l_ zeK7v5KV7BK7!B-pw3J8fu)opy#WY$DcH+~Y5sV|&$wz;J27*R6Veuwou~OPjlz&csaRjF{x zB>5jA?r?HEczk+%DW~Fw188|L(7~4}rfqh_wSf-Dy|DSQ@QqM?DmWOUC;1^Y45fA; zy=euCPy*XE!nUv;;Lx(CIWTRlh_q2lkAmc7I-&v6Q3Q}iFses^Q;Y2=B}_nZUefaT z4!Ch{xJ_PD*1?~UM)8T?Q8OF-iNrJhZe`)UBaAy3?=zYRHuS3NBK)1{979_f9Z+4X zRb?{T7ZQ8Us~Pw=npK#53R=*3e(m-RN;I&^1MAYB!`ZYybi`vACVc;33o2aq z7jTDR3IKb6Hl=pPxCR-tfn;U~e&+XePB`PRf#uG^yXviQN@x_0;lW2Ebi3j+KJiDy zVH84;H{jtUH8srZX2m@gTB$-ld_J}&r8lF?o(epQ0pJJ2o@-Q*1uiO9RK$&z-Ii1j zpxhgOZvhXmW<)S(=MY3m^D>FH+9u*h8n8Kc9CCZPf@3USnDCSMBM)$hnZ)#s<~Lt% zWdhA_M?gfrNt3*Ive=3}zNFSk*|MIBNHSIHjDo5w#5zO5NTkw0eD%dz4O?-E83-t( z_E%eo1SXaN>i$+23cPW)?(^#{|GGkD3;J)?)x|eKq1CL7 zDtJigBe}%cdMP!-SeX~T1|)zK$3d>>#JQkj0lo2 z5?~LY86`S+YZ19dN0FW1Owmzl8Zr(3RX@E+2^9n>ejp9NLcd0JM`-_^S=WGd2mF7Q zwLy<}=LM=iZ&CY`tP=YO2+>*6^ehhQF7NYhJhQk_!1ej1TwM)b9~hI(gPd3d&;|lV zgP5N=1RS!|60A*o3c<%ONG5;68zrO;BV+z$@-RBSM%-qTH{Q&zz0R9^o%YA!!2ffY zMjK=T%8`_#b_VDB+yYp1EwuwkDsv(Rvgb|+Ex?4U5q6%GK%mE5H41TCmpdRrxK+6= zWEu!T!+gjoO-hpz*X$XQY5;{Cqk$9oUZ?V#XP2BTZ>|Vx>g`?c0!Q)JUCMOXvY?QY z{qB)7cB_m1g}s;N*{8o7K^cP~P^ZGv+T~@%6DMk3p*y6TBPdMXNH&e~%-`)2U?^Gy zC62AgIb(utP8C<7+0}X>*Ln0}wDIJGr)J|GQph2{8iO4`b(K~fH62$#K;=LIov60e zk3hjrp#H+4`f`-+S09IW*3nCk0MLYm6+(eu5G!3bJHDK|sLh<^KvfIRCS zf4pl;`(Vd#K5#R7I4s%Eex>e%7{Gg79DuS=MB4Bh6n&f^CSS3@ z=~oq$3J<8{i9Ut{7;hmGnXxB`kUjA(PFT&~J6Izrn>c`po^dMcrx#A=80HSg#s^2& z%$En!t={Dd%LJ zeYPxdO~sTnHdO33rCJ0}kVxnjfN27Og+Ov|+z?nq*g{Cj=C7u{_ic#SAzow(3R4@o zS@W=DMJYfk+xWa{M+E57_UICpHb{!;YeS-aly9&stti6%ddOs!;4)yy3!Doea;OTK z+dS3(lGL;OX0}Rdz7dvQD}vqepP;0W?ktSjdxB^voGi4JM21J8 zwWt65mQ%y63Q@am^9pyCH+ylNJF9gO;ixz|y&<+BmS1WUDCR3uAR2kes zZh!zgHWD`P&|fC~f{kU1F4gc;xCliXkvB&1c-3CMK;{Gu#pueZf3+i?RmRt|;Zpiw zdE_k;_Qda+Q!O%wr{9Hp@UcKQl;kS9#G0kV>~PkLfklivtsZF%q&)Z3^GDqN>#rb zSR@EYk1Q$3gYBTvD(pKe*!@{Rf(Z!iBEMSX`tA~^u&mpJFu#1Fk^{HpP_ALLJe)}^ z$cDd~98(I0UNiJd z7g7Hod}Q|o!jvOlMDMaE4`HLN2+ z?@}5Tk*VxZPln>3%DHmsJvR1=q~~`LbysqMjn%O-#Q^PNzry>A7cH|}`x%G(;Z#4Q z6dq*=Arg4S?NidM`oTrG*E^T^k7K2bCMU_HAiqXjR#8F(lfTCk74m_i4T#$!c&1^j zyC>jhSN=WCSF9aU`1JiRnTp${hRv|^%X-aS#U=&tHlVt{1o|!KJGAm5o)>|`!Q5ST z9{;A}a@zwy-gueEI4NcV1}ULw%d`%%W7=F~J_%#&C8pLn9%&PuFFG(bqP5J1359Cf zou_fd&iYz>KkvqaKOG0_31 z5QyRY0dm?u$84A>bVG*Z(I&;T?7hHdOG%e2OP+;M$qskUr)ZlR_5i7ZxsC7F=-}in>9;;Wy_KE9JpL)80WCgjQ`VeAi`9_UobbtFgMm$rdQI+jau zO^cDY2jNgLwgoiEt^J=AQ9L? zYUP3Mpc#z*;EnT`FeOk8gu%99Ko*5if1%KPDCs0AzBj%rs*1}>ne>8xk=i5 zi_K5r05@gC*0YoYBBQ-UXxyE2V80>CDj5a$qU6LDiSfNjzCk*5%g>}KT4QfA<~F?G z(uFXVJ#Yc~1)+2PM?kVU50?Y*GcwftVh729u;HF~pyvf~-sggV`XORE@h21Jwv04H z0cGx=3<$Y>gIJQS7|x8IJnE=P#?%1uH4L2rtl6CbT#D0Jrx^+$}oFjt1hdqi`hu4*ro2 zsjpT=FLiDQ3hIiQj?`X@mGv4-(uv>>r`09bc&-gJ)R1J}@J!c=gr0(Bc|^7%M$=ZL zs+mxG@{m!wd`K{py+Bq6BvU*Va2Zq6YGahs)>1z$9Er5Yw5@;P&wgLMK!PtdODs0; zN0n|FW7yDK^4#oqC*k!?@I$~V#1?y~jZCdZFKXt9p4#I5QD35EnU2L%)WQgB-FJV@ zi~Iz+xL)2J!82vYwd~lsI*~$t&gqur)b@rm)CztBaMC6XLLaWqBdrBN9_X|!6mtai zBA#5S6;ul5wSm*wJ!u&f&7(%#5({EwL2F3RaGB|X9t1{%I(n;<&ow;!7o?;9=f z2`P%G@hPjlMJu8-CE4O&G$=|oQ&GWb-JCRgUJtytGRkX^gd3lu%1{VYB_KDBr;bp> z;1}>Xvrn0OgzNgQDkPrEY?x?J7I{pI>!tjA@Aw|L@*q{NV_0~u=Q(MFy48y=rNn4` z{t1Ri>*#Q1^pxuHcXVtp>lzp!yq#(WOuy^*sY}YAw^G+z_G1CqA`p8S4c47 zKnYQR{efqz?ou0ULGcP@J?kP;Aje}N29&jE9aBu#5V!7cwXgNSD!A#{w)|vRoky5D zR+zJkghQDdEO@OFzd(`@#8dgghxYX-#!zkH?H66RtmnGSReR1&|=u<3{6#BCYByTfF!gj>+PH(P)ti5 z6cE#03mL)yE6|x_eSk-qt&94#UU0(j7xcwlYsiII-6c_U&V{!B zVW69pr_71r7lMS&%e^544q$BO2qlf+C-aJiV4=sJLA0gWX6;RxDX^aHV|jJKJQy|o zF4v1(CV>2t(~MbpOstk2HK}wmy$P=E@O0K&hm{8g+FO7j=)!XfEc8TY>f0*eJS68( z%e{eDk{^r~UdRHFZsHRMEC<^2h&Qmg?J+B0jXDLDQea7BWdr= zNL_Nq2)e_lki8$!l^Wwl%WehSL=#A$BnqdPz8ygXBop#U$tOC6tjs0io7HFy55M@W z6EbTs9<#I#a8zj3u@~ABfg-_Ie?yt))OodJG}5OxlE=z-G1&$YBqm%2rzudMh!yfu zs98KD!~eK(Uh3*U1nbp2Ms(B*aRgV+-A1DnR03>M9xGaNS8W^9#yrC+H1g<*2)neI zi?~YP^KbIu{ubmHg%%O1>i+>Vvo*OHv7+99LR_6$HFI^8@Yjaa zw4z6vN%S||SEUl{L8&RpT74K15k?)(dfIHW-eZ6Qpuuo;Ra@-+Mv{s;OWc4*{!nikQhnI-5c8i3)f1 zev>Bvj&zq&foxeEE+{+WEQfe}2{vA08@#w`Tn$K1Zu7h`6b5zIdY?x_1<1b@k~k zn~MehMkysNg0Gyf$KzPCG|YA!s)Q)O1-jxrmzQY!@>H3foqj~;B;84on{0%)LS@!5 zDdHR9Tapg#Em}XUEHTO~CC8{n!Af(7(eA-?7J$*L08TG5pq$hGBSIcm`C#d8VBw#Y z+Casunmj#z*WwfkG*rhkZX#bOxvgc$AOf6{J2Ze)2u5u`TcLlMKRm-E6^@nRhp$2A zs5f<`9GG4_EhKYJJ`~^ATD*)+?$u(tGw7Uk80LXg$0Lmed-`#bC|_Xp@bvZVIcE%1!+0^~%we;O>=`!y5_ibH^JvVf zf|`b<9X^9`l~sPfmvHa3YB0z$+jd(^B4tWR%5McQ1sMh1HXd9;1j-G;>tlnJOS=5P zHLzi)>bkC54dHWkC&U?xuwfUgMXg}t_hO6>#4a;w+9^lIQ;-A0iMcJ%M1_Rd=@dik0#%$`Y#@u_oK7!}W5Q=jT& zRH!*MRcflM9+1qGW;Mk;B~t}77K@!?u~;k?i^Zx9mbY@b7>x9qgy z0cwlaTX8Wug3=~Mev(xgWJM(@`L~C{_6uwhafXl*l7|0?A|l9g)i7&-8%m*A>~v%5 z0kVrLbGnqb_bP`_`fy`DL>fX`K%I_clSxgJnq0CFLc*+AcNE)!O(Ipfx>Zy?RU^}z zWqMEVE~Yn>T1_>*r?)BxE_6Db?oz}m7K@!?wOY*;Ya1RpMQp9HSd5V8}s(N~{ zLgIufUR;u3sH>28OK<72!&c*JsT!(ws(sy`XlhWX!iKRHtF~5Q)M~X;PE5|^k9Uj+ z8&34;tUYMMFvt;#5DsTfJ&*>b556|Bxk`~g3g&US23_*tmSmGMr)|A zV;cE2;Q!PA`6iVQoEkS+NvG54P1vS$mXwr~l$7Mea;F(;w#{nw0M}Yib)}kcBryy} z+eoFPC2L(Jqyo=y0d5#)Gbt&_B$H%PO_COtY2?GYmuqH#8D`9uq>syta~(h?d*Ud? zMKj{M^2hW6azCJe2rwaX*rJM{mYgC7BS%Ys^pM39J=msbr6D97(LYI2Lql*fG=w8l zY$-r~WDuiWfrSmN1_0p)BB&sWf(1wtCS#W+id0QMv*tCEMWNdCe(7RFWrUELSFKHB zdq_!}_=WlzEWk~YKpr-z^X32u06{Pa8%kF!h5J@Xu?h}@l5+{73A1Vq>cmAF+nZ{* zxl$+uNNocHGf9fob`QpY1s6FS_bxAm5m?LFRT3LYmkl`;)5`~Z6wCO%cFLU$T=c$SQ#@{}n{INfSJ%7WdAvl1vj$TzZhghZPP=FwtqE zQze8JKoxGZ;P%0q8#%qF_sR#;S)Nv2unjMxBuAuy)7_k|CR$AthX-Y5HBpcA6YTY4 zn)20SWpe!WdYxV`@Z~K}U$nvkz2w)goSF8uGfRyT|>#%Sa1(%qs`wYadXR!p#+%8Qf}QZF3L7K1|J1f#ym zqu)%yWnzqxV~jD%;KCSVjG$FLp(2BwEo{rAQJVaP$A-2GWstcod<9@o-u$qDld0ft zO<2Bh=m;q|ndtQXaBAFmVdw}uo9%wciXRemp}WMOMMXjYZt}(o;g6J6#*l*2E16W>Aj?XV50tV!Q#tG%|mBG&rPXk_~ji zjad233=Uyt#^M!47EvbVM|70z_7`9Z*)mVKySt6HZM#7VEw*hUUH%v!k~tu|@!1Pe zRG?)1f!U2)Sot#0pY8_Yk-#H?ivu@Sto&in<;&GwpZR2XN(cC!(jntUJfM}RE&J%g@76mEK-D&Oe*pLqSqT`IZm z66v~Wy53*Lf6h5gbHfMr3eBk(v*D1i=XsK-|Dz2E}+#2lz9k z!~RU=kn!Nx<7J4Jj4zdA=3{(4q7qy5AZyKhv1uqZ&1rf{O(q(2U5L-guH!KPhL&uM zXo->+5{4)UKyCtVBwFI+?x5ZJ9ShC+MAS$+-9|K8tquR(G?zGt-V^kUh3f?~YN* zrV!Ky+aH}s668j*1v`gXIMq+5`u;5ZuwlnBsDt~| zr@k7B)aO1>N9wufz8d=d1+d@08w_qQF8H7R^kLw|g?hgXM43!E;B-&liwh?VytwdT zz#sLsz~F~-b1>L1#2NjZm%|3=pG@5T2gDWnC8ZgHdPkOD3oCEvF7W$z)75q_1U8P} z8#F*xg(z06Km8kQBO{A}hVEu*ZtkB&L0F;384*FZAEqf3clRAhgfNT#4RxaLNFpTm zBcx|Xk{)Jxk57!qjwG=kA}xe%ZZJ*zxKB?{k{~_|#@oYSysjWM2{tn53{LNEZ_0yI z>nQ=zCdL@0Q#869hYWCe#y8nkTPqS1&2#=Q;0hboT#ntL?I?Hgi`_kE*VNRY-QB&% z^2d(B91OC5k5BJz?=Fajc5Wmpf$YM#kw^(9x3&%QFwabgpc{Z2nMlHiD_!PhcSJ~@ z3@wTA1YsW@Z0JPT{fJkDm=$^wAHgiXHajLI8-i?1fW;M}C74`(20Fs51u!cTF@a>p zBxZ%)w97-bk0~2|#5UUwnrw;FB8~d|ZUlr#1C=DwD2g{GK28Fpm=<;zt|_vtslgpde<+n015@v-)!%{+@W}3imQ6(GoUXp0T@QMB;Au zFRWL|Jc>fBg3CzlKu&~th};HpA*^_N5}7>NQDDQ`bNO{c=E-Q9i4ri_`}DW#TK)7{;D zddHQXNW`L55H52iBpfBdh9FjQ#@J?y6UaQ8G9Yd^fi9~cZ9%)?lx@Lt!--_Y;jB#- zj1n$4&5#*0d#d?Q?9Xd9c18G+$z&5JC`cA0tI3dXYBCuF!vTK4Vd2OV)E01=MhAJa zj_0&Na)>zN4;#v$pr9ZaWU`>3pcxbtBwt1#Y$#cRpVcDn1j+I1f2rsHTDCtk>L;F#3Z?ZAd{HUrmY zC$D-QsMW5tR2NzcIBaLX1D~#pz)wh=%;m7xYzT@UkWNEdz`7bZEwm8C5_m8q2|^q( zfl>ttl>FcZSNNDh30KVY^{A14FB6EZuI1~rbXs!5LP^$&tsb)F?A|gO3#B8Dnw@W6 zP?95O3!ZqyS7gSc2?N{2a9Lo69xqu;N`XC@;)e0bli8J0%AcNtS#k*xD5onwr#rEQ z#70PY5`3#j+w2tar!>VgtyVmiew?;Jh5M-nr6ST|LyHQgy9%ckzuOw9=l>co zdcC1;Rqk_A987ojIlY;=*~;FNx!KtwGpGE&6;59JvW5D{KHdC^!aKM7*{5w-zWZ6c z6)%EZ{lcp9g|=V(0Ws?oB#)fDlvnwUrfjH8R`KLLHIKHj-+!Z1LfmIP+3M%P!~N3L zUDvM^j?UI-DdiZX(uS0hhm=xE*^&p-w3JfH5VTH;w-anp1~pL=HCdL}YNW)PoO*y! zl2MY8l97_p6C))C<26b$N-|0^dSbL>^u#EN(UOspQ4^ykMoC6VMof&F7$s35Kde{s zUxZxHvi25!@9Pq{?p&4hrnW@-N8)~8^YNF;8w$AQ4=j@(yhJL3zZEnu3%2Wi{ zRX-WQ`t>Juz`WJvy7Sw>sm>nrQxfE!ybeUk9#3Cg32l0g!4tDQqo8C%se{Xpk~)Yq zl#axZU-n%hVez=9UQOGTFYJWu7h0Lh7uGxfv#M$`)mS6Ua$SOl93E!dR2$=OGcRu{ zM~oO>^-N!L%ycj^Z+aGwucsQomxaYUJ&RXaHp;EGZwXuFPutwwpk9Z=U8hoQbqNrq zl(ry2eQqS;L3ZWN>&f*W%#>0p03*9KCjcP}L#hF)_lU!tY5@JN`=S7TSkwz3i2Y5b z7@&F>7`!xxLHBSlYxTaZ9*3VmJ_|&a{!I->U_b=XrcX3Kt zG4Ef4Suo!J2DASCUtm@W@2d~H?n8UIj~v#^`E&1{oGI~7A?2MhT;mH?+DA7-SONoRG^N$cu|NhJ<9rE_IRhJNi0Y^YkAVQB^> z8JHE?+LdqJ3C9B)+MY{ONzSBMqvF;5>h9MIr(d?a!G`?`x?!CC?e6x5^{e|O!-5DR zq@aNfEk^?@rIb7(QVz31PKb+D3ZI{!Ag~}Xn0zVuC~@6$&TY=gb^j*2+tl8jkqzSv z2XoJ&_;=mSqHs!^4@?m1Y&(kDlkT!pJBoI8SVgcMMXw!2up!nzI#KkxG1Y(_ML??b zlx)I2?AuW^iz0W-qA*L&q7=P~e9j$3uOa|fK&Zc15s+8W4%IEQW=pOj z5A2vpm5xh#1nM2v-QC>Y%-p;Khmz-VED4h!e`M|@M~~EVACIQt?Tevl_;p$ncGKJw zPX=*f#C5~0o7+KP48nE*!jPpF=eZ6-v{>k|5{TTvVH$oJOa|6a9=RP8%N3PDfIh>tWHa(6-K0|cZOC{r)GKyP~Qe3kn?`$jVTwL zdteL(VNiZ73|Ur79vjg{wg3jk#f4L;+e3Iq)>9u68~P1vz0;Fd`tVCwGYn=swpt8h zkYo6GFc<~9N&_-U&+P6w-RJD?IlYlujCek%d~i9vyS=-x)Q3`c zcbBaiQFSi0O>EVq_j$i8Q?1e}j5bN-3p~5RMRz zjQX66vOYrBB<~ufLS|m*fhI9n!a$VEqBMkbBe+t_+chSH}4z4Pc&N-1Sb`uLHMwtWdbPVd_gsG&sr#HqThLre=M=j0 zsggV2gmmXe1790w^y$tR(GY?z-N2>bjW??k$bc$z+yazr*qOet-tlAL%h@LVt>rL^nUw9Uzrt!{fVnM@rwy<0Z7 zh%rWvV~jDz7-Nhv#u#IaF^L*Pv4Rz#PN-9#O14 zVX0whX;HbVs;sJ0!B`h}sWK|7>Qpdx)m=G%`s`|!aNie1|~+4 z^-rd5VtCUrF({w_L(ch7*ym30bgo#uc+jW+4QJX zGb8{21@jdE02mAi1w;aoP%I5JVNo9x01jb*JZ?@p8A>COD2IU*1u+oAFo+?>0APqQ z1VM$Uwg46vGj+94Fquye09gm=2Lk&8K(d!DDs8rbvrfn>$07d!#Uy3RDd7LBt`E2GUjn#zcnNlBt)QU= z^j1TGw~|9Q{fBN|#o=!`M(f6R&7ruVc*&~rhmmaa&hRBy{$awc;SoJ>f`)Zh=N}RK z8IWfed?+=t_iln2)(g2}`Uvaoy57T6OMjT$B0kb%$o(SjlY>`%o<4s#J%oZaWhRN7 z-84lm^WxN-CvMLu^upV_Oe0fSE|Xk8${(xXx6JYdk}0xCE}ui-qBO`|wNGwDiuJ%7 z^e-vM5HuPPG|l6iGpKI@I3mO>ZypZ3u)n}`10~Nn3L}2dS_r;;v z8P`(aECtqUnt)1YdbyI|H?54X;gU+m$4vT&D?PkV0(%K~jxdi91A9lJl=cIxP%(^9 zNMO?!z`9PMfho6XwmM*kx3w3f^npK^O2gBPHVHB>>iCs$cn?z~RZdEusJ5k%KD@Ur6Ab`X`y8?i$q^P#WOjt^s5%XlPY-kA zX?)D61Mu7m-C-nHZ6)dlqfh^KBr-A*%f@Ruc#^99ZC~6!wLyw86GuEG`-!oJNi<~{ zfD2n3AXOgmtU+;@6`@Pkr_h4R0CEcv_D#I9%@Y(7CX^+$scxBr)9Fm?0blLy z%awL5jx6i;NK}chtlFwx&yPYX+{u{kDHEl5CT(=azpT#Wt7gA*>rIUSe2~M}FmCLI z)6_@BAIyFe;~$2kXw2t(-hH)Q_z5~JVEG=+!IlyJy_~5$aNzj2@U3b6(SfxI2xpC6 zO!SN}iCaxP!f^{C_)=Gr;O-r%uZxxw>{)>?ef7#2X_)ajYeh-EJZ`N^&>Qg;Uvave z`j{#4jw3K2bR)5W;p)p4ka)aUy`Pyynb;2yPSvnX0PI@a_EcdICslF!P}C;sgLNM7 zD4}I5x@TS(khd!@FH#ETqcTm)uC~;{^Iki?y6ONAi6-iU!keG)P+!l)f_p{x_~Hhl z4?dRVf`Ls{A4ZCwJBAn_w`di)9ZG=1Iw@k~6zZXWQT#n}8&@k1BHL|B0*mZYvZ^yb zGd(g4B$nr8>etQny{v$>Nn|g$(&htO1i?12eOF}l?Q9hUiTNtkM|=+@NrLST2twYZ zCQ#qC(I%*lquD7VS8Qn$qby*$rj6z()&(_u^*Eu@o^XR4eNB3pG7fDg3q5M?YVv%vdM zb~7z-;4uyb*lCU)c2{ z9q8~~v~JhMV*LJzh< z(3vL=(ABa-$H#e~Me^1~?35&&f;H8cPAH-7B{`u;@%2>0bH*R08B{`PKGtZZeDQ##iQYw9UjdilJex7aXxYG z+HRV$sY3zuug`oPs9~z*qU2(Fe&dv!Aer^Xc#obPWgPb)BvPVD7=Icgx`(P?!u!vV z6P8ZajCQ2jO6kuaXmY$%4l|cGPKA;Dc_7!BXq!FVQ*6;uL|R zmL@OMmLDq^=)fr}M;-9D5fGjj1BzJpLVN%ty^8v<$xHNNq-(B}H+mFzDpP=FF*w67(pir?9B@4UQ5NgFM4w6^ zCSUfAv6NCQ#K?KA_&1QWpRFm7_SGgS?Yk+ zsGTQKnpu}Pw$Bga<+7+wUk8~$b5+EvPbRV!B0k6Ri zeM&M?#b}c8H3|zTL3f!`TCcmO4iwwAOgOP~ZPo!SLm2Q;B6)^Ca1TOKVpJCg;tJv` zGfI=v4lpm1lU^K<1Ou|yZWY~9s{%PMvlDR}JI`xh*cJ7^@~LLeI6)ad_c?mjpG3^H z+5$!`pGMk4pvX#{5vyc@KCet&HgGZ-XqeN>;bUn0ZB-R2x$6{ zSxlh4%gF`-;U;*rX%QHMp-7xQv_+u_{zOaoB1gkvS3mnToQ*q-Y`e z5C=KcVo%WK0#-|zCo9SyHckWU&Q*_WgyoBkMg03zA;Q5A)m>ToTe*s4Y7X7x{q!e< zf`Ld8)5?O(zH(@(!-1&Gv2=!HMMAFc`+>QQPdWjU3HTaHe}a0@g4LMYQ~cMUI2$7g z1~FIFPuNXNDJNO#7MIphd;p7%CC-Yliu&z*>kvb2ZOKy++ik+gnQ=gv$dn|hdHha0 z640iIynhUd=<8HbqL9H-!ameg0OaUV~-&O_Eg#j+qIk>aF~vvyI_3lr1UAhjrxQl zs648IBB30y23$g5HzQmPg0bu-ih|j9TXL_ZNQZbNI5p||my3ig9ZWT#bL#Ok%@|Y2 zevwPwAs4M=Q#jHV%4`YhovFIN>Wl=v0%AJ*xTbb&q77)>g<}z{bpYC`kaJf@q&N9L zbmn;Q$@CYHRGe%faf z>Szixrw4@}z^kE=wMSJ&WQ9wpN6W-*GaD)nw^ZOm^2bJLJzrMh#!U3c`nU>KR+1au z8%gKh8}=GGRBHX!DpD>{h+67vgo2!-1XS_6+F{Ei9OUQMWk7-Dc~fUTt@8M*TqxRT z!9k3|*iko4R?*rK5}pE;B+kVtKf?EKqjfcIj5|xAKtriY-_m)-@LuIXDd#{)$+zY; zf()-KGgt+#T*?fHvRbQ1T{=l?Af|je$D8-q8`ll;aLw4MR1sJ6R0lor)s_8!xG^bw zW>KVtF{v*SQIFN2onul8kKG}AJ+@2=wqU&J_}a$Fbv%~kjcg1OL+O#bzZ)xg*fJ$t z63=%0EE@vRXaS#+Y?Wjr)u8YSK=QOahD+_%%?WgtMBkgD*R_DU85|3IAg$m5H6u;( zc@+}u@+`jj70)wezmdDh1@(r%#q>BkYJ1sI*9!q~fL3(zxQw|+-jgV`eV_qT(iMzY z*Kk)_c;vZxZ*(`jAgS9PXsKN)axy_iiO+7y0ZkE6mkJZTq2*{414b!8%IvnKU_}Q` zLkZr`o23r`F}2;xXc?TAvZ{l!O4!MlK@-yw&~d0X>BSIw`F z!xV+4W;~;dXK{Fg6RoOvXqafr6;~si$HOvYgRnu%Q^~C}{%qa2v*b}WLKc-Yci$J8 zmn=Epj(C<#KvU!CVj=jfbkRLNEc5O};$ubm1ZE%{e5}%f`*H_l6LASP&|{D?E*(|E zV&rwwWn^4a-Pk#BCFD_q8ZGcIzqDkBFlsKOKV8fmY}kcoW|s9P)O7T*pm1sJEqe)nBKde`BfJs8ERuMl0pd44+Les$fP zfK8bT2F!P4m>Q&eks_%+^$1g0hQ1+Tg-alq#pJlRn-1#?e%LWCZ>kosX|E1QFrX-)hwJp0X^TN}3(2;{` zQCe`mhH-}GV{w?D)NK4Q#X;+v@K7F(H@%2!OZ?@Cae5xgg2Lcq-SO$^uzh$WJGAd> zuFnwm(uC*$OfF(637>NIQG0Qoh-n(095c)^RO<_?RVd8QtV*Apk+!t|d({9@hTPT9 z_@F*#1%y~R`KanydfqBNf1z=l)%cMto|G$yYJ;PTVFvjxh8z#tb1~FvHF)`{!|D0% zjBb`Xv$v*uC=-xWO98+<03OPQK8Sq^$y8)~;Pctw#(3N^4r~dXe5kJcXFjsof z%uvlPWs&aUmkjt|ZiSDw9MPL!)w&{c=hEKDd$SUR<`oY-9pX)3MiU=~Z@i3~>31)g zldgDQ7@YQJj&0K0K7Wv^Up%-OV8_VkX04Qj$>1+bO}Htb>d)usY*p2D7ZP}M^$M^U z=|o2TE9SAd9`$6NL85n7G8`bgCP|WQO42WG5qF(I)vVm$@mp@G++xC*j;cYA4Bda( zMZ_`L)wNJYosS7p$D+P4iUIUUR#Wzmq^f^x0jB(|@RcplRc?Th?jF!;(9lmlXYsCF z)JEz|W97((+{yzDi`i(^Z6v^WC*|@uT8(bIb)c9({o+q9QZr(BJ22{^Llu~@wcH{q z{IVmkav{`aNL39uLhxlgL^DAcY%$K6@8;Bpfa^_H!Sr-_G9((89wQBAMH0}ad1>(X z%LDrhcS=59eI%1GE+J3TD`DKy+GJVW@4@3daf?&Z!hbvOnUTk<+nV4Wd8{EMMgy=r zc%YS?@+;7y<3apn##DNKcK41fw{o9as`MYiB*eC8MM#5%Ef~lANHkFv|N7O6G!3td zh5)PnrI@0j>H-l63xgs7$hTk zD+;B->=T-Y=gj(7s$9RKtV}xUv!d+Y8kk+k`$P7uVL>Lh5Ta%LvoA zTyfaRQi3x7y!S!Nh+c2W%Sy9Q&J^rjB4=E=ovAs{NCfC(s_%WSb06kyz7JIm;5a>{ zWmxbaPg0^#HldCc;LW~$my%`{ByY0810hS6l%g>?w|n$*_%D6hc?Z6mwWo@MuLB=N zf@QagGecy*#ZCR&@3zaZXm1g@UMHols{hi!GgIj)A|@0JUo@^=E(RQV>``H~*|XA; zbNDW8Jfhv3Jb~d1P?R;iD%(BLE50?lSuOpoUcX5#0_RXv;HsF}L8zu{#C&g#LO8ag zL@GHoB#AF4JkyL*AnORBbfKFbXGXSI%sn0W!Eg>@MGL^yRD(+d{j%l9$*tH*C6_MS zmxJ@A#(;}ZiPo&;$%J_uYcal8nf~nvoPrk;2M5^(=uHbxN6IfM#hcBrcHOen|;eVvq*-ZDQeI4MTRARi1(^ z=}yh+GeCBMprKhm1aGGfdd_O8IGSc=lL9a77K?$lua~j1!c1RJUBvmx`Nc8k)7!*F z(C5W(L{g^k1Q3KQRzL|fAt8|2uwu`tn&`?!Mp{60W`pan_Vg#wmY$R8MBhpza&9b7 z_J}JK=opG5zpuRY{Ip8!sgxF0d_kisobSZR0!|KG2B`Q?38+kS>1fhj%BA|Ym?YuR z*c#wlGQf9(aZ=4OUW7vbsC5o`UjRqZmp0I{k9Cjdz~HRcI$vX`#B2O!DmfYGM^g852x5Kc@ z9|cpkp_!m=h_umt<~LF3Smxbz;EX6>Jd*WW+y`NFc1krD%nJ`ndVyI( zApsj&D5Xhc^$pGj*)j5JmI^f13vtsd!IhC)*Vp6^S{NgYe!pW1O^Zn)^O%C>KX%bY zmmAaudKuI%J^*T4mo&Sg{rgtsGSQ|#g8;QJnxf25Pv{M!Qck=unS7Z^7!%+pVco(0 z%$)WLr>A~AE=XGRC5mpdNir)=DRq>*@>e~ABQN3Ho<|^KIC<(K&xW;qbmM+B zRZaHJgvsoLOel}I?bk#~gQql?5KV)3wdY6y&C+)jt z$|9AIXJE@n!tz(8q@WVXr7199qPvYTV2HXn(o8Ud+_J*HuKsZY9h(2>;1~8!k?wil z(GPGuY@k|A?_>v1TJak3CQZO7I33e*rwkt5t8zc3q(Ou!eRlOdcgib2E8i^@gOOwv zbm~@*%KQz5Iq(Ku2*+{9yP{H+s2TU+US1A!E@E$P@DUabFH>2xD%sX(DDNXo+G`<~ z!(}opuPV7BTl{Ren-;zzVp$x$hahW-Dz8ZyujTc)f#A5s5JYJKZ5$W&${Yt)IbWQ3 zJMNz;j`iyzC>C~7_a}FXR@%wknjY{ssJPPXQH5L4arb$%9Nj~6OQ9V|KdSY&vHed^6wW^A(xc76~+(!NC9L#cCII)|Si`Lmp zF*0E6cfzWi(odw*NnyJZ{2ZYKZNqCDt6)-Lgx<~bvI&OZwS#iy*(-a?b;m|N;a~ov}wy}oY-%>&krWX(6E6B5%!?UsGuJCH=ng^w70UP7aDQKv$|Nw zlI8#kvV_qs(s0V9+`Q8-GqbZ`KR&K=vQ=@7ghO=KenoMWENDDre14sorEVkyb42kdLP{}v_E)YZp9V$l zxpFEBg9sx_@@=zY+$wS5uyrKTj=G+l2gPIuy@@m&rEZEm(|P^XS3+h&HdP;*g6BL* zh&t(vo1%`_%T#O$d=#?w`LgnSchtc`5)>Iz_o!pi!b?@EHAbdFu-(YoYc{yIOEDG4 zG{;KnMN&w>n9@-iS-h*cmReTcQ-ly&J26}$jBrV&XM-G+2Ts;d(A+TvEmVc10~8G^})0nkm{NQAdu+`fLlCTOVr7Bxd0L; zF!=YfEOF^fOqV-ub}&D`TfW&BjSVVBMK zEqpe`zKvUa@i>6uF3KD}A~Z!oFpI2-uIRv(BN16hS5d4*3*CHXjkA0)aMtzr#B z#_U86az+faa6y%D?Y7ONA)1&uI3Nv;)oETWxnz>21I+}}2^ICG=#06-M+ui9Fb$3| zFki$hyXvO~v(3d&1>stQn`jL_z5L|-kk?tS({&53&@$5_3CIaG{ygrQOd=(N7nRbUnUbFgYn+NPD%|~uGg{+{l9>>|juIzj&6UUv zT$=V#)Yy`;<;hwdt%dhDSBpVWjO~Q@)7~Q`6nJrj@hXsO%XCqcHC1Fj;DV~@(5-f2 ziMTF=352F#0qLy^iW9PGqUYbIJ=CpzI?4`VpWLZfNt#@&Qq_Bxm!2QWhR~+^l1+tk zK`F_t{vlVp)9n&1pj^$m8?P6X51tA-=VTqx%!F_$u|PpG!=s>kEU`mG{s;x8%z-dZ zL&1Gv14M)-g;4rQrMb{FHuJ<9)M9lhdT}0_8x)%G%)KH=AWdqZ!|ecR^#_5>y|agtU#7_z6;p z<(}{+m&69pD&c_8~KH-ffn*)wB)JS>B;$n$OXEWp11SE+H zPLZfXNQ6){0p|7_93$aDhUmAU4G?w+mEpIc7EZpa*lv9^sPEBGToww5&k%P~eVl@S z)!&a2G5#p)0V#tqR3xiLj>I#0h(~K9S>UumVOaZ3yKIg?ZcZuQa-nFoGe(gZ20ObO zIJw`Mau^8q?C+!S9-x>h(Ih!%X%}43ZTH;>1_>kbDtGlwOU5j5H}abwVDuLEg5VnP#Qi}|A;7@!mK*XwOIOAvEl?`EmLvXe}ph^p6?Z;UAq(CJx#1%_l6w+5eM%liK9}s`Ld0dY+ z5&Naglk}!%VB_|dk&(5+u{R(oFUvb4_lR_64~09p{PqqLim-~qjL?GK*-a`OWBI0q zh!nHn2xHZ)%gRC}^XsQB5MW7$o=I&ZTP?nka89oq05sN%dI`}80`iPxfH9-1+xVo77y}viB!VTv1 zFPn(~KZ!9s(}EQYi?N9aR3Id&bXGx$hiEAv|-4e7IG(a1c+H3QTz-Zl>SrD>i$ zlLZsF@8>jdhwww%l6weE^})1eVxnBtoUbA2X$#YWcj+j5Hot#iV|G$zd|E|1esUn^ z?DOvHPJ48uL+01RRW)IOl{733tLj-zG46(u0-i!5{6B|;;&yAyT{YgcBdQEJrO;KY z>TVwrsQx99qP8m}H*0eZlAI7fg7n%k6ol{i5Cq}U~h+V04uffa>X`~+yi(m#+o zjEZScd*gMGi59v4udXa!%uJZ@?!k`B!8$)Xs!(%vDKRwp(GmaK;5ISdkq z9yQ$yWj6w39jGT8sj?Iq66o4v3UnY~pgft*$YWf*K<_N1%50l1Fy(QRt6jo#!x?rH z2sDLNI+{1Ds4$@DK((h~6W(@P!B&D=zA*;+Hsx-@=1=JF#mBV~J+oA;mo8#>KE6{Ty^6%tjS+-6A*tZi}G02Pv@9{}TuX$FGBdy;lH;_kA zK@7(Z!J;i#rl$zs5}f%iB_vg7S^L~c^_cP5{8zNqRben&PYj6eKrPiT!XRjzI+xpS zEmf&vEaa~+tBW)#kISlf8zZ$n!@O8>%Jqvw#vB_7@{bjHesf5qQ~z2 z!H2fGj>VT^cA4^!;+jy8?`tO#6$}=}kyZh#%(${DCw&|oY6UaffJiw!*YUWwJ$Q_ zx&X?Wz%M)Z-^-Z(Ef4=r(DtxDU)p({pPRf5i#cYdWfh@R8DmvG=Vf^V=4!a(GD!bu zK*8ynTxj6??4riSreIOXZDcjeg2;lt#1l7vuPbmk*`^H7^(bcF_Z)VYw2LW);B5L zpFGZwHsI`hF~xi^8M+~T%jD4*O_CI@@2byVezwFCOActN6Wr{v5@E4wX~~;H%fp3% zlr{BZV&Fnm&Hh#(edgHegvM))P8t~jitv#=$MQ2?%R5^A!YynOBSvsg&lI?1Or~_V zM$$4;b`~g&6d#2Em?KlQyZyU>I4W-^_T-k1B&UB`enbO+OlVyNLKq;>%~PFnA`(Bx z5MXgg>2cfVKnqpgf?$O)3au04Tp(9gALs|_wx&~gD&2~{uF|a)cbg|6u8&Ikm_-vr zx#MCASBUtvTUz3L?4F|!+pX11GS<>}1a8drPs6sU4eV?}mLEwj`0W=UOkL27cnYI( z!QW>QCgbuD$l+Dv4^4ml-1X9riOV72G&w$miPWaBj>Wk0E))VyZi-bZdl4B0uj;+z zipwngvEEp$Zc&ZANc};AZ-v@ceTVr?JhiTwtId{sCZi>#ov`d~ zUF=~(Qm**|wcR?1beIkOL$dbcp5Wx1He9}ls}4NaE|V}Rb>^ZgmxLL!yjEi4J*HVo zBXijP%XvEt@3I;*Z-Q4Jv;OV~^0sUHov>nq@)|2w4c63+I;}*Jyq!UrttbPI$}@}Z zuC@*SLeSc8H}$gaX4=%Er_eAoto^S5w*Au*^veU_`>79DJUeE{YRX5Qpf;sE5ZI^%w}-Ugv7Y} zpML{Oq8s8ofz2a*MFsGl)qZsfAQvrzAiFGi_%O2F#U)88;@ODE+O;<)z(X~@K_)T` zfWG7_mdCIS+`o(-+?d5$9Qxnx`P|9-y3-+o{DRWO+UxR*5KAkfJYO&lRH=_47DNH5 z>%7J%>bAJI*uMP$I!LbZbY5&$LgjY0V!(I;`!6BzaE74g%e=TV-;OF|dg1S@54Fok zM*yXNkCXEd55`m>%rJI%Q0+EKr@@dEvBdCGqF4fUS>U`~#~Klo|_3bttlx z^ftDa>(yyt7CF{p^~##4=(6`84o$=XM(-(4GnALh#zfdk=#$dZDi={{Wlu^Hiz2^W zoU1eOhx}PyxXp4+--hIS zt*kz}T^RFYW#u+Y=?vrc3Ko^mMxMtNH)f~OEIw>D9(X{~*gCcS-oHt7C98~cpN<$B!$ zN%1sL)jA~aZVoL5<&)DC{bU>~2 zdyhfgW6{C8^S6s64XdMvlxZJVpgCyz?rQgT&9WU7!_Y(ZBHm+`2+- zu@aFhbir(WCZhtx`kX+ouq@j-Y3A`X)EnjmK|`lu z;kT;-r9cU=S)u!y&BKG_HmrDPbB3LMPM0lu{3CLOq!9%F@e1zCC}Ykn5X^jAPI!t> zVZjOw0AKw5MMva?3!w_9c{d;wnLLVk?~b3Xs8hMnlwDPx!=Ud+FC@URGda5r_dO5r z*-Uh8ly+C3>E=pbQl~Mhfw4tz+yt?QQe#S{`q}`k__<9(s*B!2TUBeQU+c}`emz8V zdM(d9a$Sff-0@KSb7YFt2+E=(8o_VD3l?c$n|Ha6ll>#lh7AgikmD5*>$ofQkOX>M zduX1d%5Bg*#p8`e{KGZ_`VBg`d&eD79N7Nnw_d%)<ou}*{7to;UQ@)B(dt!z|bUy{Q3M9Or{^OeyHtj{X&B}aFo)rEZ-0657d(~f_32Q z@_G{UN?`XD#L)qum`7g)VK{6GzN+&<&`k^ak(R#%{vtRJNGL-)36jvR(f)8S%_~3F z(;VBYUO@DIg90uLUa-Kxw4mS&y<^Twlu&2L>i{7f_6GzbiSn%brePKL$%2d02((rr zB9!-f_X%Gt<0g71eFHqmc0wpoU)ts!MMl&3K;0Ewp&`e{yEI_Fb)pwX6EicN^LC{o5o5~-VQ zd>;2Cc%UC-Y@2i8&d1>BSlYZo_OHmcN-$_F=*zrvFwy!SiHpHvGCbUsSfkN)?nZ{CTnBm-l##TDM_=dF;Q@}U)Epindxe&xf=)?#L>Jdw`HRyK4-!s%^Nvo{!D0M zB@F_b;P`3Z(1{*FgeB&nbB5p6(aN(gXn^Zf2RsLKmz@&Q=!s++PI zX@yEnV5S00r@Ezu{^J271ayCM`x2*drK=F|JAo)~qNToRDz;*CQgc56Y!m@6N@8L_ z3hW?3;n_gZncoMd_sR|{6)ucvd!NYdNu7`auX7GQJHX`%7)ltxBihiY7IfTerZC)2 zM)t0rKufR|gi6Wq;YGBcmze<;h{`t- zuQ^3TJL2DOOcjK+v=|9Jfwu3ky#CEc3TyJWYiTlPO|W+X09(=yMX19fJ&b%6bsg2J zK%zD-V<|fBurI)Kct`LF_B5~z4$KZ08`>;Fku%x zk0MlD8b3kQfH*pFmkKnXCUBR>uS)_0)pdz7YYiyAJ2KidAQe{gCZzg`?{@TiIMxIFP5AU=IXzr|k_+mlvfMQI zyo>dQ#C!PGml7Juphec$#ycm!xk`+RIiT9<8WG5=w;r{n9tRqunEsZg4Cm9atBBx| z1czsZlRt+lfUKbVs& zHJe8O>Nq}tNR+bm3inOgA*I-N4Y`>du%PQ$ZA{Un~}2t@!b(` zo>11oS#T%q_L-Nps*w|h9-`6kpU@qF}SP5u?-bR98nWY-*`S>0N81qTd6t?gPG43Fnk*hqp578aSk&B z4%=qr#2>x=_nztD1n~L>_wn&f1)B{GK@#E!G!r|#CxFqnvh5XIu>$JhEiwaOQFfYb zAlz&mqpiAT!=20q2)28Nsi};yamODgM9b1K z#JXcO#M`c*UXEB`LC#UtV9doh{Q=WCKEdU>=kUqGn+Pje9D zz<#km&JI6Khs^f?<}#4CCxBxb{}%KhxP{T1N0uZh8&l+<(Q2C)up}Bgz=Ic!B&lc@ ze+0!26d0rgp|FCzl@4D8@?8pxXuoCyO*{*QgSy>(4i2H5lIy;t(VtI&UqZ0=ybC_~ zZHtu(kS_;11A}#58o=xu2$gbodJIz0k8j_|!>Ao7rFZtf`$M{WdN1Msn3+*cE^x(3<4Zn{sL9`<0@%RU>g7lf_1x3x_go=&g(k zov>W3+VA^X$V+;w4#B{t-AW2f-7c%z=N#oHrkQ0H=P zs{BTEs+frk`D%o2pwXBBl6eCQYPt@jDmz4+$<5Uz#tIAPUzy2)HV8}PmTN0xiVi=K zq>f0q+tSkCe>9SdgR0I67R505{znMnlrEp>xFG zpZUoWZ;u}z9Qji))2&AR)U`CAXyIB=Ap0fE&xmhi!(ewe7Vhv5ycyCJG3T>_47v0% zxL9Sc(0jqOz|NykE0Vrj{Jk3Bv6Z(IJTj!lUbIt$YG$a5dGW)}NypVxzX4Qc=sy?j(DHI*SpAtXzl~#r9YIkAY^#D% zor-TZ;pL^roxn|s8Kez&en)j8qqv9K&?WTQHI`P6w?4Oc2mpqB98G zqZgPDOja01C?O!PmEGsvBN2jCWO7Bxgw`GZ^K*sV8~1aYzR$ipKnkw>YciRA&KxV&kODSbaDYXW62B-$$+jy~I9|>LQSC_KVMDMG1H?*kC~a7Uy?eV{#!lAeE9eskHYt>Z^`Pv-|;P}va|Z{ zm+W{S&$0Jb{y&~$p11OEb;3W*qu~9IZ;8x%PxCFQvLo}}FG>AB$9Q=D6Lb2!86U6e zq1_hhp{`;mHsf(DM&sde%(HNf{CZh5u-(_Qud&4U@1SV2TXr-7Z-&%6UTHuLeUzMj}~OlI{}(Vk-|sp&)q@+b~U`7$@WR*Ee7 zI6Rj{mi&|lg2$1SL&xVoktP2~6Y(fmh~CUI7rGD|n%po~p)mN})AUkh9lr}Pp0Qh5vUZOn<=lJF4J{UlrypNl(% zYwQ;`l^Z`E!C$S)^o6Pj-jJ1At;+&|?JqT(vT3*2BTl8I{FE}9N`EU72Q0~S10>jF`EURB-HBxMSE_Nob zd(K1s5sn?yQ8NO|#Qkd~vN=vvKS$v7b>FIf_Z*ABN0#=XzE$lW1=CLV+%G|a>ppHE zu92St*U0C*_E|+~9~hhfv$$ml-~kH+mVT+v)57`{iq!5g#uzOVh`dz#0 z_X<5}YC*^aXF&xA@I?ngOhX90^7v`48Y8+K%rD{e0{plcMrR}-@CiJ+ugg}eM-)Yl;q&>tOytR zQEDJw7P#ng%Og#!AIZV`&s_CL=yJJ;tYGYO%#zz8+k*JCpSAIauZU{S4V!jpx_ESFn zaLzB{V4VIq?&W*~`d9FuUqAX6*3ZEyax$UEonORXInigGIWs=!xg3n|`uDjpu^}JD z7*liNFe|>~n9D>}!d&^ySV;?lv&_%TUl{@Juqvu}VPZjwyK}*Agj(jq57!?D2qIaO zmUSr+E?<#G4sMOhSESL!i7s6_CTwVpWMLqIL@GvAR7F)(MO9R3U#u54Oi`IBg%QRJ zN0yAAZQHhgxDYn2C2!b*t z0|QDarIb=Rl#>I?xdFz8JftLt+oGmsHdJw`mI@W0rJ$xfwu#VhNg6D$AOblgpAHRG z3sZTtk(KH~CL_UBR%Na1Y{;@?sZvFDvN5b!i800)qmj{!Qc4>HqW}MH4B7DiRp5jF zTxX~mg2WI5=m+TM$4MBjUX)NGDTSmIj*U{R>)>wB$6jNra{-d0W0WZJ4`Bdcxr z(EVUT>r*_)gB-|95f@wx&4y+(EJZa1gbh_nimDysM+*rA-QC^Y+&$+{kf)tQ)M&#` zV}KNRs6GD;Gc$$Zz`DD;o0+?rnVA`hrR0xvR{6D*%*>J_vnJ_e$;=!BajGcz+YGdDB0GdFj$9f1-tetW9aO{tn|TzW>fW8l^Fab#^G z6k#cjdlQL+aVH5EBuRFXa7m(4sZ>dJkZ?h=lVm4}PLZ1zqV#dX;{unBiQS1V>~K-3 zxTq`^E@niZp7mH;*dR331vIUeaZKaPFq|YM)QHsGGhWeWW>-j`agKa{%zwl|=PyU6 zpC89{{MDbw*U8~OqT?KMbiS{M^m5qwnVDNRI!QNvlfyg|f$#yHG_`%eR_WKl02FN7 zozs6#SlGX9Uq9EjJ>9l#+cwVyb8v1yjcl{O)^tsy`8#t?I_Hdma_Mv(iE(73O-<>f zbk^G51u)Po`I3KdB^xyeZDV@*+&0^Xs1HH7sV2fg3z4P|15LyNEW<-q;2Qb;aSI7S zxFIJsD@5Iplde>){`NcdwXJ^rg*@v%8CjjMef^#7+xf`qPtvJ{X2q2oBqSA(kW`TJ zqm;eCOEX2J)ba8=XaJ-#R- zqi~JpQ=E$SxfGe1lpz;E6dnS?%XcR7^B3yncNl-+@Yju(-b)`O5CRL7D^BDMj&+8$ z-?Nhx+RKxsp<+B2=)Dobdmi{8c-sHRT&GxJU*<~^H$n}@2#{?13UlTdmoSLTmKy6@gev|G1*;`Dqm3qlR`6#c$=#a1SdKOvot7nAjb9fwC zn`ea2$5daDCgLJsAwpCU*u3Yx<0UJFjuR%mhDLn?+oiuiH ze(dI)lYaRc>Ga%uj^fvzA;Z8Cic z{-%!|)Hu+0`}#c>^_z!!ii1*tYvxa!Xj4_-A=;f^=bUra`KRYP$-x`oQ&z>Sb2gb8)`!2vW_+k$<%$~%8#ECAxV7Y&zZwD@;hlHXVJAK zqzwAY?%j;GpvJgvx*UCevy;@Iwf z$9`vHW})4&f3h{IYu^s)?Z<;UoxBuujvVznr$@#7>9udCd;9U!`0m|)XYF?%a#6@f zpb-JcN7G zb2=CRg>`}675uk1eaH*2+xu_7edq$^U|wwB?K{hroO^tcxJEuqW}S;&>!LJG`x+|l zPtP$EcX|};=~2I=d;9h2Q5BLZnol?mKyUjj$3 z$@KNL@44-5Z}PU8#>I?r{{RI!7p{@dcTzQIL5ZpZXlX@~W)9To-&*d#>qOXyHg{=_pBZoh66IWc4$z z!(*c3a{$eFRkmMwGkk3IIy@O-bPV|x7xFH<>;SAbwBUwaP1jnV)2R2&laX4xYjTZx z0J!zFlaV^x9ko@DuSUHeS4MXsW*rq)FhQ*>p;ZxsmRJQ5NLzfO<$~R4Q|zFtoH%%} zVP{jS7R)7!K}wS^m<+K*3VxtM=ED+Cd{LBC0xTviGM9=hCY>_ca0J(-I@Dz%8<=!7 zi6~1^lze6&7~)o^A#%kMGyd*h@jEV_kj$4ag4qR$L=fcqb%6Z&*~oI8%3w@@L<0+a zfN(G(#;RfFMGj7m*zyA%7=UyjBLzRGfreO55k3I1bOo0)%;+NKFFi1_-e2~mGWlW1 zMl?eV+zd_-z+^gYrmeFxc(Lg=lNKf>TnI3KFpF6az`?INM5`w6Mh@P=9``t|v5gH2 zpfvz~@oRh0_aEG6+CZ-Bn$S>yR^OJ(pT}+a%hdpT>9btn#*G|N6D>0-I3P+u%CPkYOPAe++CyN zYlI)z{;<`-ki|7Xj9ePp4ee>^v;qwV5HyKaW>RiVTCLVF>~@(*_Jf!gSne{BOb0U^K)JGuz5@&N@HrY_!kitoiht-_i#Yw@*gAJ;vgUCpVr-ur~uOTxt5*vULQW z?}DF5)@7F%q@0?JvL)4C(|&#}SS!{THLVq+2Gbn&g9g?=Mj2z2QO4k?Rv7=oBFkGh zFdUeiv*N&RV6Yn;7>>&Z3xmO6vW&>m!awKyIOqKMQ_eYK&lqDzk7GzA&f+?LgJHQV zwGL(3CT2+$?U$Us`z73)knj*x61Ry%TstHI;K8b(Sk%Y-_Bu?OoWA=e+?ym!NEnvy zbM|W>sbOP0aOd=OzcWAnRFTEfV%sK3wN+UpDi{m~Q`wZYDV0^-V6BD0EX>*u)E>>X z9%!=Xca?2c^IM&;ZO{Fj-N$q4{u8&wD*MY#S}c7TyZ$PbJ5kk&R?=8)O!C8t?N)91 z;DRf^&{i4(7^sZ$DRnwyZ25Id*-}c`-7UL&j2LB@)hlN8hn-wW*)hf#V~jDz=pCnM zF!;+El$y+rz_h*U^Jc0nr2o#4jksDT%!cO$2MvG&E=C5&s=K>iutEK#@B$M?VkHhE z^KtUy-QBGPQKK|aMW)U4ZA05OG=VI8H+`bb_CEE_CeSuyx1kAiTIx!FRqHmi6>;G& z_7|9q%XYMV*_5`WEzF(YCfS`|HA=f1Rg}gLES6NzozE$KbqgC#EdPadXPKpFQ$G|K zXU(6eFjr=20!16`=gWj~8|_0a%`11#DR<7GO~$19IMHH$?))$X1*O!2Vz(q7;%}PW z`B?5mnP%SgPbP?K=DWb6ldL=A2>UgD1I2Gy{Pxjt z&_VY|(0I5eGLmRhV>~=WyYWAMjC|+EC8eZU!BLod_x3enn;z&a`P7s0M<$aE0fWLU zj#8yqD$HU)7EN#s-|)S7tG9%Hea%}^CH|86?a;3;gY-#f`xp-_S7Cz=f^H1S!fgDC zOeWJICO-!TW-$Y@pn+NZVn7x@4Dtsgty+jW4Kd#V!@3bLzF~gvmf*t_gZsvZI^)ncN@6yQ>g&bA>rXKNF@oETr+geF)TiZ;EJ>Px^3>aoJwo@^LFKmBkr-uMBs%+6I{pl6pg1M6ROic)rtb0}}uQ(fVM_hWu zB#i%^o($t)lr44X5X4gnQ@AGn2t;3+xa>=EDbl4IP9Gz=B*Vy6WgJ6S#~Kf#{LrFzQ01a?yn!${q;&d1mMPB^Js4MF6b~NZ0+7(KHgv7`^$TO z{rjuO`-=x^N6d^})zD!hhm0zP;|2&SpSWOCThQDbFiVs@Z- zbnmaz_2+tY@2~E@zqsqqdENWV)cMoAzv}yoJ|{x={*v!6e}C0>900B{Q zPDw>V5rr91U=bw>FtJc|C73BnaO7kPkqu#DDM(qvl_9v>jW)K1pokzGz|bTN5v(Yj zoFPPmC$tE~q?A&A^`~{31k95wyjY9<0E^1hMJF3BVKq(d0{$DPr>rS>U8QuNY}!GvXSTlDQ`Q>zmOD zY-qjfO>TVWAs(;k4361Gt5s7)R`pu7$nx0sqb+48<(&LcM*g%XT-Y9E=U%wdsgcFc zy7R7deq8rf3Gfx{G{UOC9x|a=3=M?zX!kp|5{N-tme#*=2F# zax7<@-jge;xo{1?e3l>22V(JK7&wmk#dXT)Vb}&zUs6UeY<~zF(r0iDe+vj8WY~Tn zVMLG4^x46bjFgi9M-ub`X*|8Hpz&K(OFucEG{4 z@iH`E(0qCI%Tc!8iD!m?Ey#P1~p{rgE z6ku09AV6W<=)_a3u6h9QA&1n-NVMUdY<$x8&}?fRwG(GS0Rlz;cn!sCpibfF1dpY@ z2C^mRTe#gbczWwe22UU3RR&Lw@;bJ+qk&nDL;Em*AZ)jj(+?khoH*D}iZqd?frU7g z8n_A);+@}*ARnK4w=8VG5GkXL))C<_#SLxhvPNw<0Dgc#7E@jzQ3R6E z6A0= zLsG2Jkb@RY*eq;tq6In>af2F)uvMz?@q-6y2m*_iCteT(FE)tKB9wwCY7hg6Dq`5E zg2qY*RdavCVDbN z4I5bPGSQPC>Zu8JmN(>~+d%OKH*753Mln~)@Pjc{02HPMT(EE=7J@xJ9Dy-J3Y>63 z!v?Xi#X~(eprHi`e7tZu8j6LSbiF8%Km}Le@I}xTv9Nuv_t3Bd7pUMT2O`+viP(jr z%M?4vf&&X=5DPo(p)SG^hUf_rBha!%ORUGIj4({hzy>KYSRxj-(hXm3Fyl!KK{H(c zo-xsr8m@np-Eb5FP6)}aBQ?XU`1Qk84=MtXn4}WjAjNI3$CUw`3aHeKT1a}(nIRDY z0002v6#xJr3C|K@P+)ih?M}AY+IDfB*mt0LskN z%#H$DPMDw-zW6f01`gv8sfFlmgZXYfK>Qar*FUNPPGB6*J%uOn97koH@COxGrk6aG z9K30cmdpE&)x@*GO>RZjo){)HO@Zt5GI6h-ie%FU=0l!PpG&D)DQEK@96&}#<5sbu z(VOGQPoVB6u<+kok~2A(l?^NEqu3U~yUGz4-jZQe(7xOYvGPsPz=@~tg#F0l(0lS@ zYLtmD{vCA#pA8dB0+yV4P0wuGzvZXCWE5>54Dk|P5ZQhU&S<=23@XZfG&`CF+WKm< z$v|b}(qR)Rkz!<_EZN`ADK{8y@wNR-{CQ@})Us)C$NS{N)+)j00qc2NCez;93-J3z z9%$f;9d)v%pPL4?f2*W_IcIV=n=!J65{xo3yJK#{BD~1V1J< zCCv-JeZK&qJ8?+j42j?*pGKrrd1nC5kY)C{;;QphFPBeic9p_rAS^Oay40c#Cy>`K z@J_%p0lI@Zn(@s&y2F#O30;X6Ko4BsQ|B~@xw(4$k(*5mBPERlKQy2c(a981O5JFRg~ErM*lwvr98~ z$IOB?{0rts zVaeTWYrNtK(Z5D1qQ32q>ulX9ygiadQ(kwf3T0&Uegm_FyopHD5#~RS$2;(Z$e+yr z>D^Gi`VG|XLxga=&W}Pxm2c;(Fa8tV#J@^4i0BX+$?Zzt$GKi9SNUV<_r2~m!t!69 z=e9r}T;cmJ-m)yBO|z+8oQf+GPQsEqedw2LRAC78)d_?e&t69t6?m%q*}26wF|#lI z_aJbs(46_>0JFuuC)#OaKKI)^@rd?r0i_PbF-zZwH&P5bR%6P|O@|=^J;!nEF;f+@ z(}0Olf}lr_#SH*hu{Kg!8^v_J=%~ya)OQAgZOkLG*Qy z*JdZ6A|+PcbNinCt`Q8efJe-m>5lbj4xQNnAY71E^^@*05R6+jDHxT!Yd|=XL$i-s3M`;25S_*! zhthXD6L%`X`;ti!Aaqqf$?ZiY68X=;ZP>W9d|S~w=wk)M1CxQx%zzJhes3f{Mz>Si zO@24Hw~O2P*E-(bUETx-kY9P+YYjbQZ0a!6A~jRD9qYoK{bZ-Wsgc#o>YGW8Cv1wX8#P~ zOt78+;T)`({>ln#Yi2Q#CoWk-LVm01v=CdW*JcJnmUD+Wtp7GVd7v=4!znq9^WIUi z^_U|;>3=^5C#j1AiTS<2Z!|zuOlP_e=H<4@xum`+faOx=?XqO4WrjWyu&G9M@!IS3 zDPWVWrSb^=PHW$m&pfKaEzXh}4Tg|FUQ_5Zdkd+f7zMPHUNQ^qVAaK7g*_}4?`CR? zh2?a-Jt|ccWbt+V-8QC8A$pVs6)2`v`tm=Z+a*8(GhfPDiL$%VY?PT~_(@v~p z_NwFd6JWNMRa?~swW08SRcgcM4Didrj9LQw@2Y_iHfh6z`nC3Lu)p}kpPss$kJm{V zMU)y>R->QeMU~w0dH7qzT<5d|_r(!Yq3IXeTZ0HahccD7ytgg!_Y;{WG9Mn0Ri*SIc!RCFi`%Sga-)X15y1-x12QbTG9y9-I z?mKHUC5ni*eQ9yH!>LFsT9ua>>7e|PF(xX! zwIh5Ch#U|hB;UD@js+AsdA11^ch=c#ISf^avs=UgX!yS|e75Q_uD$(RsI?vcDH2Zr zjuCHddd8ma)Zao+$zO`0WW(LU6 zt;|Gy!ext9`;wLhOa^tV?4XOXc^nAu-#3g-3|}Cc<&K6WGP(&DX0|_9VY&{iQLnEu zWRilS{1BF=GlN_8|GabcFPp1DkB32mhz3tI7;#O%?zR0J;-l-^ed&s8@fqVC=x*-c zv2Lmu;+EbQsaE=)!B(w(bZEjpXPL9A15rRHMp+%S$jDixT_YfQcn4wUx~Ly4sxp|k z{+gncv@QzMFx8J=byQ3hMMTMRK~wS1uw9S>e6b!qhgms7{P&mxp8J*!lOjTjd9ZpH z^C0Y~0G)w$8YT=|Ew3_Ar&i?M%CI+SNHePjuxiK{YBT{s>}mx;m?1%DdmRXBy9&@6 zzahnNVG1PktKf=ifK2f|cBcx3%ce_@o^Tu4uZ(P~{g_eFGX|5zl|`Dv4I-7U{~ zQ{ckp{(r=fQ=nyomp}FoFWtnnK3UR136Wyud4>~d!oLy32PG%|*dI9NO&o(LO?oBgV z{s?vnO*6Bmf`YS+I#_#!@U!?=d~{Y;O9iQM={kwOFHr|}IVecXDFgteMm4Pgg7^bc zrTd}`q(LKdc6d+5A!&?>Gg6j4*w!9JT>(^=dtk<5Go-ADO(}q~BY71=06rMj0KdkA zjQKpjH6sD#Z`}#tba)Hiho*s1f%`FNP3bR+$o{_fp%$&l#{~yuR|AJUS5U{0EtP+8 z50G`ng0cnLVqEPdfZr=cs-+h)E}P##0AAZgbyuKJ_^KX1&W|b)L~o`?Fje~cFLDt< zz;UM@dSzhNNuvCmKjX-65mc%|T5sdM6(gPi%5~zQwG=}(t|rv_ACr~iGoSGQXE#31 zthN6-a9UI~#*95dr;zT};Z+DwbsFB&m3%u;wfHPb!6c?1HJ9%o5o9Rfx_(t<>I~*R z_n#d|)&<^HauZd>X}$B6gd7(aNn7u(c4vP6agH9-M<&idy*2$<;&__3%9#y}r76P! zLC7GRFXk(_!F4|3fCa5`_jeUo1*Nm#Te_pEF&|-KHZ~{QM=X2KN}cr++-=1!mBlBd zfR}vkcSN4Db=f8mRep;)9*wFH9*LbWMN~*TLcEn_ieyq6@PpjOxHAGCktz1gvT&076Q6Z5>U2~(*$7(TlB-A7luQ%=YbLvKHgA6aj znHvld7*os&IjZ0Rn}$=~Ka7_dgmdIaPG*A{|8nc02@Jv*oZhvnSxmHNrj@Z6U~X)foVlSu7`++JwSBhej-ztx z{%3d;0pkQdr{r=KF#=yli(llIJTy&F7<86&SWluHj`MspzdhNh=Xw=+h_%h!qINuv z9T+?yB=(ZL%qienp$WQq3Js0bDBgBXn3^PnJu#$qkr-A@xnQki7a9O&r=vOpMI^C= zl);5B+}+3NLOfTU6_glbLymxg>@;KF)BcL&2nJ;cD7$zQ24Jy!!}oHu$ianYX$Fdr zci^i6p>x4Xg12C<*V8itzp=8W*=}UTIKPFlv6k|fwm6h_WC$Uj6Da_1TBC3TTl?mX z{u2206!1EpM#mzdxA8VRMom3>z>Y&8@8GvjE!JRez5s-v(izC5c&Xee6 zLNJlO=A;}$Yhq8kU5cpF*(@jH^Kc|(NEcFGEpq%bv#}3OK+EMDCCjO`G|ZN7{r}G5 zH6f1gx{6-h4!z{6UONsN7|OueHGtibaxj$vl1{(?(kdm)KXjK$VT0wkX$0-wBQKxQ z#vo{DN1`k*rwU`%M;6;9uDAk+h$D``{3r<{Jv@rrwRY7QEC zaVQPnC)-e8Qse?flNsvGkp(@Yst~jqLggr=b(LVCalrOkb|#=qGozZtEvk52G${ZW zrWgy z_JLB9O(0!#dXB-SfV>;E8JIHuOqBfgC3!<-jP+&72-O%i`f{aL_E+{IG@io(dfP|8 zImFsRq6ipk>PdVqZ|*O{lE09M^*|sQ!%NaYHot$>PD)!mAiXNCd8N#oZ`9zPpQ(HXA_oJ3;ukG9;R{2+GAwtu^vq`l1qklyln8s zq|RNICwUErR4cxgSViYQvC+XAh(&1$=NE71UUt5Dw)Er0l2};)uoC<5GXqs3Ec?-5 zIbPbn%>SV~6H&-FKYYwfNE;FE@ecvqH*->enJO3{p+NMmfkKzm4tkivQpxcwuvOxH zJwtP%nS7@l@X}aV0E5MPg=;g4nIL~_Hs!IXH53qy$7Ov>(ayM>=uiJ+h(oz2a`#Hy zY{Ma#j?D zm^#cwQ#c&RH2X4&G%u#$(M@CH0}FGX2P(uF;pR z@1#5K?pU5e8#P)YZt$s$wL(IS&1bhyjB2D!k{CqR$LUkMQB3YJ+g+Ye-mze~Rp>2P!YBa zIjrn}yaZY>yk|jRXmw;NOnKSbaO>dMEWuGjh%CmrYQ;R))Js;D+eYl%eq+RfZQ%ar z|IryV58v-tD25CPVFp#SB~a2x0nUs1Ikgbn%5+Q-f+}$IkL`VqeYikJu+g(;6jyF&nyE=C3N^i+j%ze za5-q!A|fMj;{Ww~%>^Cc5Qw0RA^6|!;5tR2h8-f$$3V~ZW)M>>Z5;F0P+-FTk_qar zb~<3$DrmUA91tL74=-2&>AQCKH%U)Pr4gb!$)(V@lNv^BFpQ{*^0VQ71D=;;gR{D3 z;aONq;y}XE$9aOhKwZT-VJFXLY$bjhTO>Z!LI8b_k3YnNemIgprZc8^D3CRB_I(U1 zDcou9VyqD1wxx!C=Ab7aCgq5~SxD%waINm6e*kV70sc3`H!yBJCVb!$3TCGG0~5m8 zV*YsJfXB{WjjQ0AL%>_>ybZplcEPzt(DIuI>I_d!`+X#n+{oKCfaIPhJg#;w(<+B> z_lE*=KT|Q4MrgxyhWk!xO3P0ldG_u;8f{vvn^&ExH9Nh-j8DXsI2UwO_Ps%*!XU%& zYZxW6U%RN1wbn&|tyUq&q zYklFE(~PcNlLkhL^#Hbl5RqEIJK+jJ>e+efCXlNZS&iUCMtqoj*|66x#wLWwnR=YWto>u^xm`3{{y#lQ|6fd& zS+Ab}Qf^3tZxgr=f^_a+CB~dAe*V4}0@3uOVHeeseuJk$p=i&9b#Ro2)LM_p=G>&_LQMCPt?!Hrj_CQX3?>0^vRfN z%I=A!eno#AyB~@-n{b_}QvBVIZTI%t^Q#@IGP^%U;LU7TZTH*ji!RfCa#L)W?cd#i zKL!aO!z-rUzG&%{X4BaO2mkaBP4OI}uxdZB&FXBI3??!k>Mke5NKeday})?U#K0S3 zvniL&Fe%KlX#$wv?Uw6aG=@@|``&aIkBeRsfYeVsn9U2@&YA|0XZ#o$5^{ZnnX;@>Uk> z(7tRAzHMN=BPUoEr2!1;p@EG|c)ud7)2IM^$Y5z^lQpTERv}S^*iBEyr$xa)Ugz9i` zJ}N_l!ckIeU?|C8EE;s()H$;q_B=d%}#=&tbGclpU>^iz_Ur;qg&PAnU;!T5n7_gT71 z&sc9G#Q-`}P_TsskQZIk7MI#!dOyn(+nnmw&_=#w@3b`VUnHy6pIaVV$_Y~pG_8=r zE2LW1$I6YE&G=xrE|-OzkR7S{zzxfQ9%fB{kvFyedSdyPLSc&*)+mqi&3h&%3@RK2 zHU?qDDwLVt(-5+HZbKhld@A0F$z<>Wv&;+a$%tT8%rmTK7&l~gPo0;faQdSidr8{a zC=i25aH059V&PHJa~K6!Sx<2&9EyVg;BCDo87Sp$jM#frqR_UMrlBQr8v%Tc2js!! z1k6Uc8hRq7X-OMh{6aDha?;CflZQ8XixF}%Qu^oMAOx3e8pDQb)z5+F2+H?mZU@18az z0J@QJ`5H{ZYTW5d<^%>_!U9xz9~3oKW?lqNiNnjeOj4_xEB`G5_XEdS_!YF;Is?09 z#B%wuyOUmT1>M|AlL&LuTMxxeCT|5fe4$1*pO`H=cXhfu1A{CY=&Z4U8rD=@h^}CW zo{~Vgcq zlJ!nOoZ#d!gc3KFgzw}*9Mk=uPoF6J0I7>eGHLfl?^-Rru|F_i5iy_e7SE_8|H9QI zfEzW!cY+B*k|*i&{QqWXr|=j1hovf_9y9{O8P^EjLPC?7VwSdvJP7J$AdB=+V|b<~ z#?q|J>LOADywwEqQz{?su=EU3<;t~JROL{N5QB$`l5GG1r8q-jXxhkcfBw#=I?K;w z-0(f}uldfb`)5jO z5QZH@pNdjXsPq2denIw}8F=t?=b6Qz;3*?4T=+$Gqwi^)O_#AS4%S z{J1(falO2E!X-fd=R6H9WvBSjI*f+aU8K_0ma1ESkbf|+CgRU}5|v(oqp4@j=ED|^ z)gn&kAYCcXm1_zBO=3;Z73NK&6SQ@DvpS4!kg$a0?*Yepaa#4u2_IU~d7PA5neED~ zw!Ct2B7T(KPkSAo!AQoEVnmylpx5^mvXmTZFz@|MqVqtkW@SZ?-jv)=ZRxsp$Ni9f z9xi>IDkOL@b_2My7>q*7k#F{HQI+;y2l1WVslN_k!l00XT%Ffzf>k&^d)VE@UPh} z;t>;wM3jAv{y3o=I$-D=fbBumKyI zzT0M|&U&pJuc|LPNu24hQ7*rNa?hX3gWHKKV$BCoK$18tQyh zu?0MWZ#-8ru9R`ZhpOEnX0>LP0B4rQXBG*~3ehYmO+sGXR|Pp+n}#Y=hoNI!F@Q}? zn;KLv{4yGpRKdFq9weXFv8t~Zb%r<}(H`{4B3S=IIE{#0+C;dQDd?=HvjKOVtav25Be6&TD>9HxaC5<&+!XncI$rSo zq8Ey0xs{`6`v-qcL`>FZ=r9^E*wyhmIZJd{ycjyvG;E?#=$BFL)yg>p_#qipoPcmu z7wRS`+vkuPEbmf_Ju@ECC^r{msFn#O)#~XOYnh!MLWqTmal-dElrPgo(;x4wRzhzQ zCX<6#eJHtOtSV;@u4*W3_veGS+6|H4TY*kMd0DBR$|%#ucjV6#nZOz);*Pk zQKW0Ert87VG`UNHuFyClL+uDYYlS}LN+%ICe>WxoPFcB6HUM%EEFQ8&5;8}FtNP>+ zl}xxU`##;p73>pYZZ#%tcyyx!NO!fC*6|>GBALqlcupr_Y@K1Y91(*v@#u`f2vvew z_p|!f)^_SG)j{V{j3>$5iAz>8@*1x4mcWtn26zBqOdK#rLxskRDNoBojQoIkfsas? ze?pT2(IEw`Lm%QP2e>0*cr@(fYby`%?p32vQU;X zGf4qkPlS;o^j;HtDON-qWidv-6d#hiN@R~vBFP|-!nbflQH>;G^R<-d>a3+4;+FDc zJH>nI+3!W12-iT;r#%3G$2V;pM@ZG3SNTtiX6aA+f8W(wM6xnf zY9Ll3=`!|`NZxi6gGP%Kt{W;!5)b#s`mZCFh9zu{kkv{OpivIULA$Y#+eb}vVu_KqwE7)34sLqQnoxLhIx zZ7Vtw0rYhj`9otP;V@AB2h9{tYW)EA*$PQ@i!@H{3DsjudweODxCxZ#*VBdZhK8Yf z_V)%=rh74|d@LBL0%B?+0rl&vP+j4%#*aJmm9$7wpij8fdTxGjH=VgbWK|75LDwQD z8r;7}XE{^?_FBP+lqPD>XiyTz<~y3?zT4;HEokmlqc!R{kDFH zfr}ZRr*O)%H_(MBiM+nSPP=?+gA@+AnA}qksf9^h9LpVB8$x_V#;>}3k2@fW&UrH{ ztd?(XTn}Z&+{}QW8QjBg^(B7`EE6I@l9ltCISW$MRJi>wf2^sG!yY2=Aa))Gn*bZj z_t8)T>$)uc1M}`BTR@Vy4pGP$35jgpn$Zsuvw}{zU{%DtfI`^e-b9h$jB~6?U@O6@ z+2=t8K=AMX}A?H9?5afX)_lt7Je3`L`jgTtq1A?}7ySWjj(Bb@h z!}ix$z*8k7sW5_I6eJJYHo;Uw;8d{XP6C^8UEK^a@{0%DS}O#@UMl{^CPFdHp%Trh z4ne~}%A%|QU~IfS$yjpdf8-l4dcKuDcRQ64zH^eF=#w0nO&O9crB{&4a3fYJHnPKN z7^NnRT66$m>_>B&L-FB0nj%|;g27jwt`^gzz6L=IxL-t8`yIBl6eAUvD?r8o_;O|I zo8`;fKJJ^d1`s9SB^!5x`NzQ>DhA$C8>&z?*+T zb=rE6{Vl;7t~%ImJhyBaAh3}bz1*}JWXUadnaU&DGNL6iWZ#nW7uQYGnjmITA9MBJvNYP8(1f+=q#A!o{K=`@p9)pMmau72l35S|>O=x7`%5`y| z!fU*dKZIDi?(~_s;i8F4lJ{CCxR_tmT)hL3Elsd3JhpAywr$(CZQHhOTW4(RjLkE) zXWo4Gy&HeTU(vfeD+|4=x_j^JsLZt-fQLledaV)}x^?8|uuI<7itLhbH-)BEQpkJI zd{Va=xTI&kHG<_Cg)b7HbFvzNd(PEVtJ2!KtBWc-AV$y>x%$~05s}h9hlc3gG^!s|1zDRrGITQ8|V4b`XL)@jsWmvXd)(EgLcoVQlGIk zF?~LrrJ7Wr3a)%>()@ZWkKg26e$#2RZf1uvrYI+PlKr7eQMPalFMKBe0N*3)#DWRD zVYe5Q07aR)$HA%%NZxrUu3@xsCI%nCV4ELN6;rINWlX!{QrBg>6Q2oUP+9 zE#;DIV7IseH}nU}B65x0X)fI9OA~)hFz|rs&zvGDTHdws&fTEWJ7A0i0K3@MAH#&~(B0f5z1qXyIv% zhA!&bV5(;3bVLZ@%KL7B1m)_Qu1u4bq}`wfc>h*Otxtlp8VB9IgR-9U^1&2WtXym6 zgA6-!MuF!vG@T|k@GPn?)syovP&{7D!&_}t^1Grmbch?gYjbw_3efi#LK291LBuT+ z%;45|RF%L*E%H#uQ1J@Qh{j}Uj1ZPiorhgt+ogovA#QRuPAyt*RG3B_qz=r}o$w;kWBN&dOV7}OfAQAQldFEn(EH#dm? zQw&PHkzDb;Iz%1=O{8HCa9YUotBHJDk5yw?=Dqn16ak?BCzG&@=p<9D;PA% z|0vJDpJG)X+A3LSp+HHHfvnWh+H6OhnSPQqI^`i~5DJ!=-K@}bA0l7^({vQS5zIfl zsXpjcIgw$`a9)ieYeosd5o`?{^}QN{~K8dUj%TJ)1Mr z17j#BZ??^UT-aeD)7>k|cUwdJMWxX+uqG?V0W)efgO|F<>%Dd$utR_hs)!a0DMts0 zO1bD$sXdOp4{;Ol;IKFentX{!o`Gw{Mp3A9+yEtL$p*(<%O6;dsnxowk-uDZWRx>d zvO+A|X6SE#duVl+aFLXPvsr*;xYM@l1wWZDqv>E1W^!J*Q-sC6DhG2_?&OR2-U1t8 zIpD08SV=A_4z00J>0oK%M*;R@ko9gT3B2jv-^zje*|~o&)Y41HE|B40L}G|8TJC^g zqyPnoW)w*UUkaOueZdYV4tUaeE4-=u>V(@K27as+2&;Fb;XJ_nycuXXXO08I3EkB) zHqp#aaSZUOLs>*C3ZVxRf%#oK|<5zMZn zq(bq=3)DZ;ZT}&KERr;i7K9`@^KUUV5Q##C*(;y7BCz|?)_eVRlV?(c*Ii$&xs`|_ zq6v=wBie1K9$`tY$)3w-q0G}27OU>P%16>z?kWlrr;}in@-G_)43dc15fGRQ$k4{4 zz>nrUPt;y$eK51kAVdnU)ncT8sz^j+IZFiS2#b*TKxgsQ0Nmn}6h(v?phOsYris>v zRR^TE(h6!2LYjltJh=+hj7wwE@g*)|nv??X!&t@hAGx>cP-^WUF*fI)le4>vmaRGp zZoxfb0$Qu5Y6xUyO5i5fkg|D&RFozM5-I=dI8u%ya{H-a*m-b~4jt={n4b?eVn54Ee@?&s2lv`5C$Y)y0c{UwNU z)0BZ*-DeCb9YueVtBtpv2ro@GpMa{(&-lr;tx~}|y4fzIHvDhZYj&R~D6*y;z_qqu zoloF%8)%^kB?wZnrvzA%#H-1(P{DVRv*NR*2(-a5e$In7r45}G4EmKoS6s-|IW@SK zDoMBT!*yal!`~>oEer1+^ysH5NCM#oC5LAY-kP$-Go@VFRlCS0#2% z88jP5*?7mzBy*-eCLrPW$vF*w)A>{oVs94R5&2tY^;ZrOLhC_*U_a{%hACsJ$Xn`W zl}|1T_lm5BxO%!UoW`Q~wA;|ni`z>Yd7BxLu+Tpk$bxzx-sLvFL_m0z{RCBeIm1F> zX#)sAdt|(jEKs7T;cEMjJQiCFbkraJk-z6H|IkCr)fA9WbbJKf8(nwb?w=D7yDmXN z5vHnU^@dn`P4%zjmS-g2$YhxbI+p|`>qE$fyvEJu$0W|8VWd=mf~ga!yxa7r0QowR z&EVL8X6{u0&D-{dv>kBW&5eY(u>E zi;H#sv0eYM7r_g{_0RCXV%}`X!O%l+C$tQO1US|nn|bK*`J;k80laWxYJY8HWf29%gTP_ zvwSVl3r@X2voXdRnXxiu^aNpjI)KF|9&jK$KSo|}YI!)MHL)_LhRg+Bd$@8_Zp1gB z%4SIj!Bv~gqEB#ioY^s@)T-QXFsWT#Hq1D1F2~(G(n!s|JCT1sxjrC! z3a!5kqF#kcW4p5Yu9>N7)=c4>`0p8H+-9UFWLhU%HG#l|zWeP! z5A>8UtdE6b#iEL`2jG+==2aJE2#Y;uZ;G1=L=9JljQEGdD1|FSB6yofR$4f`vZuB- zX+WU~yF(YWvVh3Lq*lYo58N>TZh zC1nI;+En1$K?KmTYIS~IqBoQ$_Fvevh1>%8B$qFVGJZ|SePg%>8H>-w$mKV2sd6~6 zX6WDSZ{n;Dcz82`yW!o!A3Q4zO1RC0RzSm&$P0lrj9|;?18(r6RVH$*`IM`3)(=aZ zozv(guK7T(eyc(-1kZrU*LqZQ~s@Jv(_o;(e1mR83N88k99OdWkam;!fPAOYK$9BdU5L8!; zM;ZU1{fAK7fOkp%$_b7r(3SaR6>gjZG145x#LWXncwF8QE(CGo3?iu*K2&MI=QpP6 zwnesB3C99m`|_#Vxw@D2~#&F?_nW>^BU}c=d3j1fmrS!%dXX2(GRPyy`5m~ zJAN)<+FCcoM#(wf<4FCvOSa&k>W<_T3mH58w-dnl*LFxR#t(wV6+hfaj=!ZLZ+0mL z%{XRBX>Z<(mm8xpJ^@H~$Sr(B{+pX6nYqAz(fU3i6$(_acsCny0CE0|F&YvV+?%^^ z_U!4pvuNV53S>uA9@R<%tYb$=52j(jv=w3ZH=g|}MZ&Yv64eU4RbPNc0Zv!Z!>W|# z4!1YKLV$DNrqh6BHkqc={K^7lvy|o3P(dnDf|f7Co&=>zs`5Bg?2oTUa)+R`z9bse zr9ENUA09FikIP~X|4V-+28Q! z#8>2FglU^@m|Oxj?H=?FOo>3ZI^H7UBmq^aOeTSS5%c z_|8!^WKa0suJ7qY%M<-%ldHD$Ol#<;i&I-aMI}$9*xk6`*fv01kunnGc~;RYr+Snk z=ir3m5>tCqOe&?LYjl(4tzp*mQMxLxU@i$V-HzQTITyle$FM-TGSl@nMOeX&A$I$t zQ?kHfOe&%)!6lYII%%)v-nL>3ZHmqCzF$usS(Q1 zsd+NvbI6S~=IQMg%XlhUhPrabckUpSgQ(q`l6sra05nCT^G80_ox&&Rf}k)aF?n1U zFrn-kaNJU*3oew zsgPdcVP#}MD9JfvMo>w0n|GqiQgW8#FJZ65Gc>7|AMgtvI57G!C=K6%EdzGX0nocp zc4h}$245)XLUqvx+z=X)khXVf66CkLl&IN_S3Ip#1y3BtZqgo~Mo~XtppVcR!~KeJ7;2op~vg5mFSbj?u~$A_w&;&oRfAulQ2j-7J@`NEf6TRyrOV zAPS~bVLh86#CCh7sZk-9N-xE;l zlm*0X{>NL4NXLFXhBd}s)(yZB!wL2yj?bcISdcbQ*xqBUvQ>UCr?*scREUai8G^ZQ z-}k3U8_4JsoVkV8jOTsO`|nVH+`);YNV=^N(lJ{wdz&_S?taRi5~as^v^+w>URBG> z*4zQ&2JqDSv4T6IgyH57Ql81h-RzPO`^dC^fiCL|1ViL(j6Ll8sHiOGQlCnn;~daN zmI|t~#M@m}P`{UO8&f6^mYBOBFzA34`w2}-hp|SM3+zA>N(wNIYH#<^w1Q{g_Fyx? zhOnhqdi9((&rX7AXM3USh}-$rhHV(|BV zzmp#DpFz~NTu(<;XAtLx(dlf=@GA!``n1^GaM?n*R6ZT7Q7AvwyX1ptI@ZL5XV){? z$M=UkFX&ie?63_37x{nZCoGoCLeiAOq%D41d=gSkflrit#L21onPk*9>$2%#Ep=lE#c%X-@hJHjwu(U?qh{H?$Zve5Gva3 z)g1TCt<`G?tl22wh%oCd66e++g^;S)nYD~b`3T04zsbkg82%mTtOR9tC+}?V59)1D5+Rh*@nkdco9Q$P80Qx; zNovAtZm`OGom2Mrwb1C$$ydu(-s-JD&!5jOQlm_HN@n`uktsx9U+X$+^0@e`izpEo z$v8>RDbq52P$hDGgZvT_OH(ia2{sUpUSG2W8!c5LDnxD!#RrKQvNGL={g|CVFe>Af zo5W|6W)AsGBnHJ@~FB2EV3S0?VRRoXeo+i*cxVVizu_LYk`$MU_q?bz%jteVg z>NhQbpWaBPH)4;1B*-VtNy@ZR}I8t%(LF#cAtR274^BuTYol%<$m6GjW0}bY-=N26>s29BPXEsp}VIcuF-)KzU%PHJ8a zU_(UH<-pDdvN_q)k}c^SE9KE?OP2Rb3Mxt|;L}%eVZaTlz}w{F&r#vmu-xo*f#AH6 zh=tyc;m~U<2Nfxymq%7gra7lwD+>Yz|p`p`&bF5pLGlKn(W zf9eeD>NZjaTUmYjLexQM28##OAbr;5H}1`7wpJw`Ks2-)-JD?D2L7c67g90b4tyD> zQTV}c;|Yn&ui>(m=d>)_-1b%AJvj(4h-_4-??aT~WJPwJ<_g z)p>I`uL$~d>%D+q^AeKFpx{l#Svo5))u0+NL%)Wxad3paY6``XncbXz`;CW!EfI^lWj_N>U27pRV zwr{*2*A{>lFL_fLP)L6ocR$S>}!`4PsSg+n^d4=ZiK(1g_fBaSw7p0=8_Dg&6iVH20_6ZtZ z3BKc3^QsI4Xo-p8x~pwBGG<@IFTXYznPJ;pg}%u>BmP=F9h$*8R7l(K{)lC5PvxH8 zI?_ZfkdI?>S#aJDP3B%6)=FgHf2yG85`g*wyHO4C&E9)11RsTb{X2X*1sB^6{l<4@ z&yTl_!8pr=TP6SGLJ>NOoX$jsq@6mz^wn!&R6lkBKJglEn%kVE`Sr)}V>)L12x0M= zo{r1!UEBg0nh}2UAOT&&&FeBN0be2x!lB z(o?NC$PXJF0a(`D3Isa`)UBmF^@XZKSN zD+g+?RY7L?MTa zguN&qqr&E_x;Z?0A6N?0Ia9{>y1%c(R3BY6ygqd6OoImnz zjI9QL*hmGolup1`W$TL>rr?kF0@*@=F8-?qCGTguc%@Mz{8vmvhU6hZ+amQV}Au~Wf! zvlMvjN&o`=f{uv@D`Y+&PbGW$(G-5H)uO^JxV@SWfnIKOMJCmFU-h?SJ%O|=!6$ME zRPoRj{M$NbX4ux+eUAdwnT%@m{|ix8wA2%*%0cF?(H^aDsS1;i1lHNO_pu^3 zQPx3Zxrn~I+friztH$1N<0dbE_4G>;%b^~BTVVgG9H4ul%r+JnS;?Hr^+-?#UlR_@ ziLczd+_LG8v~&FYFS?jqvZL|P_W7KD4=S$5F%*|l9BobAlH00lf12y#k0b5)=VCiG z?!)C|y5U17w4@fI9GYut>Z_#ps;Qg*(MA35-si6-`=mgfxP&+t5e}iED8}E zdZoEqA$Q3VeTB?6s7iw7^E`6|fIhCo^BYr8EH*%{ZZ;#G8S;qNoV(a*7nsYLi{cif zrxb(YsK-CgDL*m&Bugwgdf*+fk(^A6gH<4K>L%ysGlb^F8c7VB(q}0_JE1bXf6*(1zgdQ0uD8w9rtU! zdm*_EZY=^ln^=%kF?4V-hctD^PS%C06}tD~FE9!7hy%G>?{0{PfQ*xqjVuo?>WiTC zz;6jVK2~t@PI-LJsJJk%J}Pmu10pM{$%1V}6@;5`EjVeQ=B{wdFr2+Q9y+J9vHZQW z&)+T@Cw3wBy_>^Ye#tqV;{jqoEyYrAMJS( zfd!w~G2tJ14=Uj`__4nC-{6P>1dk9U+NNr06h(;>?suKCL4Utm0bX?Fmjh_o7uf}s559OWDz zd9}%PW4LygzV(2xt9}ypP-1-m?;e89dHC)87(HG3AKvAK)|*f zqpct?QdenX*S2pJ4JPG%3qI#P^VEd$@VXl?4O;_Upn0+bG|K641|Em3mQ2W(8G9?R z&OPXzd`*?aS(NqS^Ik@JO_WGdeHtWG8O!g5gSUxHXOuBAsj%bk9-9ers6QYNS6TOlpc)I!T1t6UE6vl`LYG ziZm&u-&;}?84~FhK$lr7lsM(qwyII{l|E1#4F^q9l(wMNuD6V{H0d7Gc|0VY8p0oD z^LIROodC!e|8s1FYk}OM!4l$zkuvpB`h^{$EPeFb`6`=#sX_k_>73G){#W%hV0SSA zPaiRlYt#7@GK+VC$IDp`E-DFl*$z4~4URh1$1t5|ZaW5y1;=v(sSJ)`|1WfnTB$f! z30|5l`9^Ljk;QN@+2FZ`H>ywMTqrbxl2o1*-6=%UsM=@rt32FJ`zbilV%Ahf4|Mj6f^m;GrZY#lN&z z*fW<%T@ta6t&ROjV9O^~ZcXb{{pC>M`(JR8Bqq|U2@yKp{63hMU9W$CN)V5CAifQ}M&Su4- z&<+JqM6suRzyw_vU@%VCub&{0y=7e!lxo5szvA0ce0+^6s>OKaJ~7DyqU*FRYf0f? zj_2~6IR%MkV9td256S|ueGy+{gPN)4GX4P#(01x*aNp)xltaX5RCyL^h%mEC4-_I; zeWeEutNA_IJC%@s5z9OES;{o=@{D2$(VOuCtVNXqaFo%b^Y-8J!c%5CoT49QoT4HK zSK@^l7f3>n?p5^8mOqcTw$PV+kL(6JFQTful3V`56dK59?ey08P&*$z@fJY&FSkeg z2Y9119a&$n3lLt|N0GuTD*b!CQDcf1Rs5)ou7&k#iH^Hg&3NvM&N~tVhPfd2mw3`{QkgvJUjh!Dpbn( z@f}7moeAu^ZG9VZU4=3qoU-^&P;$euaCt5`uNnfS?v_w-PnC~>ExwGm3){y-{gM`NF2zr;DRseVoePlnISBfsNhc{noieI*-DN^dqpTSs-+f3_x_YcsIper~N z?ulab3gAS^9g*8-6mto5&Bq!hNc;B0q4fF&vM`oH5Wq#1 zfugY~Bn4Hg@5SB2t^f_c717gip`EQd;aqo|>CRS1M^0r*ynQDmiYgIm8u`3Ey;?=Ui!_1~T7?=fP`R#8#YX#7S6tD2g|Vmd$E4>pdK zID9bOyT825Kb=jO{4j59D%o1#9|&EyFPTedaC~a&Of@<(o}{RM?+Iv!`ZF6-LTbA;?SJcjAEy8G{wqZ zy_t%l++jHI3$ttMPK@*W#VWH`uB>K{+Mc&dPq%AoG6rFzW%k`%tGfg{1v9g>n9P~V z9fAjS9W~l1-MxlQn%8&K)LA-Tpf?0I#)jTKmw_}XOI@)%eG`T{4y%^5__r2CQM%-a z@1XfV_dceW;OO(74*PC~P<`s6-Y93RcS__GXjHI?tTewcA0udYoZ>mA04fu%eAE?3 z(1708UBc^|x(LKIgw$ysC|S(!#nGI#*4Mt7P+B2uPkm}O#c~OlyoNu-w);ZAG0?gD zZQmCo%6yHl@wqO1pC5Q`F5U+LZRS3D@OkDwG7LULA=mWEnEeSCpLx8S2XP!Aj21OF zH8?C9=x zpP7FmomkS1sI>S&6ei7OEC%@5^4*}*&mEDUESRHHzGl;GsOiEOhK_naIG1)UL?O{-_3c<6Pq2pJKIFL12FYpxg~ zs#;fd+Xr2)w5)4QHds~~)HWTTqR$e|NkwbN z%l-kV8ZplZ%LVE-l857Ayl9`8*C628_nCal0T-&S9Ap@oe>{~{Ye^?fZaQg#fx5eg zRLF#=sY94B87z{>@#qkhYxiSZPTb@(2|)XGYon2P=QkFaVbn0tXvmH(G0j>})whJ_-k!Ei z7mM%5AWc7}UU)Pk>kOos^tJbL>*;7Wlcv*?iYRDXNHgg%ozQBHq?yF_5|!R*0LE3R zN>iMyVzGf;a=_arF`xS4gq6230LD0A{3`GF-nZnTCndb{o@!jPihbL*-ityYsyBG9 zrxFMZ^xb%LJspW8Vo0+nWWDy&erHgV)J#Iv%d(l*!aJJig~c?c$!BeOy6lm4sZ4j( z<{#)J%yeSgBFbTh#b4U!jx$7WwKyOeS$R4|GjX-)Mub(NzxINU4d?zseNhQXgW$*n z9ATdV!|60yDvF;C;vO73P5GOQyugB{=;&ui=CW+x zRVB^iy%>zQE)_)hUY=15LpdJr+wUp^5P$d^L|NlLbK z>YW)9c3w7yrd6{DDeQq&Pofcef7?uD@|q(mL$f`Y#}nvyZaiG%sLx(Tib|p3YqqsW zTxI0V3ezv+@SHDPpYxmR_fj_@ix=^VBVQ`cW(d*M^$L&tS#?qxLUI#fAuadjN6<8__TbrSvB_ z&65T2mk8-H=1;*Nn>{$={$r0nYg;G}8>rv%uvF6$uVFmPAxA9+$*EH~n&T+{i+%W_ zG+9W)T9BRx3Ib>I_9`N;y8@O^A(5W0mce|z zlQWRcFQn2rj=~+S#9y>Y5&J=2+{@r5W3r22?SkD>(CP{4lbv2S2I90u(Q0In$`HSm zrdU9+4DOX_c?OSUSYW#ovhw)jgV>H=aNYYt4&sFi=2;0@T{sBUHuD8~lGT9U5@!e4Ppw#riSiE|%D?Y~^phYwe@dF#2XQ;lph8Hm>dx8!3|Tbeqube8 zc+G}Q8;<-sH$r%ZIBs(bd*!~u1k^V1+02VISn%WYr7R5qSrcYxCFZ3ENB^MNoXv;_OMfEV6kp2X*dND;Lc z7u$AgZTG_+6{WY{@CDD0M8cmjYi~p&2u|YMXt=RAZufbok3X8TI{WOKJkG;^k(d6< zMl;YB$VQS3ehG|nMw(y#Au4@^AL0Fm`$u5J|HhX9T*$>26V>N3&}bjS`-_F%uv2i1 zo>SoE(vcCLU&GhO`V%!8_qK2X{lR{m4Uh@a^$?tU+7!70&C-dD-}>C;J;D<*T3CxD zaPze@_C_8`NW;-PC$O1>QyI#5I&*$ZsFNB(a1Ry$NfjcHU#e!UULgoc6-~qU>WzM@ zpAi8>P+`-it%y^)tC$EUiuctCx_4Lr3m9;pq$+@=O{nQ~s2{@^+M~g6P??C_`S~OzBh*8mU z7VifHwi4kTTaZcxq<4`g$sx~#-#-+3+J0d47s&`}uyCvSjW(*_*#|?p=Gh0K3=y1! zf`S49I8wMVT|m?@niF_XPw(Bc03Isb=;l|4{bNwzKtVPeWfURhY!;Nxx#z9r0aHpQ zD?}4kVNwRji0A`1&+C1OJfrD~;;;=3MSNAvpCMdFea>}wmEg@`hI89-X0A{3Im^+m_-1Nf?#+GDH21)QdMfM70STs7$kjb2H@ycGN3I7I)?WqxDp+JRt7D2H$#_;1G8au}&<;ey zC{13AO2uo1g4T3*Wd?>I2!veryYBBhDi>Qw!N|B;wXA2TvQmNq3*t+X)ZDpptCf%L z7jt%Ugm8{4jw!^u^BH`iGnbQolGC*anih;^KQ~Qok2Za>Ve)=I9%p%OURIQ)!@FYA zioiU1tN}vOiq`J=OXb~j=girMpTP^xXLM@~59qlw_%31}J-%bbzD;L_dw)ge(zAEg zouAq?Q|#Bbw-n-kyspluI%D7_*`@2Gn?O@ zQ^}GK*MTsKdQWBg`Mqvdr7H8Y(mU`SH*5@1=3wHV5p`#A9BnS_tNxtrMCJZD!@C?@ z0NdH>{+sUa*d~W#Yv9o2S;}dkUK{&hB~L-5uE7_xj;wv#96Zcf>?cwIQ`WN7BhvY3lH|;FWqH`e`MzXAR6J0I z`1k9mEAGN(8trT$J|dJ21ERG$9#*d0X?1P-H?G1-aj?z$m6W&8cGcWy9<`LMlBtva z8XB1Dx?1Vl)K@d#DfN(!V?U}-<1x16aXhrmDdkWEn|Ufh9AqIlYzK@x2DCs_nWDdj z(2jNJc~`1=*uPKx?UBx7upC@-rv#C6oqX=$pHp9%b^!_3frvYgGBAY;_^(B!^!_6n z&v5clq9Y)QqV`8a{yL}FQ<;UcQ1&ZbVZlijUf$ZvV8cmv1-G+|YPL_iuF|OatML^; zL9Rg*Mys!dspg9^B4_Crg|kD<{!?TJ{9Qtx4(5?d|!S+K!5sfrn-L1KCaz=x_4(* zPjFaOQyE8pi3gpZD{^*Ea-3(`%(H)us?A@;r+d_OmeD%-Uk@ykG}}JRX`O4Qy!9#l zU!FpZSx$dO6$WZ>w2?qjSyf$GQQ53gWktEhR@K#wo0`_ujajF*=GCt8(Dm63@aon! z>!$oKEcxK+tqG@9E$&@8?pkr#8B0<0E-FHYQ4I4K#@VGtkeJ; z{~6)V&95!7bG`HD#qD==_P^u#J@mRA_r3Z33U|BS^d2|8eCoUZcl7_2UVJ@W_p+ZH zI`g|d{8zdATJLrzUF0u#3-hDxRm)OtlZ{@}B!BGg-X<3DGn^eyP4xU=cokfi(WT zo>On6inG$w@b8qfHG)~lD^SPiSzn=gbVU#K>( z;P+$lee0`g(Y2yX=4g|>ZLNB7rn{||w%Q+lt}H{p=2~Kw?}y*eeYIrsVsXQXjTt^= zRGLx}89xVLEXteEwOwVhlUjAj3jSRkYqi;xzw^hl&X*P5N|x`K(8G~M0gfyjAF4-@ zz9jl;*vnqs6ozpOqhHJ~4b9afS8fb>(uSlyLtyx^1<19`I zmi9|Rq|fr3b?0Ea`PB+L_2TyqtW@%$Rl>@$`s4J;14xV5DsRjK1Yn0P2b@i7OlAE0 zGk_*Y$Y^5ooW$E0)sljw6vMe!G=d+6m5=0k7D~cFYX0PQb$-*RB@e#N=p$UQGM=CUhVH+MK2yAh^u!Iv|7k@=T@X% zwAOScwc=bjhO5h3KiG66400MQ4mV2y=7TA8_U}3yi@F{QkD_L=$utU^qN0MHTSt}B z9@r!}qK5S1R^(tfxp4odfFQJxUkw%x*H*)c^h#Tcef3o&Xsng2kQ=VUq|1 z!o6%fVTFjJ890?gn1B!jJ=b^xaLqJ~Sds>!^AIA3-X6T+KNHO$S#)%ev}H`EkW3tx zf|AVQQrMM5ZkYoS!+;%;`9o56bpH5Z5AVO2B& z5vOr0Sq`zp0X$lWD|%K8r@Q32zY!ZjfH_M&5fdaP*5}`rxJr--^L=B2170) zNn|mFS_Tan-qmq};RFH`1=H(-^G@wj3jdC8D(f4#S)7mbBQ<_hZdNA zkO&%evEj^(KtNO_i$bj-Q7}SfeWkrn2ozmer@Zl@VMzq`63F{t2_$;cwqd}aNLu?W zBMZV1il#UU&)_mEu+5QR0(le8feECL$WRcTM3JQ&a@HQiB-gSCOfQShb3kt|B_Mee zSg9$Ln$`Jvwjuv7zB15ianJq?4b21AEQ6{;XDwk$5j@r`Fc!@R zpD7NFl8i-nX*)AEC_HVYC~FOrr6_0IGsa&XEc1W0EiOedmEf2mx7cJVGGgNvy`<>L z@Rwrh4WxhJxJA!k$gRUONm!ndE{X4w{Q*3%K$28UQs&AT4)$$rSjW;Ig+c!A9&I`w-aI4`2EN}@$;JTEY^~{(pF$U4f zUiKSSMX)GqC?J=P#wg=(TjT_tjcYKF#>J~4$mxL0r>m%D`<13tgVE+i({`qK*!KRO zSm7UlBhtwX3bP7(%$;Bu83VUCzyyNk?2C(vMTB|%4ERb!-(XetlG$hy5Yr~niMp@vRjQsO4`_oGv` z`b-3c-UBjPCRk7=4h8>d5lEwU$lwrE1cq;>C7*fE5oe}qZnjrm*q$Qx$*wM9uNM4J z=FyVB0DAde;YQ+6bW$DdtVgFNi+wLAk`>Y7*y;k~8zH5~s)#}x+Hk-|cRg)n|+I&yj zZigyd>}*aP{{k)8s2AvB^Y(}jHB9VLX3bgathIXSVkBxl-R5*pTSH(m5@n^CCu6ug zsfmGxFEyv*gAhtw=}^WBm#rb7%-~L)I(6#Qsk>vjW4U92aa`qgfVPNIB=;G&Ialz& z`XBB2EkN*&T53g3gDmgJ`=W1gYnOmsz2mC8(=R5ai(R=eugUXZxX-xFxk3}1JhHI? zdF1^xqA#I%fqLBMc~%20pV&g2-Mln=;b>-qvta_VsC%|^_xX`wvq_^NliR)rJ#W_~ zJKMCoSWiK`ueGZ&N+1Kna#HxYyksl>DV8v4A<%sQI(n2{^$0(GZp%DrdItt!Hk-{> z16#}^@F@i=x52PZns5100VUCB<&(RVoad!Zep^}~X|sw*WDeyNwdbrC>xn2x_X6M{ zdRKL=Ih%i-gCBaFL*3)3{rpfT&R~)hM=b;|LQh2_=znPZJdg+jRreF*{fM5sk)lMC z8*H|ktnGLqhf$%ia^(pGcY8k~$%y?3z770CGP<{KMGnwC8{}N@dHuG|z2@wT&76Tj zXY_0e%}k%rue=9;{skvo0m0A%qfMyvELlV~=#YX)000310Rt8QARr6~hJ+%KNRma0 z9*G|mfDLhsP;5w>3dF%2$YB^pF~%5U3^9ffV-T65p_&S-&9u5yHz1u6gH&i9Pp><( zpOUkO!Okd?Q1)HYG-Zx)ZT4v5yatAjARde2SAf%B1(%ihF@*##J1AqJ8~xKLP;OxW zkK#fU=0xqs!d1V?kH8LX6f(&QY1wIhnhAY}bwg($3-d*Bx*4G9g&$hSkBPY0YpU(f z-1SA0{!?`=yg(FzI?`V+41@Ew)aZo4%LZz*{*K)G#_2V5RsQRJcum0aNcieOlsBJL z_(Rf%*eWwWO^DuJV(L2BVMe7!$-W&1ndrKI95->pA_v|Hs$oKax5G@xfNJ_%QT$L! zg?Q~#ytb3`w*C3}cNMg%eXa>yc1K;V=UIXpQBI9STOnlwVAAi@!D2%4eo&wdZ!v<4 z>=HM@|48@U$$>8COWCzNJ*t^Bd0jzov?<8loi9UliNM=SBNB`S5S`=Tx|>a8H@_D| zWtO_vA81td%>2oPY;~@|t->%{t4!v*l~62rA)KGW`Aco_^{5S-No6iYgeN23cK31` z?D>bN6KBfAhdg@rEfA0s3z?vaSZep+tS*(3p$9Y~)v6Xe_mCea016f{hZ={nfqNRf z4#Uxm*f>${w%CO7rP}c_aKZ1UVb<%jJTS7k`j{d+l+5W@hQ^D5oPq%q!ud3K?t|H0%F_ioyFoDZi)xvMNr|HK(eaFqsHK$-5o!pGqS@S|3S%Z7#9cjlb73lUghr(0RcGzc za=&9HoHcG0Jv^+Ir!psD`7$G{1(zuM8+1FMe^D&8!&8j&OH2#CA12G-9Z>G6L|@-b zfE*vlRBOg(3^vC)TkNoGp4Z9%s7uY#X~L7sm_4>Xl1?t?VP}VNn0p1Mg~)D zr0$Jr5tapG9JwE8H__A+UfvS3+$}|)911U>w$j=CEBmdALqj2jG3+{6et{xJNzGlB zfP4DP!f6WT4x;7~JwM1w_DjLQqIl+7B{VI0r;pewh)qg+JO+nYt-`b6Sa**~YV0a- zI=3M?_Jf$TT>%eE28`}2Cz!GnT!OOcxDK&pM(E zZmj|lCGu__!1#GY7Ipw@xz7kps^8k+9wnlCKdQJGf9k~9;~YMgG+@9ZXgAHJgOzwA z*ksBLWWzq?4oSW|Js>R06Rp_Z5^34fP8?zvJK>dfrW@--(2KSBN@$WKKl^4E4lA2) zBbWzio8q(dTrR3xP};kZ9}CDwv}?<_DHzk$;O761bFgN zGDz94Qgl>ksrRwn3tDFDMoBm|)hhrxgbb_FoDl*sco4JI{*=T;{QDAP!d0EA# zS1j{pfG=V|)Vf~DQ_o1n73nxh`iE-dLj=jUTlJqFZ-0XZAD|wADj-)@(EtpaQi#S! za5_e9c*3WOdPEEvbHPad4sSu7q+9s!*OTPN68Vob@H2-XWIn+zfiR5HW}rk#Hq_-X zc^Q#~@oaV_lQbfNExCT{mLmVkX6ARGPV8G$o4Q-!V< z0)(0%++kP>F+GS`;#!Rd9!3b94!q*{509iRN}HH43r{n5-$F=My>-~DHp328EX z9UcHdqo}}e#08TFV1=FAm`Ov^tc zSRVC2qnQ+@KPGe8GCu9q(DTs556*p-k`BhS{w9f&>P@m+JNuJZslCs*VOnhm;e9Bm zWF#;qe>q#R$7I~bRa9Oj_3w(aG$J^ZlRFF7Tg7>rw9wzjH=2h#%X~hGl()!ur^4oY z;p}T%CpHZ>>rpEqL1Idlq+s*-NFilJzB+l&qGrAqmi!{dB$$XS31V3_37)z+@~&vD z>)0Af#5FZUeA_g+8a929cBVN_cAW?MleBzY^qqz&EtK5^aB?^z3(pr@nb3!r(-W0T z`KNq!ZSs>`1X5{=>1DO_f-@o&P@Agf*zrIs1{iE(vG&_(99hfwP;_+eP)(q#f()^V z3zvbtoq99pObS2J=+!<2jt%=u`EvS}CMgWDe)+{t8L}4Bmgo)M&0$PVePrVW+3+}B z$9%g8v`-q!G0chb>-Qx@wmaFM$pH$OxQ`zHJ1TwAUrQyt%8V}%U*O3FseHm=sevOs zU_5MMx@k3R?*c2%{&iCu#nL#toYd(Z zK`FGmzN78P#ti&8Id$Rp|Ct?}b35)us`WY-cF5~zj3M1woz`WYR{XE28)D6E)up1x zh-APOVdOdnEUc{SzCo})wJ3f3LMK4EMskGjG0c5}^|fhJ=vkLY4O=`{`%qq;g9PvK z*_G$i@CH1Rwksh!6Y+)q55L=k8B13Tk~Q)Z%iJ&9Sz_q~2DOfD^%APuku5;zJe@4L z&GpP%Q>&IGE6pOtXSX$*~6bdIBYOY{ImO~*q}zr2^6t0 zRe^TU>mIKZi^aVzKJOABCkOWf%Kf)9!a%FK#Re+Va_(E7pX~aIqR=bJl~wu+sN zHdkgHtaMQH_R4n&0(oXiYX|N;K|x(%jixuMYJz;_oNt<_)xtj8D)+|kEoHiGmk#Cn zOnifsLbu8(XnNy2h0*7X_Sf3+h*6$PU)@#Z9Pf=!5ZsI|8N51{kTc;YJTVdu!P5jJ zSq_ly7J0Rk-}n#3EE%Y{&qvQ*BL^L-XIo8 zu9>m{!v)C$vi}0us+lUnh(Y*n$zVW3S#r8r!Y7ybo8Zv{;?nB5SbmqXPsvRQorFtB z;pG=yu%JIwne_7H%oar~{c7i5PQ@@sB!A zy_ai5qr~!@0=s5@Z1fb-{I08s>Q@c z5_7lTq;hbRVt^TRj}8L9p*yaNbr#sYYXx<%<$bhHc0!qDs6vW-3(o8rj_11q(2FYS zAB>*)o=tWpU2&sHDOYL*JlGB5o*)b^u7(2aE_1zu#6A9zBIoF#?Dlv-`wR|6BvOkw z+oSC2!xa$sjLqI__i;;+^nst#XKNp7DD^bB30EG-b_fXpwBM^a&GsyrK4(ZOttqF@IoECA`?<%E8h8u1io)EWe>&Yu1T z*LdwE3n+hjZsDV{97({iJ&v178Ic+Q85{CIUxV9Ht`Z%nK#ye#a;m<}K@6=YiQ9k; z6NuwTJW9tCk1F-1`|*H)6`y`r>lP*&$ts)rxNXU5431R>M&~8Xhc*}-nAKwC?hEFB7vSnJtmkLR=r`q9-(+*hQewOM|&BHI1>LA0w()+=q8kP6JKG>8(6 z*)CK`XwNz{VOu{jJP<=v55g*K!i~WmkktqMM^PPdgK-6B8jZ8?`Q{whL=!5I*FSH^ zTKFL#j2lD}p#$QJdgds&K(Pw8wJ!~86PopKVDk}4Sawce-VWik2MPL2~4$U_7 z^};SRBK?8*nouGc4B)MaF(U>axXu%Z3`|U28|=63*8h&6=U7Cr^iwfNOS#-alSQw& zOffb7G$(67eML^E<6#pcle4I28o*HQYnenn(9buVPeP2;rmG%8C>WfQb9s4kE?zrxNAED~S@7URKZxj5O;7X^;{S`n-=ny4E_{4L%gWdVLE5Twu{K6)44s&wZi{pvR4dK@$d0{y?s51%XPT ziuK~A@#S&Xq0L>?0!VPDoedlwVVXg{aKq$BakF4Pgjm$bZnVx)!ler(RP^F-&19(f z{JUTVOUPLY{a(D%Gu7Jl+lRq?xScJJcJ;m~xbxPtTEhi98hJi4 zDEyNw(Iie(vu;bY#DK^JVXKWP2Z{74nlaSmwn#gVBcv;Og(8*FsE4VcNt>d(38xrp zvWxk*;|I>WR=y>%3B{D(k#qO$MF@t&&3Bu{d>z8e&epuKuQ)t|YVMgq<;&NhMZybb z4c|WLNYjWk)pa{2&mhvVyCWVygvYr+J+j8(i_aqyQCF4yW(JWh%lT~Uc~XnZ3&nAj zu+Bh!woq3g#t0&&EBGQ-DbQ3s#0tjN7KX#O3Fh&5FgL#j?3t88wmtclsTmibzofVp zwDNd^+n=Df)Jr4CYDz{92s#%-7>N1ci|H8n&t0w29Lq!pYfw3U=lybky@gHwP?Jh> zuZ@F((wHJxy9Y+jg9w?7&yt~ba=N(M$FK+=AsywB`hl*F1T}wmwbNT<9l;`A!d2V& z+SOEHkPEWR9Si7cR$&FE&Mw+DQ|N$Z3MuYmi5~zBc7HT@S6p(RPxwG+7rM$&{dh-- zWl=6o%Erd{dhaig_r7HgkH(jKGNMqoET*6^eG2BdTf+*b&clcopIbqc;6S|$D>@+~ z>o!B^1k4O8yVsm{SX!3W2Ps4@bLU|eNDpjA2vtm`}D8}=b$ zHw@odNDn#6{NvN2>8$c&{lY1su{2X!6A# z(`N(Xf?5)hTs*z?=NtrCN6d0Z_JcN&Ys9Ei0Ws8AzfcWw@R6G+%iB(acn$ai$7gpn zh8#YQB)SE%B_JE5_TQw#3y-z2w;U%+OX;W_fXO4~Z4H}Fc%kkIhI3PD@;f;lwjcpm z+PMw`m3LWT{n4b8wA8<*8l=aDkbW;AXu$P-2aa~!#_XP&%7BNP%IMbx1OR4p_t0B9 zH3Jys&SQc8{kwyIanQ~l#qA@Yaku=qx^_MAFHk$LdKoaH?fpq}Vg3)0xvxn4Z82Xw zp~7{flpuLfBzZm#SCxWSQex=es6tIrH{~m)qNS?8_P&qFvgaJy64@ELi=58K?_kmi zNJ88@v^V*4+&X{$Suceu(~t#crwCGrGXVh`U%Vz026jLj>sO)K#I23e0Kt|IuWVQUF9CiwnWH`Shk{rp8nQ+aG3b}oZ!`I?d-J5n#5)^N|Wh{EV z7*;J_oSV9h9XQ{afezL0Hn+*Pe}IDX1}vqPnG|P_=W-bvy@jJj?}a^K1&c^*u4Ipu z`pCGJh^eS`dx}72(vD)u&~aYyHA4dvx;U8Bpg20SN`kqvIaRp<_EVa3%J2f-NwCLx zK)*PcaY zKdOhhdmX03LX4lM+uj^_FGxA*GO{Hty?a8&sM`Y)W5=zI+UNvLM}gRIkT*fl+FGda z3HZlwWiv&iT3=4I*O*HZhE-Oy!Nb9{xhMn^b=RcEK#Zgc;7XNE?g!wr_^Od&qUHLX zYwt~=-+8yWfZMw-NEEnzd{;|;-*J2nhPZ}IU=={-K2s|EJ;&D~MfbQ}n(2Q{qOO~v z$*>b4?7DvxO^G@y!@uF!z`lxSe9W2fC5nKi2`WAXpE=HdP94W_DCG17vM71lk&HXR z^mY?@I0c;|mi&OCaJ_r;v_;cTicuf8pvYNnSEJ$0osDSouR6?wI!pJ}H<2!)01K-{ zfG8f}sTr^(sF-6U`fl$imEnitQo#I8WS|~}0y*&@MDN+h+Iy702!P9VL0J!kNZuPR zh@kxx%5I!xeCdd+eePqBXk*w7zGIn_8d1d|xIFTHvq-&fIm8TbV+hYm3dK!L=N8SJ z#nf9Trnc|8ivZak#1AN6V%+N}XbzI_I$3nwN{a8eZd^*gJ}uV`{B2;2cUU+oN&}B> z1S*EMSyn@>r6WS7+#S?P{l;YnjaOY;|Mam}j)s*^P&tW-StFn-tc;(cNr#(VJr$`Z z(-t_?w!fpLe%K$43x%yJDN||$yq66zq&;cS`~nMjOX^;yWcco$`VpSR1n4Y{rR{=DcA@`(pHh^N8hyKH@mM_&i#%-OI>ObR7s1i*a1c8N+Z)0} zhhrPjrAEa~g76zgAB`v`M%gD&5j=qTd<0vyHz4QY`7A-BV@xmKnNNQi1ur6l4p)D_ z^O0pVJCj5y4w)=Wi@h!!^%(ws#7rv@e^~;JJ8fR7BD0?H_arFx|JaQ-XXI)n0^3;O zF_H#9Esrvyq|6H&fFemN+DiU$NLJjf(3@LAV+hgQBNT5Vbes}8EQ>JI7)bOl0?TYA zRNglc7|^Lt3?-5EQv~$^f&v2GP&rEgELM}9?NJ6?d>i`3pguw1kdM54noF zh{dE%z_Sx;@j>4qH9Y0A2|n>+MKWePEIg&B>WPn&GVy5=J_V}+d?0mfT_V7WyaKR3lCiL#MKGp+81aKuXV~kBZ`*ZL)E&~9N$g~% z2NkHdyFj;aY(Nn=GDTmn*1`fFvajzMhAr>E`x!)}rpd4o|DhgWYYc<_1fix95?i8^ zeDsnbawbfJSW+cmONyfU&$)a|K_!dus|or->$hNrep^K!JF0rcdt4PTUOCROGSrd^3hGTUvVw}>9_A+2^~azvRvq$ ze2|-uEp7m7zY^Yf`sG-qUr=E)w>h5k$l$J^+Q@)DG)uIpf7vV#u2CCL(uAfdZ77z| zhBC1Y^Z!Y2kry9K@n{-YS=867qy6nf*m)< z6u+UPJ>)SA_L56$BD05(`xqwQ1yrlBhB{iAL>0x8tD%MvS$OpfRTf+U3|$40O1Ngt zTcOdk5JveH@KcT%vTDCAPi#Qq-r)oowD2EdQsoSn>I3Rl_IhT z9~CXkO<9Cqwfzr_;`!>4syT&f-m@sqGNRN)3;abi6ZHh*n#y!YR5g!pL>(RD%+H#w zAf*dg9-H|S=u=J7i|zD+8uiOI`*XMiG1k3KE#1){dnkRg($hl`KQRB8zZNaQj zpGNmt~4{2r#{jpVYun@^uhoElS zQq8^GB4LPNN{Ssgbe8DKS_lllLMTyWp_{^}oOMa%Y>bDky!`c_X@JZ5lBJXZHCX{) zxoNcUGio9~*r;_nDKj@v1ECa?Uj61q)d`4`PYo=Dw8l*x`%|AaN?iiU@wA87VpjsX zSoQQMFn>%n0l)UFV8sa*K4VB!Tx<&K?ak2-XHf12>3h0UTsRB(7&Je}qU8wfDC*np zp6mtGk_U6t4MdfycJhCReXz33dqsz5JyrQ8gRC)XgW$^V+o;t&9^pPbxKaJz=w5US z=GC(dhNXd-Z9gLxwfQSWX!{A(pL~CNL3)B`WuPrMEm+6zEnUy(32MBiqsM{nKr2|I zeFiej1z_YbP!I+t1-+4^6W&*L?01LJODmQ08JIO7< z$Wppi?x7m#OXgbG0=^n4TPAvLAv%{n1gnCEXdfEY?LdSwJl!l`YczmRq zIIsikrP(8$-r#c&MV55~CZ^+?z)8jnVUJBYF_C!qxW(wrx?+^6txZV?`T!C89_Nem zA#k?XXZ;&U;xoDL+&(fv;*YbFho;!wTwQUOaHH{Up;G~R{0MbUh`8^gE>*4-Zv^+X zH@QDl<&{;QUJtI1zOjpg(LawVgM?}CWFiFITT$zmtk7w-C#DTXk6E&x8!jN{ziDig zA;k!jpovY0^i0lG6G#^%sv7EzRo}0|O156`k6nD^whTf}Lk=<%-qN?I_i#GN5oeaF zn*!N5ep3IwSpNnyKD<8AehKAmoAD35ac@^P$8#d2+m3#bEEw46NYOWHxP*zW#3_*SfJlK7#z-NIfJnF)4!HCr6uOk#2ju0DyPb*61XKs_7!yhz zxBgIJ0AU^X63kj+Y9e3>z&ms#PJs4K_wwFpYri1aBGMq4UL)%yhSp6q-qbM!%% zXB5UG7H|!K;8P7OT64?U90EP@aFX|OwerN5o%lZo;RIhh+D@KV{Yhz^;BM;A zu`&!&e@=rHnoLcVKnA12fyY>J-glLt7+PFbsd0!qbFD0Rt<-C@1+B0s$j}v&cqgO) zeXdK>ocCrl!;;e*F5xnG#!X!E{X7`WbFr=8fUkuMV+ zCFOD;fA}z2DvQSW#1tIF!Y5`yrycM@Pn7SN%(o4p_L4y%(?FvkzyN2{ma+FB#q zZ&9xB(y}RY&U}Fl3NoW1q8vN8VskZm;J1^nPho0(_lP`-$@# z3-lLmBXK}1J6&ovx(%O_r!0?REo+>{r>g@Wl*N>L^h^|%LZe=EPHcimA$7#)YYk+e zXV`qs|H6~0+dMNpD`YcVON--nBl{{I0|j}^zREU&w>A1XDdmjBz_XFs&NbV~sxO>a zmXWf}rvElxD>))}9b-o$AEjVl=JZU|KJ*c}W5YA-orJZnsvXHRat`SCwW@PaQ%qN;T9ly-(t=Q zoCdxBGzE(jg(Ga_?STA9#`RkrCYQ8E^MvZRMBD66%1HQD6U2(;u1p6K+S!P%&;Rt| zaDHpt!y)0fB1*=jStHnC7vmbJ!1ebkrpB}9XyeWR1?EYDP7==gb|HA9t0rfPjYsx2 zb+QUz{coY9pE@|-_n;qEu_g7|(iAr?^x*p-msj?~#TW?cqp}N0ti|jT#{eX_hBo3w zd62$&*S@sNvg;=uhqqd!WBU>VV@P|yrJZdoz-ofS*x)WNoedzjH+tubBTi|h>cY3& z7rBj6xeYg6oGE?%l*|$9AYEH5r&+>8yo>K}!My*IM{;e0R1XNI7Wbf*rIWTB9~vz% z2UO(SUYLDw_*mLKb4ng;-WAN(xMy5D)3T5iOCfaj0C2Tg(sPGWvu)kOi$yvTt+0{o zUT{4Z)+Y^5p)=;U8PLX^rQR=zD7PXv#DOuu$?NP8-q`_I*#uVCt8P>rFRt6#M084K z(H+@(;XEw1xMp(ucjxP-)H;jM>q?WA$-9xV%I&Tb0OYB^ZLUO(gGI~*6*{GHv?Ax# zqG3FlB$59fssR-cABTo3{c5rmEInse>P*+xoLwJI1*%BWW6*ja_r9T-ME5U6f<3so z*CmHj12kX{tFT4O>Tx2%{Z*nN#GZ%;T?IZ_#4n93so}7X`BShuq~^bLW!^)0Na8X( zutzL)`4f-JCn9I_MI7ac*l>{L(~g~V6)d6cNw~Fe)_*;TYgWL2d42{k)-hsiT#JYY zIeHOXhpu>aoRSl5AFs;)0ZL#oUK}806F4$PgfE}_yi_^ZrcQUQ)x419u16ZEG@^@a z+X*oR#tQ=#o85d*a@lNsSth6~PikX#7=G63a9y@H3_)6jI0ZxdDrC7;3I%VfCnZ() z!0pr(vsPV?!^Nh)3NZrqmds#<^xLs^Zo#Yhokb}ueLmiL%F$~f0x#&m)}22Lpo6W+ z>a8T`P-tC1l<>x-5|UpZ?K>Jt}NZ5t`AtUKh%wWXRg zUS_f+sy0TwI560&69ofqS+3PT%!?4?QV67raZCkQZ!mKr!ve<|pKJ_+5R1Y8GpQwr z8aZVu=k6UNQ6us4lij-}x6rOXo{8HcgcTA~menAv)HM{7^%=uQ8BG1CfpoSnI|RaJ zI4G1RC{dRisC8c@ITRVTD_Xyl@h1=Ql-s0F@SO_m6k3_)h|8@S>oV;WRoS!J!x(cY zr)|o1SN%FwxhnE*mc|{k#b6TPhGnjn z{dTCI(tnEs3QE3{=sMsWrmh!S8YK&QFy2W1&WONqUMQom){eCWAf*h~q6|oj12aqu zGfHqTyuzy)#f+e6=n!lERiZNs?R{cK17`)1x?+s=!PdNu)+jr0-u%kXLEPQSdJSq0 zvwQ}y^)8b|GdofE{QN4POks>H9T1CPQKh8vc^nJ(RFx_eRTGUagCEO>iQ(D-$!h%> z&#=x0R?EY)tec$(F}k9YMmJpg>4kFRnn}haq!q?Qf;{r+M$;6k8oC$LIxQ}qX!AD^ zEyTM2Y9~X|FFPK&v9Eg$HB-aWuyyI{{=-#i6<4~U`)FU3qyR$+V;oLj6v>uVJq_>% zCn?DrIeu-m7&4EYsXeN5t9X6>I8Ec-V9aL>m+<5z@8AEMR$6?7<>KI6>4#+_cFl(r$D3c zc-sI*bgdJ0Ms_Ew=3{OZE+Y{Qgw-k$x=C=-$kYt|4SV!U7mhwB@>KJbb!!?x?1%vDb1Cd(JG6%3+6?G90VBlZonQS9td-vK&>+3dhs%1r5k)&YwCrs}t>5(?uVUfr}W^-5h=c-p_p=|rx=vm%i5B6}Uv=u%q9`2EsEupKPx zAnX2-xAu> zLB4BllwWNU9wIZ@wd@4}lGDRW>FSEmZcdoTZ8n}IP#>jkxNPbxuGV_iR+cF!FtP=* zKWgxs;4LEG*Th6I+4pnYs*19}S)QahYtqOFfQLl{LaH42YwqEg=UAdVhZ*U{HaL&T zE^uN`VvmF7V}m+K9z3etOg&Ori{diT%Mv9DtGskvHl;&KuR~~05_RoK#ohK~j$L!S zkNS6KBUlhwO?wnO?H<|mTb+Vq@;r23 zXnscnzvSZ=s{0vc8bMzqgy3@-GjL=RdioDJ!~g`lY2ck1cFn_B6% zpcIc4=+IfV^16LK*PLraNZ{trZZO;|*(PGv*1Iu#+Dw$r7L{nzWLya0v&4TXe-gKxk_s!EX#SHa7ziRGc+yUAJqBBUPTRWvU2 ztwpr9kzd;c@&rSp%R)19Pu8nC8L=(F_z1-o**Kiw%;bx)dpew?N7 zRQ(;i_1ypxfa`A14V}}4%?s!&puy?fCl5TawHA2O6j6(&P;diB8S)NIl`wS;#z=R0 z!m3`R_)l0}dt0mbAV@ehw+j`DWlM8G**pY8O-DFknRS6Q@8`b>%%po12_s0{ziqtp z?$x}|xy@MdymO&~AQ_f8v4WR0nB>=efu@lq+}UxWW1cN*82^fb=S?++)L9-suOo3Z zzy&Dm-DV8QtM6}JH!1oRNgbhOnX`{FVbJ#`a2I+Mdh$TI1&l+uM(1BPyAxhO8^FV4 zcFH`~ttygsB-@gpIj&6cw<1xbf#<_@sS9cFbQ!yw8b(qYDvOduQ_ul8^0m6{*+RUi zivTP~1Np7G2$Cz3zBIb*H&|bx>nT?^^*xlv6b1w5{pr754g!vXSd^;>=)_qd39{R2KeM3+7s zj0!n^Puic*otAwtu8D?dA`Z-FAY6v8E+FZ;S*fbpjnNt8Fz^xj&2$eP{LaXgHS$c; zIGaOlG|HoeW6v|&b9>*~V4Jn)20v#Iv#d%ff`SL=q>dC}5m1lw;Dt7tikcZnB2vlr zA1v zeG~NpE?E^UD1f9bTdfQ719F?LwJDy`(}Y1HSjV3bLpDrkkL5m@=9FPHoEbF7q-V}! zyUc3%t6Oglw>K}dna(n~sr!x~MG~M)1x=ldD+L*i#a!{97qM^Bwt?yY?9kKuT zvI#7CdnfQjeM@_#s3f$q|0^% z^(b?7(#vXrqAOiF{CR0e!M91~@~`n_&cS3D2jI|Xi7gO1~Fw)FpiLSW!h&iUuu z?}{rpZk&IzJA@6%f5~XVoTYs~k6G14v@Wmm1FSs3&9&6lif|5t9KM#xP9~?oP3cwQ z$3?`)i@qY@jryoM+CU(k#wi=J#sok-K$)(Lj|@IoMI~LvxdnKE}q!n35&>ZF*%0?V;>zY|e$;Z6z zfHc7jnd(xLzPIP!kd<>E8Szg^8L>ifVjI0<5|Zi$w{Orc${k5mYPff22TXoYtwxLd z{A|@{rJ6(Iz7`>o8V@hf3;ag>&2HW33#dc}Mo4shvI3zVw%K(7@-JXZTzJ3)O9HzA z{+dQMcyv}zdY3CDDzV}XzcHxHA48UymqiR0O4?0D#?@KT#+oc*&wZ!sFSst#An8Z3 z##x0+A}CM1y(8_Y435OU0l~3KHo?v@(l$>z9zs@2Cx`HkauS6)VU`6xhoS#-jHxs; z+Mhx+T^rvC=3@FU3j&$Tf&d8gUL|Z%LhlMQegvc^^7vC1;1KadK?6+g-s9nm%-D-8 zq~EcaJ{S@nZZTYwXl05gbV)Udre1URCgzGh8PT`?x$_l+!H{I(@c%L3W1~ICC3r~5 zZjQCJ3dX&~fABuI5Lud;ctu2O-!VXX z#wDl`Io8Tr7?l8nPgU~F$QEWm1ja0!_Vtj)V_#dRJ5J#6F!arLOh2(Rg83m0mIa07 zK;J*PCT*bFu+qR+N9_js5WS7XuVnB_3IOaaDLm(xW?iJ+DE>L26J$K6lU%P_cS zm*<+9H&7sUflhXZ*9Mu&$i&8)jufT=R_O%Hq@++Y6vZ}Wj*Fx%+GQ)nUsV<+L-VV( zj;esT3hqeux@N9|`2nZEMx|swUdVY?o>370g{!z0*T;zzB!HIni?a-Tz)1t2Z2$CN za^`)~g9(?MwQvPx6=5IaFUYoZ0%npx2JyzycfG!Qa^{q!P2WiqheVTV;AXVT5e0mz zs`-jNf#;Bwdbq$U8l!^SYml*A`3N_F2Q)9Oj*O0%3{NlAqO1{!7yvE(`soXI!DbnJ z=%AD48^Ya&DcdPpz#)rfcj<&C4>oXg@eqP9> zXrAQr{=-?%*NSO>9sc4)l#hn#_lZrQ1yIG!xjkdlZzFt*mV2(ihmlNHGtWh1it$}t zEinS+4=|t9~eOQf-CSiB4|IjgD-tM z*g=v%@d~<8>$Xn;>i&_mDp;N^QW(yJ$s z9!s4;NY~;kp0z}UH;1ajgQKb8ycDO!g6ru>mp!`hzq*G_h7FxS7SRDaG;Jg{>TdV; zmM=D1WK|=_)TWjkq1A5)DB!-`cH;Idoh&gYce7mAPGb@RZZ~QthLlPaA@Uvz`GB3x zq(x$SqSS{kqx9i#sAS|ztKSw`-2?aay{-4hwnkFshPP5cN~$NK??z*+N9gDQ=9^j~ z!AJ5q-*1{9iIv)oX*JyRP(u|1 z=Fm~!BV{&`6Rt|*1}I^v0+vBGh7j6d0)Gj3OCN+a8Vs`k*zO-!H9#2Gi;R7pi{7g74-N2KL|n1(0_Exge`Lu zC7YcT8%UHJSxZ+nT?>)B&Z?%GB*8}GF9Io^m}>5>ED&N3V=Z?cGO@{;Hzx(Jvrj)u z5$tGDNtndwe)VNaE<5OXU};47vs4UP(ndjM7nRTze(~r?f}N?=Fb1SXWIl59o|~$h zfS!GNb;0Su?2SIPO|6XR9fNklIsQALpj1ArJd#t+guX=!2l5zbjt}%596Fs^yqf{l z&XRXx87le}BF-M|U70pOqq zks+kY7pwTj;7*k<2Y?3DROQRH48ZZK@?{g30AIY$Wu5TZR^>Z0_7i#34q1>a8r80+ z3JT!tnN<+QXO*v3q^4nyuc=hL5)}<8`rmMsuR)cefp+Sw+o>*&L=UA3p}l?XT^{K#38Jbb7G?WdjYI`YRv^xSqOZAq5wzG1J}CvI%i3u;NgGKzp(OA zoAAe;UWd1H5%9`DH-Xe4{j1}#kW0E5U2jcHQnkMf;=(ubfrl-6c09`zH&B5V>XnC> zB@-*3XPGxX)x>Ra+q@YopQs7n{WH6yfRzni9JwMKXXJz&XQ&4$*cxqo0p!#kqa%q! zm)>xzRY#{*h!HAzw4|n$?*vNX@`Z58yb*~l1kL9gD&$)P7F08i*Y(E#g>6QLt2S1A z?m(R8<0GY#*vhA`f#>jak9^@iz&p!f-8 zbF3?0dvXzdME`(Y`EW?%W$Uhdc!KP8>YKtg+1=k}=ao-EFa|}hd{}M=82ARUx#kof z+_c1Q7Wng(&u@Be9Xt> z8;0^vUq#C?3D^sU7ce5PzGcA2XqP_&5Z%LHtTe0zAG_}DMD?EECN`%l!U7hwb)F2Q_ zpIfe`I1^@gkV?ORYIZe=yn zr7LhfzIvy#`14_Bl;TNkxU#Yv+zTOJbSWRiQYHABE4kxt={ywZ+}Y|WMWGn;zUG3< z7JEyX`iVJdZ3Coiu~0g943v(C;@o4RJn@La*2Qh#0!l=i^Ff^Tmy1xo;jT9st;M$g zCWq$a65&!!%C?M*Zuj^BTcu7?5@SuCH91^D*bpNIc+O?_T&+DqtRZUg#(-O9}WQmHgH{^WMXC9OK30>7fDb6Ytc+GVwbTd zoqL`6KCNbtD(spN3h7KC{@!Gs286|8k~&q)8bTh|s07$RcI_r@J+P3&%Y{^tEfJ$# z5k_&&JnniGjF_NA?;oP$|BY#Mt2;7%F+8*vYoW10OBIRF)SD zOd`*ESV`eINCIJE6vm-Klr$hIPaP~Bi4q1NUKu!Q!p8(RdD9^p*nhSJ{Viq+#K0jd zb3-B1c(1AQ|8e$gcg85TY0Cz;cg%g;WMo3-v27buU^sWn zvV3Bzowo0sS--C&a6dMrNN!DmoLZcR32uv0KtR;boL!8Jd%1q zF2sQkqACdvgc0_!5>( z21X43q!+I<1;%AX>bAtBko4LnjZ#vw-sh}-6tligP!S(Ea<}ZwLm)2e2w`fw zyloUTt^|BrrTsNl-Cdth`W+mFxpF69`Fvq@K_xOLDkzN`P8R`_xJI>e=?V^PF;tDY z?U8P=je=9;t(PpS%^=XANI39$D@ZSyn1OT=#4%U%o?5C)?fT(1Rg3|h4A+4W7pKb& z{5aKAqpW^p=IXY5<(*Jh8+rOcbfgCdtNc+%cqtbsqJnsTxxYoSp1*Ayuf%eF?MyKI+9Gv9AP@LcGp#;| zYddi0#7kvrKUK6`W_7@}p%*FZeBXJEgi{T>L*&q>q)s9N zhoh~~rA&nLCFU!(d!)!B^GJ7NlYn@;&Q)+I*2evuCCI|N{60|vPV|}}e3CE{GCyEE zY5!`ZMCp2-?N2a6$F?u1u%O|KUw*AZ_y}r>8s0cCYO+wo-FcZ*aXl-@M?QR&?Y9I6 z_Z&yr{LE%gwm>?Z?=sPKOtxY~2^M@_ zLoXzv6h=2QR29)irFu?#dP5_b2P43UaD)+hwcmp7p$T1vwj5IyCFnoH7GoMa#sO(y z3hL8~(AnXNPKME=O^vV9gk&(BG7}a5{=u}#Xu5~*5t>n<8Qph3c#e zO_H*ww_s14Ua!VioSr+V(YTr%!3FJ&xRg15bQy#WjfD3B281W512S`TS#C)7EkIv} zZt5{*_r-|`s@^GQ2hB(n2BZj5i-EJ_QWWT zt`mI~XylBh{epP;r*P(1DZgh?4_=r#A$P8C9#dw zo#mvev3N$z)Sph|umBql(m4fwi&xJH_h{fUOrOOp?ci*gSa)U-T~j$1w_SCv9s*>c z4CH$#Zi{({8cx^WWUkIv1wlz5vN9N&&PQ42b!886iHIrJrO~~zObB34YZ?a{hgdo8 zFqKB7rtycp@d!&<lg%rUkFXt}#? z!Ac~15akG0h{v6ZEt546Sqm<1VJ)*`c9~QX$-AX>T4tZN@b1VDCp7QKU!?Uw_P~_>|Ks$^e#-y`3z&rKL(*UUQO1#Eoulc0^oBI@8gVOj1PDD4drZ zy#oS=c4Z`!kQ<4(GE0pqzk`MSW~+Jb%%i~2@zTH}*8P^z>p0Fnh49BAfN5MI>D!&h z8^~<_CP`u!5klO1p>i?pSG5vdT-U(lFSQ859L*3Garxsalp%fXF2-$TmX9O`enB zyOFkYCgw~7F7zQ1KaP}_K|`6?DM-PQ4CG>-3R>xiD&~(q%sz~@r)Dh*k&8Q#%kvyY z?$$E=u7mVMc)0ABg&-VT7)ZR_ta#oklQHme@=AHSO}kz7lV(MWl^@II>xpa>jdZwA z#o{q`o8j2Zb73y0+)@&pFGAS{SVmxPO+}nIX}Vz8BnW@SPB50RSM-^c7%{nH zyog9yXV8UG3Z;!6SC|*-p*W#=xdh6FK**1~kp*XiwB8LCMX;P&E~3R;8m|j%Z5O}T zT{0PZVZx7Ntj{46gfZes`;6~-;gLt?SbJ<~0<^ID(iye(@`WL_l&74a1?qrF)N7ED zmo8&eUm9hwdX`&n(Po*w3=AO|j_yp0uWyG~jA5WpeTo|ml%#4itS$sy=z%R1uWaD0 z92{C0zM}@!HXY%VWb>3Fw*xC8>!lGnmiA-4yOJ>R-T3H^I~pQQ7-8!7{fIq@FPPx< z1{sLAgNVD~)ZN#2rN3S4&^NhYr(pfDc@R+o#EM*0n75@6B?oFQ@ZW*C--4U0$2*Au zVT1MGo`7M{+Hact_5rSLSxySbnC=T={Vn$MfwS)487Qk9Jb*cHL~Slvj1?D}Gkq#y zabA##M5I&BYa6Bcc^CVyDCuK&1ysU+*|=^-$ar76t3#fzXC2wD< zl@`$u4mqLf_*n&Xv_=+roQf_o^Lz{BeO9aFBK;dKHvFRgH2}zHnj7mTciK^R-sz4jH zc66~-K65q##lk(dRXfAWG7MRA>9X7ut9$%KWR+JyEZopxt&bp$z3x(0^@+CP&|l+g zg=t|`a$Aq<^^kz7{gH*)Z%bhgLnxzZ!G#;N-A@}%P$bw9@`L4;V?P_U-0`e3OYSnw zt4HR(q4=0*un|qTC}*>ZSf1a3h*;+K+ES-4KULi~^OwBjF6@AlC9;KbLGtGswGF@o z1}PpKD6?RWKsli}&3s^B84 z1E%DOBQH9*Dq%!Gi$T#0df&|miDk_!*&Q_|lHyVRjkFUq@1CqQ{3Az=d)9?txnqUW zQdUj_HKCv*7>@-cAc`TB4&Gp99CsB8K?KZr5fL1m_K|YDCr{6&gh0pwj4D+0fU05s zg-6vG=0ehzE*r>8pO#I8g72(qhMqh`FfF~TRQOb&ueqcW2F=u7r*PzNWDMC~RVVhT zx6uvcdux-dLD5!FfK*Se0C;6WbBnvD5Gt83B~V+l9RwY%MnS8?@pm?x zWOd^g0`H{dFGmDirMLN^vE#9)zfNub%?~)D`R22|F7SU1M_~@tB0{H%mRg@ipSdq*%JOb_Sysv4O!&z z_0fluGLNSKF^LnXO%#RP*^b}@-2FU$Jcu)K8_qr2)k^U+@R9iH&G6%15heBEcPcr{ z*k--khl2Vh1v=jnxm?%jRmKYK(w>!J*ewr5ZFXZkT;t_x3hyK>^+-o`1)rLc)SN=k zZWjy`!OWRv9-=q*9zfYxc|HV$xv5S+o#`bO(%W%{bIlARceL!3*p~&nPp7^HS;yrQ18ydq*^*m*{nr0iG{fCjMBmUv3jVfT4tca)hW$iQl%ii-|j z=HxKerlfx&q{n{74*wBmH=j_Z2)J4>^vg>21`?&gw5@{wM#~nnXF_!G&oWDhT(mJB zI&(gdW)pvz`5K>$jeNu&&$A_)?Lrn@QArMl{X?FQ1-FDK)XEu7N zeRMlFzDnKj3FXlVcvSN)g%_CVfWvse4{5s&+$8Y)#UEj;k^B+Iu+iX_jyQZ6ot?Ts zMuuO^?F7pO6K0QzXvD(D*wwgrq#{6Dr9yZ{@;vpvr&smFSUt0;fF$K8Wxz6Ihk{>> z03=>Mp>QY&+z7*pUI_BRgQnSE@$%Aoh$!Y>*6E1n>0ZoD?l4Y1YiS0O~tB^#C15b$Y3+FuwSmp+$jbAr9 zqA!o~-KbIx(LZ3<>yX^zA6$fuON~hE!u=Q%CXtRyxRDSswOB@ZJ3?oOgsh9;HPxzk zVk8aTLq@4+bp9KrH=)Zwrr^6PTwBxO+3HP89YkFC)~M4?08BkogD9XF`Di6?`u32$ zzbZkaKNe_KXO2dT(HjJgIy(txWT=q$0!nWsJVg5tWJU!>Xl)em*$J(o=S9%GuSn3%9(D9v(Cu?Ggr92upew+|sC~8xZdysI~$lo(&Vp zfc+wNeh~7sYA<`I)i6)=oq*RWSTkW?8}=+J4Q|;ZEPCM_)-G6+(10gG(j(NR7EWFo z+wm3dSiZdL^R7Q;IYDen*2_gblQs?dm^ff6xwj$llf5lH%>#xEN%(eGH^4N2@HM0h zSitOX7sTtDei@PIxF&7Ju8ci?;4fj|rL@VK%=?_Up0RpgIK4Ll(Y__;R=Hn^H+)>> zC(7OMWi(Wld_c7ak-X#k7{It5r$-FF^MM_j+WDqIEO?RR)dk-y1kl8Vb0GA)$(*7n zAY?vfk4%+d-gQXbu!-g&mIl3dIrgdU;9s6kahKCqY`o4*3uD_$^@o8R<3P5?;H?6w9ipPJn@2ruUDNiXH(5 zQi~H{P9o$1xvAp5lM2?b`jX>&qDP2X({NgnQ~J7%my1oe+`v|&qSx3)}1k`&U&G zQt|0(U5)I)o;a&d(EkSN-UYiipKh52skwlm_?K`qkMBjmI`Xfk9Qd>Zsg4tgg8ss1 zHtLXi!~&Ed0(xaRN?}mWnNbJXjWpNHHmb3QqEgvOcct&S4pKvK01z8_zzws2vy>M^ zBZ)bf_}91U34Tj{xCMad{2cw79tAjpA^7O)8fGEj_@ms-44{cAnr{iZUX-syZn4QG zdv7UaN>T=j1ds$^1T8kKXhz_e$XWhHp9gnO@^$lt^PJ^NJQi-4==YxZP$T4FIvZYh zn?Bj?&GdS@?cwe18x-Q4DZ|`$+Ai@m!bptU5i`7g?8yiyYQEdO-HTeeIMluQxZP?u zRGSq8$aHV_c5nA~Z})a@VDIAY%~_sve}pLnDVS(7n3S+VfsRQEAB_HO-<-FE?0%qO zgK{+}C>M0UdW*iLdmED;VsHCwtzIqI!)?;U%U+=?cETZdau5!}b9%nu6$q$B%U)!o3!|gg5Yff0uV=0d(16Q66 zFzl_m#aG?>|Hu1nsINJazv{!R($~PRf#BCb@N2fORnqSM<5Bw>5KzAM;(i{x4Ck$V zZM$2S4z-$66Ah~*MI5oB(P*nAJ0BVm&nk%!J*;RnT9Xw8N7X9pRgl$oSe*_kal$Tf z^(0F0u=4*;3ZWpOaqp%1-b+-p9f`dciU~KE`q&mf?FMbXKi+aE<5CJ{P}S|nZ$Uz3Jc2$WWVSZG%*q^0o?XUi>HgA+1 z_Oth~^gT=^O_Y-6XC=-1u%UUE4%S)=Vq4wE7%lFYrdB_<@1`VmL7Q0nW~CF%pgc{8 zz#f0s1&FdglZ5?1kH^N7=m3n~tTvb=2o!dS$69}SohTNz;Sq1&0U<;N2ZCfs?HGcz z;DBHN^0R>#T)2%i-<);}8eqX^g$(?#sW8%)N%<#DS zXR%?Vp}7jb%GKI?zYh%Y2{&JyM5<9E>aEY7dD`ZSl%74a%FHS?OttF&!4(wCon{tH zwU8lyd7&>hMTG|!0eiBvw16)t8!4AmD%HZWl1il*Q>m0nDg~8Fr7}46aB9)Rn9>%1 zVX~wf=SiM4E@Gq`=Mv+*=D_a^ zyIjEp37ROz9Vgw^AMZKeL7qH-E%u9>c2manze`cCIOZ5^L0b$L<2#H6UCa26L=#i+nlYf%PQT|RzY z-d(c@0Q_2|CE_BQ)bFcnR&rfrCvrFTytw&5x6rfxUP4x z_@5X|3?>E>gNeapF;&FCz`$a)TCG-#6$8@(zcg^+0>fSe09UITRpTxI*hT;}6RSqm zxTArgN@Z16@oI3@E?hmxFZ*MbaXn1fvGoc;J`5e7)F}w z4`2WtfPe`mYM!%&`L))JFnW$DZMD{Ztu@zLYpu1`ntfNYT)_St#2Xc~#?0w+epS`f zoY%g55x6r<_qu#|PVjXvgarh$WE1SYE8-~y7ID4Z_K3nP8B$Oq`2TNlk5i?jTpLRU zR5rn;!gdER#R^Nyx2NbLK6R%xP#EbhF`hT`rkHqf?J&|@tNnNFFxK|WFM*?7fj(EW z2{TKm-p0VViWs_j75}Rg0Q}Ti|NnU^&j0_HKq-AnDW#NJN-4eJDUDJ}sR`noDYLEj zhlq1V#)FJX`)MSC2PmbK=2|O@3jSMKzy3e?9~O-?JFTeyoB(I13WLZ5fmN=d21nsx z@VVx%iYO>9Ks7+YNb^O}NmKyWahggKgC=E{BNRj5XT;2+ivNGDU%#^U{FGA1_9JJ_ zytHIz$xf}cRwR(x9^e1hzdS_{@nkmp|EHAB{{MOY|0$)E)>`WePKId0!^2ZkqjEBH zGE?FOo**}#qZBf1g8VQm5@gAW6SE+;+F7;L)>_2Qf}BS9|G!1-EXWd15gs2!X0zFB zHkL{DT>Vo@Db1deqdYfb0O-K>|pv^@F0Of-{6) z>-SKxaA5D&y2ggAH@u3Wd;n+fSp~pfKk_w)U$q7qls^W{GxiGbP?#{GskP zR*ZCXL!Av0e1wq*A(uh!Q$F}9;Tm@klgPo+NfYv31t zI;_pGf3rUx-d2{3*XL%tvHgcXx9zr_YXh$>428m5D6~C?xLSB~*!R?~Pp!vc*T+`7 zI~NS1lX3sYK5l!iAM`m`r8$zb9DJLze4OR=dtT^!w&x#uA&33X-|*l5$6@#e{s?*) z(PKvda|Mc>9gGweA%PA=IS|1?Hv$oaU}y*kZV+Na4KxUSKxQR#LDd0<%V>v2)hbgg z!;NG=cGWkYB#I{cW3b?X+zdxshZSZ43O6?~;P^g>R!w|3>8Tkah#J*W(2Ex*B!Gec zU0^sa+pe9h(frYzV^_kSJnUYFufctFy}E*;1~UL4wXLa_oOh~{vPya3p$AT&#T9^r z#Rf)I23_YLa;~+K*M1hX7W^*g{!i(gbN>GaQA#Og>G0YRM1i$FrSzXA$UhB)X|a}4 zN+~4)j3F7YzCQTfSDCD@|5aP*DGV!BGCuv;O?Wzm|8xg?CTfI)8DI>_K*h8(1qbmM zvk*^4JyfXw;nAwV6(rE=m26k?rq+D=^v~9Bo*p3yyItZ9tGCt{Wif1MqzDltMQ;Ns zM2vS&BNA9xQ6MHzfg?B|J>g>lC={3h;X@A&E)3}bq((`zLf+Ge_Qd}7G@>!>k_AP4 zcXy{b{G%Y&6Dy=4D1P(1n1($;q%^#E6v3j_h{dFxK!Z|B>3XC&hh`>ea<=OoV8ln4jT|`ly=yJU>{(Y zfM5uAq%j|WVB9gYb$rIo!eCAszdS7%0^O9XJ7)GJb==?)Rj#OO{JDXLfyY0d8rFqs&YQ%kOaw3Ul;x=^ZssppFe%;3fv*JKOzNTBH8Rlo817G z+%b`MC3QRr>KNf`{ra_1e{zHoMmxOS+S6!fX!{4=OBt{!#k+g}NbbPfk~+S8^CdG7 z&K&p$l#)4c5d2!}=|7Z>|I<7I!4wiyp>BK@vzSL`c@8Ui$|XpSAegbUyq@a!p*|Ad z_*`SaTz=$8f)l)--~6F$fEm=(#S9Hi3|oEWuS$}61p~@>8lt*WTTO_fNetPKoBCZG z3XsGRHT)Qffd>o;NMc9}IK049BU%9s6`c38xH_Q75JxFU0t7bX>U;tOQU-{QMERiu zHEP{Cvg?XnKNaj+>rAb`XObw2XugK`|NpNee0^(W|F(xp>Br)NApPohYXyF|zST7bliC6#_Ip@cc-%q7q&6Kk!Xz1Et2yQa_N14Hmscj``f0>!#hcTOUR>rUMf zKfHrcHWvKACjS3tv;Y6u?Eil@`~RQ)|37Hd)I`mF!iDdN7^Ch>;)DpG6E(O(8Zb!{ zGTHx4vEU9N1^xS5t*vw3IZLQs!AhsIZqNV!|Nm1;>C>K4N}pOw zWiK9~lv0YKf$pSWN-3pOg!)2ZCyZ!93*PU$prrvj3xzRG(&Pg;KpJ3)oJ&Oq>BqOoo#73Y#Sx9!~Fs)8wz4l0%e) zQu66BI7c_5`*ce;U!cg+lak2Nm6R}9`qGj}k}|pQ&giz6GAW!w*-)8#W@#*4yOqj* z@xJ9doU>j*uiQ)MB?Npic@5#3Yvx()P&n!iGYc3-a3#(_8Cl@hD6D~2q*fs9a)xdD3|h^GXVYs^ zHO&`9IYl{0a*m=jp@eBWPVaGgkJF=J+m6%pIK6MtkkO#g@Ij-8%+Y;d7{m^X^fmtU z3+Z)x{k(9mpjQf%$Xq+u&c$;rxtLDmD>sYLIw)S&rdjw z=wwE88DJGnSpXShG8?0Jlk8i&CuB%RROBp`rsWzhzmY)lw-hC#Z06%d<1l#(lQ=yV z7#LkUe}zi^;`!^ShHTz^+4EN7NtG-;NhyVN?OZ$8E+$jGaw2V{+3;+ZzB`I7Dv`Dw z)R+a3u?2ozi;yTma#m8dDaB+lTFqJB<3sMFD-&^g{$g_2igcx}EPVY~1F6U=9g}^_ zRru(U*&-RE5uk>#v^y1gw7Z8{oYP#|-p*~hJGsEG{mvKoHJzI7)2AtlZc(Rbx~--o z-B$+yzrNdQAQc&Fpj94KsPa~Q>-BUgdCOh9BsRBC`Gz5;fnha}Dq|J-vZ}EfXtg52 zZ@o@!F=rVWEQ7M)QgW7`P9f>yxh_E2!0B1QwX6B7#OdwR4RLxmJiTD}HOsadXhp^v zD3xP$jarQr`9;%mjZ}@>w|o-8)u8R8tz;wZq?6veuLcQQDMwK;O&pfOm@H z;5AmKyQ4VFhWDyTWX79Be5iV|j}7lg|2&n_ZdPv*^- zJ-c}G#!1n`a_n3~xSGrxmy$~eR}d~HuHC!=vx7{kT)LRpo?Si9QE3Yjxv)Lw*gcGh zC|B)|?Rm_=_N=nP_MF0JdtPB-dv4Ld_RP}4_Uv-ot{!?6v+pbfB)-hqe z@Qk(22|7LV#YxlcqUU_k5|deC@=8p$A%vjXAqf;i&gm*A5s*m)v_0#%@a1o(w2n#> z)jm?EX2W~fYluh0Ln2c`d$C?hHrMU~)Qy*Y$yaWPv1IL)wnDx|vkE$V&>#LnXDY5 z9HpG3QnEb=w=)D6`>kvTdKTHqSRn2#6*c-SE(!ov?wisI~%vX!ymqC0QU2M4UqxpS`eymXBg0cZ-7Zi@V zPc0}ME*q2k$XT8XFdNG;CUdE^pgiz>%2$7<5UlN$U8po|+RG(WBO?I;AONEl000;a z3I>FNu_y_XM3T}66o3p+gfebkHWtT1kw_ej;wTEkIE;cA24WaSF=P;8%o#!g00(R6 zEZehX6P`Fc$hO@_SvReS2d?pl`zfrlSFY%84&Rcd zuMjmEl&Ps*va!Hr$2BHBw}uc3S`<5!09PJS{1O%J)}`~G9H5A;a7na6pF)q6i`ts}0ra`$|X_Xm^FkK>kLL*cM2n zXCC|&{cBk<ic&bv`LN9!94U-L}4*pY-U;BoFo&H7Dv}vn)NXWA~y5 zo-Vgl=}>TK=$?n=%Lt1@SWjM@1e-p+hU<>y%00O0W>QZn|2te!DW6S4o`EZj#Q^%_ zTj~6l3%uYqVRyO=hJO%1zi{QBmZz8Mht%x#?y1&RtJuW1XTBnn{V|=?x!JiH4<=Gr zxoUDNy;{|xJt!$C%Ziz~V~l@fC=?9tuJ~vu_YFxMNADyt1dUGqxG_SLnZQ?1Z7_v_ zvJf*5eE)v({NRi)Nrd?m_J9oLNg63ic;wd$CN9t&gv5aa zwpgGrHv$(b26m3vDMkUMpz7TNEfEf-1OAxj7?5wkp`QzC&2+ed2hlt>wz^ggCExW9 z9=BCSwrALa!vdx5R!s^NZAv&M>PQt=-xN}=Gawb z@$YBqv3&88ELcpG?)c#vVwZPCSb*I_0mPsL7&1Q^?+4PnACU&N$BX0~p(R{0)5Cf_ zB`U1;ulyt4n#7!ROhgAxtfuzZ=yjmJRj};>Ly~m6WLQ|hyi%w-V}=nUQS>TXG+gkZ zyUHSJilHDC2&%}LE2FQV%d9fvJH-L!k^J~^r01){F@NzN4%Sl>+cQ@aZpQ)Y<6e*h zfKbm|LNk+THOVt#IVx+Mo6rEEW=>t&P)!kmEpDcc#Mc@{ zIlN6IpX~bFqv`h0IU;6~vBH+X8NZXPV^O%@Ctcn!xC-2+lTryH!r^jA{+XI=A#BXWD~xx5u^LzA4Ml^0n1=1TSM9 zpR>lIqb{R2+sE0;p^Nh@%ErDDljFK3`q#o-E$$j9n_UveaU6ULByaL4cE!jjWkr&U z1Sh|Q?hd2jo%BX`$0j7Haa`}+5k1-a=YvfSm>+m>j5iOnZ`F#q@%;l0bZ(=z5nPdq zbP7yK_yJagCsP5O72~u==Qgm>e{QqeLq`QDmJ5ja9#<|6+4J36LBHJF`{!lnm{*FE zbr718Y}!vKZ&Xf5k7=uproQ?!KWNG^*EU-Qg^3~v>24mq_7)BoBF*2;dwAJ7bJZCl zfV21i1Z5WiD)rsMc{;&i_nQu}2S0tA(#e~<5wo~9VnI7A;efKCz}mWh-05W!oWfVq zIp<-DzlGG1!>{zl!k45MlAmXMzGQrT`$(n->TtjpG!679l)BvWJ}>aAh$9jTUEPQ~ zS3L;;LPGpXH!ft~J`h;Hm3xY*8Jg85XzULA3<`veWvVJ>@R_9d?M#en+~I|*=_~RF zb7uOQRsK=|!j=c^ntos@_T(D(596V)&aifa(1(A_0LjeWYof)Rje6UIy6OquU zw~a!+lzi~-I+ADr6op#d{Rts@*}9%6vc{=Q1U*0=_KvgWR+0ru@$D#H*t~LU#U)J& zIaMuRWJe4GaXYc>y1RIqEkNS#5q97edfEMZ&cixZjMH%UL`QAk=9zP9n(mb5C{vqW zl$2m3U9>p@cfoo(f`v?RBdn6b+H8II)=Tgq^GNsD_`OEDV)>h!-8Y7_SnjcNF@b18 zV`jZ^>Xb-%Pob>TaedNjogm5fhf4(YwM|!cz1kLMl(aLp610dzbU{-I)nzwHg%9&` zVBuUcT>)p^HeI{FSHvqfME8=4G0+~b9S;-V8YO1nxa+!73#cJb^D{~^^{J8w$Sp%G#9M9590&(VY@cIZU%8d z`>^R)<~UpZyT{bTm!2CTc5V~HUmRWN3d4{%0Xf1yE^S)uEGRCc=FeVp5IV@r-c!Np z#ZIl6!(E}0Mn7`1umy=5$<0RS(M^)ewIdinDEk^T#A>@t7h!Z-mr=rZhL|`uw z%nmdo2Ug*C4v&B-nfV(1Jdy5OtEJ9Y1y6xiJpeb@XyGdeMg2ZnV0~%O>_Z;> zIin!#3KnB_{N`t^{LesmjsNf}joBVwa{rroYTX4u`gv)lBvRcyH0D#%f@$tpio!Y~GNLO)mT%(I7%*Tqsrt3?- zwV}V`r|Zu(fSulOhBf4qc|kv0w$q4Vn~TDE8e^}SE?iV1i9>^5O4+!t$DrM-C8a^t zGbAv76!ul6FrEZ<3DwJ%q4dJ`ip*_AN}HCq^x0Zc)tbom*jcd@PgG%rED@necxadw z0`<(3W`op|`U60~iP^R@@9fjy!+eoUrHZKOE~ZGQ-O0ySaF}u;Kc%&&waje5xA_Ax zntvIIuL)Ls!|h2y44@jEwI`E;67atkF9JTG#b=i++@vz9^rjHN#%zVa%||)dC^vzP z_)nhr>tr+=43}t9v#uE5;<_CF^kA?o??>7xyK!jwCfEc>mZ83 zD0QCukX~U>>f$Z+iu0yQg$P!6z2avE!xCB^G5ac^i?xpwa>}_KYKUKTV7L$V#Tls! zewMl=8>=80Z-Y*YaDxU1&ad4fm%js3psNs88mC$P&H?1NT?}NBOrlX+=K~egn~1L) zTY24>Mn~mzGli)L(bFw~lZ*$nWitkj!k>L%?77bfrEE+W``j)fD;>1AB3dtCL2be< z+*m9vXDPrR9QH2y>i_n<-uD#f73AiH#MNorwBVODuLb04^*_CV?k791{isg&x6GOxdjhQuCNi22{2kob(#aA7AtXxr}h{$A3vwZ}jP5GLCnu=lD~}R2FQ|T%MmCiBl~<)L1~@ zetf0%J$o1CRg?@%I~k&Qy*~axMkvYU6g`>MpofZe6iEp83|1|P=$7-a_20bd&3s7Y zk_g?lhbbcbE<}U@9kD%iI%W@*ni{{EVbgeoa1H_BKxA053QQtz)9uivSgxx>E2{}w z>V+OnGp9;m{doPbx`^f}k`yLr!gGM;cg-}{2l8-Bw!C-=DNZl5nb+4(l5oktB*LVh7OxVkRx7gcz6k>{7#O33xbuvT#aPQW|^(Kzsrv6Zx2iySUB9> z@8?XeKZo~XHv*f8PF_g)#|6YqAWR;Tqv{)EheykI_%T5xcBLnn1nuBI5R5ZpTTR7k z48#dVJiCR<&2SqW^ZG1PpqwdE_wK1zZTX$#YSytGF!{bPLv`PrYOI&4Mc^|oh~cSQ zEM^@Ge-{wDdO+=<&)Ah3m@)$sUKW@A?OhL1rOVL(f(D*O#eWn9fpX`^e;*SHSp+4i z>_Gwe4=r<((b@eCI%HI`tzH+1NP>g)dn64E;O{1xPU!_o{!Z6S@HDzF7wO|6M@g?au$gYO+Vu~SXJs0Z$d|kKfNO=i6{G9KM2En725WC`ibw} zqT>q{ZX$Uc=n(JkRtxU$xr|a+rc$#DUgL;N`V?b$nqS3g;ENq7pt%q!i%` z#s}n&UXTayhyu|5F+*2>Lw2=OK{AIRQZHbm$BwLYkC5PxfTyZwHXksmEZ`Vj>YUAE z_J?q6>l|c!;@1EQuWO-CL9=9Q&)}U`kU&K+QOc^NyfA43tM-944vY)c3O{;87!nil z-sXepLo(BVH6UQ_PKc--n#IZYENlpj))O5r zcrcU%0eqPhCM@>G0ph=*GGvrd!|qfO>yf$e;Muvxg%z~~Fu40-zt34SqcYtaQ|AQ9 zAK&DVGoJ#7x`R{6ZxU`2SRInbH7|fFW&ILq3 z%ZZ8#@50a@<>`D{W39&8PM{HH$AZO!hf^c;O$aNr5R^Yk<%=@QDpY(!(UtG%a34Gy zQ>74r!#7sAnrXJtH-XnuOahK2RPtvvhY>Hb-Wbo=17>hdz$wUR*(x>*T@Y?@H8(gh zL5|-a(Oh6`Nj1*HIpXH4q!;cJKie21q5QTUg{ z7Tg8|{z1n@>&Wy0u6TVPm}-c*l_-N(>TRBi7;un7z(taxTr%u9rO+~@&j1G^)e*&h z;F+TCNP}1;Sojr~3h$m)xd8Hhao826+K{v{td*V#VHjLa20V51&nq7) zz|JuB?r9plh-wZRv&;V!lCI!vF|AYtL8{NW>hYvUgCJK#+V!jn;l0-<$7F%AUznTd4*p0HkNF6LtJPYO(sCI5*l|rn)-4% zu+g-P8Rr^_xQRfjLiF@a@sw|1i$1curc<(Islpg++QqP7f9cKKA;*O(e%Z(n$uU#n zFEub8C+nEAVAoeQdkEYB#*-ls4wiv)05eYmFNVXv3f^n3IA;0Aczrq^-^l>{-tt-O z0ql?=C7#(#vV`Xqy&zi|F&M-GCEnvIA0q^Qr5tU?2wi1=X#XIIX~ zW##(`vEVYWK3QE>5d}EyO3#?=8m?ISk!U49jM{QlDq*uex6ppe6^*c2Y+q=9>#9cB zT)Zp1ztu`jZc41CgXH}GR?01hY~hMWN*aR|K;cIcyu8q8J*11KNt0(A)cV(?#jqvLI_Tav z=Wsz2dfdxG;{>Sq_M35mh7RV?rVNW4xh{dTuvtFp=o%++MjuXM{Bbt@5<~XX6In{= zOR(GHuVXML0#I)l0Dz~D$t!BXb)z^;=^-?#ww3nsZGFG<*w8z*0C{cPDfDK6>|mSi zYib{6`(!&tyi*`-DoaX@YQT^cpq`I*%*Wg;s=4g#1piVBul zW-5K_!~V6NIfOzh<1#-RUADW&HjF64)jfDfb}|BZ`TQ7umsweq{H-|BePGA)mepB*wkRRn1| zDd5hy7PUNis4vzu&l90*!Kq}Vs-r^E!?fCg<3zKPgrE)|b(j;kk8KjT!jj{BdnMZ; zPXKO+X-JUgd{?2Gf}~k3sueb4ZP$-|W~*oK9}MZK;lp2?2a~{cxqFPghj5eDc{Q@7 zj}c(8{gdhq_1>iIG&=~Md?egH5Z^*A$qOj-`YGu3Ln`uP4zJv4YOV#WVIFzNQMtlG zz2=z}@?4;vTEX6LRkk2$8r;5QxISnoymf6Q{!KH5I}3)pMV;4!_tF)j(cI3 z2#Y^{1%Ki^?Ibq&llKLRo4F)dJgxu%UG1dHAheeG#DR9wj8*m_;KT|6O96@ks)k9< zULvT3O;WYT#poCWs2ZyYcOWDcNS(@F+nUtp0?PWQE{uHZ#xU$Fw9c91h(_gvj}z+*lD0HeYd-Db2L_NBNz5fEP5>K3)Z{o z*d$+@ZA$?i1WWLxn)wPM@6ctVxUf|eg0;F+!01^NbxmUd!^ckYc_UllWLthKnnsYN z;Ky=6K8=;K8b74$JgMOw&ku1)@0L##Y+B!Qk=R?65h-3spqCLqw}eH30yC>Tm_X|}>8rlf z<`&*+&z%)XsMhcrPPpE)+VMKSlP|(YEQ0W}fW94}ThyXw%0uu!uJZNM#>x(!^T-gE z5d=RF!C?f`8>aVK5Rn9Lmeo(A6V=P}!G$H8HU44WKgBK(meU~#4vj@&F`!J|knJ~o z(i9>;kkK#X&iK%XL+C`Yz>6;INB*s$lIj^ecOP2-YluLe<7LF6k11%(A{bD(l zfEiu5rk#tff~{QaSyN}-=3JYIaT>DhDU;TSQN9bC>wAS{<)sb^r^ z){LdbV$1OdR&Y3=ZBwikWZlI{;C34W-aSE4;Bp=fX+gIQB>{KFm)F9D&R>AQWP}wJ zM;hXE=jG$jEaW8w(Udv8!%L_`od!aa_o{{Omq`%SXl&KDptaG29Hu_e4J!l2kaK|A+%?P&)8K2WyGF;>*u&3n_RGGIl3Iz5)FSsIO~X;~Mr|I}55 zb=IGhPloQfdEED+adtD@X<1It0K&2{1l=+ z8IQHolBxfYTR>?+Yy_9+pLYUmNTyY!J6hIL2eN@_aQ4Iac1cU*2^pC`xMTbPo3+Ug?6P$A?paVt|Bd=f4Sya;gg+ zE~czGO{g`-Gkc@gc)m~QLuGiIP!Ayibev*a))3D+T& zVE6ZU^i%yT(@d@FJ9^CLz-mZ?Fo~nC9ZRz#n^|G@J*m;XS>!;SDKUU?j1* za9{%t^V~js;ZP`KT+}**P{?)f*zl=7eM`K4d6VyER6eJeKFV!*Jcm-RANif-pVHX$ zDyXFVMQDdJH6Ag80O3WwpBkwa%6N*YniBvw%bz3&i$(%gw_iBqf;mqXHSKw)i!_H~ zXwML7KTr`qR}Lq(Q&juhkvV#m9TH~c-1%!`nY`v)FG4gKH;khZR@|p=-Or$56Z=k% zs=!~`+FETz6B%|nOh`gY++`qc@tb()ER95_a``d$+mc`a6J3{Y^DgO9JOSv*kSv`0 zc%fXCOZ6+;yOj4HD0?a^sr5R?4rs%Q~=;R~Z8mt$k>C=WS4V0wXRO zB`F^vQC47O#=E{quGT9eCY-oT5_0U7lPZ^50!qxY=%5H^o~8I=jn%b>E;uI3eIckn2LP1$KnBsvgUDS^X!vLU4ii?0Lqmu=++%p>(QXi zJ?uSL{VTxmgr4Tg0(Q0W%m7}kjcXgw$6LI^jL=i!KVMT)7RP|fDE=75OgziU`cb6xKEos0z|dJXKLen!2|)W+mZr-G7w|qU z-D8^WLT4MQ|)bCDcboXoa|p89u1jE9O4>rvwL-pL_XS93^ymJYh1hJSd!x;45n zy8waIiMt5{vB&0dgRYJC!dHQg@{z7(7})sjHG++Qui%~H34Mm_-|;qDvG2kOrsFse zo%m1V@jXK1bkH9_ScLWHMP;W4mj~L@2ZQV0t!N68YP9*VcJ@g;e>}7yZw&v~TNb9< z%XBxLpgJ<<*B=$rP%==K_sg^E*cOO~l}}rVh`$|Xs{6wsc%kT78WkWQ_5JWjjrZ>^ zQIS+F~mEqLE+8YSUwRl`H~YhR9?fZ{qm{|ren%1rG7idn@l$NN7wPZ};;1Ovi% zDcOHas6!7sTDhy3fB$G?$KzfFXYqzkyTRE|qcs?w=rhpC`Dywxq_jCjcz7CGI#aZ> z!P(EplN9pv`yZOV{j9LNJd8olz=O`u3NNwsv_S$#a1l2e-Y!x6n#r6tNnZeXn6K=R zA$R+=1(mb|kAJamat?Z^XlK#yvXkJz40~a~uP2HTa!F%XCZQ=+#-=X6R;Fy?g{}3P#i2l81UK2du3$R!LHCo?rS)a*RVo2)f(GLzybK2By8$J}ZwH#< zd2sgjZej@NkNs0^VIPw6lH(wg#8{?>GQ4H9MCtFWq#z$K1olu;=7Rc*_~G6OVNad` z4%jb2J0QaOaHug|>HkuUoq)_K6KR3>khmZrmQ4FKlo6pbUfX)Fwf;X~^>5 ztZmkBf^L({SL0P}Xl?*~B3-$^ZImkG`>G(J*7SL8 z)fLKx!y`h^i4))+!plVdaPDB<5u$TAy$r=SMOYTm6QgZ|3+AC{I!FNeub8SK z-Fau#17S78aag*TjS+i?UDx!I)FWm-!f4^O1OrO+mi11Br-kmaKB*jkfMF~V2V_ac z&+1PDa8n5hFFNXd1#LDw@ur;K4N&ETvgVd!62tdC7udo z*zg5hpOv<412HiT9^qE0U{5HeQI4K7q$M*gKp5lx1f3$vFAje+s*y3ce00t z>%n9nVD|qJNNW_?I9gH~)vf2WB~`|OEE6Cs%FMoSd{0^rWcO!XUpf#=e5xP5JmZsuD~wsd6OWVQg!3zsOX*n@hpHmAK_FI^qC-*H+m=Re%PT7b zrR0BSH_xT;iw^e)v1%baiW7J4OXwr}cigcmb(D(KFmgCW5cL;n*Dzj;h??MKj)hoT zZIg(W1_v~^F`&F8w0Zm=`;v2LLy2}DeV3U8e;hU>T3z{|)e51caFA52SV`%Ne=4;x z_VoN)LPg68@u$+!BA+PaY!Krs9hRj_e}~?jw5~gX*Ksc0y}W9wQzIrn0Y1|;0 zrN5~{5Y;EMjR!a|{`{ur(xm7YX5_(0jRFI`#3B_DY`4c}!eM9zb)v(+(Te7z!K}5e?*t<{c^Vrx+61we z5o0%^(G;QxfT+MtE2=q?cDDsZ(T3;aMdm=^s}a)tS2}<_Bm;+Dwi+ex;GAPY&x3vx zx6T<4B$$=tl>lAvck`ZbAB?s+pJ21AdD?S12^S$?0S#BP-|P0Vu*`U{aXrM&!wGi>{$ z!r7^-v<2fB&fv5Tye9FU4zTER8wJUjWL}eGBiD!?5k23m9;*aKTsVrEp{h}Snf%50 z1CF;sx4Cxzy&25aR0f9%wkQt3T}nN1F)O=6`~dO_xUK*}SQKeOMCx=yQx)2xsauVl zl5mY)H#OwXT7k%)Bccv@*n@49f4{b#*+61)_06{&vgD-~)=wR8~hI z0fXr-6T&C7Z3@*^FFa?%iEK)gTPVYBAzc2pm$O4;Q+ z8zk8HfOu;3lPMTEL=@`E5Ga;WAqLkVLimw_gZLjqK}>r`%$h!p1KFw6#lUhzk(dPp zijAH{Rz|23YlaL}cS=u}n{rUn1@bQ?5cRIE*x+=1fZMnXWK)>ayw_9CGhzo#rXh{H(7X0%*@FG%78qu^N`LZn;h zlX^+xXa9{90ejYiY*fATAwDgkbNd&@?bwGwOd*|3U~^j5X~4V~@x~PSrk85t4qvdg zFz9{){=Zs0qnT6xxtsDQ5t2y7R4ZKzvB>Q@-FNoPA;Ks0T}Hh$u^x z)W%;{5&$qWw

Y(mosE7o(@q;u(uHQ&Hpl0GBlzvOWY~hS#Jp)|~}i^zMvwIRyNd z<+~fm1|ofI5RtC$~4u4HM~R&uy#`p{YC9WD7)i=;{y zYgxSUzK{c2?~oE{7t_xyUKAMGu+!QZ2!Q;MYWOhFTp)5V*Ii#g7O~P_?41djxI}Rs zkEMYpn2@vU_5%v>?8Cfx>TGZE|CG;EoC+bC685M3kWA6LU^e2&X$kc?B78|7cc{{n z|AFpJO*K6QsNL5s=8IR(n@&0fK55_~8xwfP@gb{nCDP1><6=BSp#tb9?^4j+_V1O3zg`MDja2YicTl5g~P}^F$OjN`A5yMZ|h>zPoAD zW?v29=lx1MNn*Kv4!dxH9&l5d72z)4N){5%OM_pY3)|_|Ds&Q%AA{j% zb^(;-h16D*^FhRak6kp-aaj0hr||5lUnr zKolyH(qJfSKcKm@hY^!J`SS(t%Ul9F2&s05VVDxU{dLtYVa#BCjRAnGi@FvK5h05y z2J7%ZH770@K{$R)?!N#-!b&HeM0VH2smX>Ejs`3Q(kASFx0gi(1`}=TUfVGk77l#s zia=mK{Ddt{ybS>dNK7lfa z+zB|(qv^Op!H+qx6s3-bFMPM)u5#77rKjVRFqlg*3Zd71w*dXXRJe=%S_|41y%LzW;>8l(>t>QP24!kW9CGu4ULi$P)`)JQ z;G4-{6)STy^c?Hj-ey)^C7ZJEQ@BRmiL%Nu?n&=lEyjJE9KI``1SQP`yq{2Ie>o1Q7A)i* ze`4T!NlapVq_kBW^pcnv#&$Aj7=8Xc@jGgPC=;k*cF>YpgwU>`k+(rG4j_=#F68aS zb~;cv6`U|YC$*LV_}ew^jl6@C*Fx8dks9go&&Z8Lcu`u~VuckcOui7Vv8OaoB)SZ0 z+N<{1kM|FNzX77d&2W=;uh7e>pJSnCn;;S1alxC2L5`p=@^Hei1=O;biGzVo5$|1| zHOuDvB5Lslg0q((EI^wBC~ih}eL;ds;146Zp)oe!*tG=8PoF*DiUKWM+p!8OWCcUs z#w2MwWE=P!a!Me)lzI`Q!vUlx7^s*Kk(-x=2spp;C_smBKu52^X^EPkwl<=)OQ`)u zD2$2m1*)m3LNYT&@p7rEl!_G>f$>ZbCoayIZI|FqVpz$TfJT{V0Xpj3j142;>Kg^;Z z2WLSS+EQ4m$TsMX51kHG*);aZ@(@LohqgzM@#yP?9) z9Xv7<6UuQeBg`h!hx(OF`&GeDqO#Z5cj~d&aAKVGXeHsMTB*ib-=)X{LgRZPy_iyE z&*v!AJ3ea)sd3^-HNN2JY>}h!A_L~DgMQboWY(TN+0Up3(DR1&zUgo^^AuM>61*fg!h1_tK@LDnl ztj+`4*-PMS zb)rd-IEunr9us3JF74CjQw0 z1L=B)omoAQ0n6B@6)KmNW|cdcE<4wKA-&y{FLWKqXS$(qw zJX5-1WC~|ZB?Rj&Csh#ayqtv)S8h0KM@JWlHk0NKSjG;7%O1b55+Sr*T=wn@U#oA{ zXtgG*5__e|q{zgxChYM_l(93G_|&vEf`6hGJY$bU;2n1Md1dvJ3eVU(3ig>kU>UoW z%QFirF4vrF7@2Nll0LOs+uhH*YF>GF9ZkyEYqj?EatA>k4u3#N8GDVYLYG-Zba&_$ zJh`03|w98*uzUp5MA~vCHC}`%Qc>{`^ZAAz59>#3u_N9 zd+KXJt}7*iT$3WmvI%Ev`4Z(uk@npk#`^B8%xqV|$uk;J+{6c$QH=Ip4nxd4ZD4lcLresR1 z>uy_UX$fMSDcJ1hs+8>(s|NRU&TbplPh;Tl!HbiC~|c8N8U(tu%2>4 zt!~`X)mbAcD2bg*c4Ju81d^05a`1v5KoP{2L>6Y2kVF|ODONrRf)oJcfZLjMEjYrW1vF4~j3sI) zY$IlR>LMh-P>HdGgcWY|3M{ceh#S2kVT6tcSls@`ZdeHVk$y??O{JA-oR zh^N~RPA(P^ftjDGrZ&2Sgbi*#&gK$RR|r7J3{TSsL{Q==#UhC;wRL&v2q=z#Qkk1_ zgkhOxkzb8SBuPG3Sc!W}$_UBwi#$IJYUYkZ-4sf%nNo;&#$D7|Sm{c!VeEE(rC9cG z-sm_|*mgJKIL0y6YO5)(WjmJV;xPW=91|&k zH6@2-1Q<80UH6rCin8bp(?um12xWRBJ5Nq$1{)0E;_Nvbi9MdbxID($JBenfYc-{w zf`N2X??jjJu&eNCp5h3iC(VR`^pk$#L-Z-~)IuI$AU)JO(TB$1D>Mj!Cps0J8ta|t zu`5qyK=qcW=+jrCEN++PRco5LBJ+jurec~2H}(^5U!h_f%eNEJlPIJ4;<1YK(35nr ztr;@VQA)C*Vwb9*06?%gEKgC^K>7>b>8Mmp0?%UX>nl8q?z6>|+}15MLBS1c7iIU; zl^sGe12DkQL6r>%H9-ncL_rIcDuPHobup(Busl%$Mo|!$ zu5cFGUe zr^{HJbm8O(mv5Y_TxK_#;<8Z{m%XL%PYq}16EL})#AS~}{8Qsar0nRak!Y}X0z@-u zBQARv@sEugK+3iZF-K*fcNohUmpzDoW}Kf^Dt6``>@)Ds%UQ-l9iGZILNWejJOVX2*E~1p6YDc(z^Y1C=tOt~8wOZZx=KKDYrW#Lc{Cs)4 z1vfU8+dYMqrd7FJMf4ip^36Gx2eh@iZ;kKUN|I9Jay8$#g-&qWBNL*S1QE}k+Wa)b z=H)fR!rG`v9|6%!Xi-Q|ZG;W%InH0)I_G&nmg7E--PfkO$mVi0PiKBMjnRaem$zw* zCU`Uquq6Xf=p{}))HKEnjqf_=+;gt;fV{4Yn)$w5(9@rK!yf=DaUfv!V9gaQv#%&mxuhmtxRV&Q*RkiEsW&ON2H@Yk4quVr)=AC}I zVxCsoqO@J@y7ef?Q4bdn!50s8tcN@|ir`#2FYTd1Kw7AG=yVmG@`9K8i_WEPpSh24 zE7^Im6geD^+y46FEw5(79E)@_l~7)a@k|XmP{Zy()80{^8j`Jf7Vc zd8x<+9u1N@4F~R52C28cvkX#@(jvFxKJtvtsH?uFHd%eBt=?*|v}BAWCP`}Cb~?LB zPP_p)H)Ew+fv<~@_=xf=vGl99b%Z*b8gjfT!+(yon4ngvLQvfD(6@%pp|p24v3wz zK-RV_%d;*n)>><~=2YL;jMJyao-Cy0SNI-iYiJIHGs{yz_8@ITpdk3nq*2knq*2cl|Gai7?5Bv zlCmQyi!cbmK-3dkPy1eXSRm$gY@}7#2%Q$KhzPIyhRISS?nJF~*3aCE6B_78k37 zlLZ^U80*3=E^GP2p@MbUoFvvdP8KYGuN)z4P0b68D<%fc%)FfD$Bog4D^5*GQ=}Mm z5mXsLRdiw;7~@xCLv3uPw25tF1LJa4pqZJOo{`b1i8W1MFHZ(o5SE7Hj>e1OMZ*K? zFXUQHjRz!WH>@T|1Zz?Tf!)NGXPi+Z)_~;*S)4OE_ki95YU#9f=;->oU!9E@PR@-R zH=G8IyL;YoD|hcP2G<_P*fB!kVwB4qJH|*W;yAXrqm+|wsaY#dluEVYy6IKP%wn~= zt#oMwXxR*2MZQ+;C0rLlt|(cg|Iki2*`W$3DWfc* zPD2QFwNTN?%p$E&0%NY4=w^k}Obta*tw_4jyC_LkI5ASaYL~={V{&9?rqC@}fdVU( zSZ_W-da;tW%%m)}`$20dsmUfORhOkCF?dl)*@SWcqmlw+h6|}%N-|0!N)nnhOo*|9 z=?OAcY86y&bS|mu7cxjU`V{ktyMQoLMU$l}h-}hQ6+%q@c7YA&T}9a^r~6u>g6T%D zmzStef`UdS2^uxsyi_FJ$Zg|#0@qL2rpvxG<>~TwBwpqjM-bd1g$64udvV4RFU}ac z?Oi`%RhPXmL(6UdBoM_(%E}K+8?6RgHkzEWsv$w*rYhWoWC;o?M0hYM|L|}<{^7xI zXo0k|yTJ60bdO!Q$N8uFOi>Rjkd=p=$5O?39?HB}9@4xn%fp(Mhc+z_Z+76}bz6R< zHV_`Z%Q!9`y10Bi)VtZIVHg-}Z#@tO+gb0!V7vD|c&EMl9z5f|0NC)43_NR>zIqtm zk*<0hJac)N?MS^$1SQn24c`od?OzQ$-!{QzDm|#(zApXMg$>WR-Bt=V5dD2hX_uh7`7= z&z3TF0VN4Ox{2I)3(^%iq7VL&@f95mXC&ZRbemX%+#WjpMOhOLkUk_}S@a<)5jRF9 zJRo=$9S2bhMHx{M5I9M&TJQ{*d|of$(5@1q@4W*=indO<>2=BxV4DepX1i9xh350cV-V+iUAe3`x%ke z<($=3;dalpJ7Y)Ii)Ye%USeX(?f!>``$7)f=nTG!Jdwif*Kw;w%8nN>;m2m9uxptn`pofe4jXJ-8HNVdCW6uC^`b&l z<++40OMDUgbMZ`ij4{H+7ntDo&*t;@r-5`mgD2i%d7*VlU%0im4L>5s<-g@x-kV;l8 zkcANHYl$Z=>+;ePM=Ic$f0zMK!**$bToJsS3|`n8LDvu^Yy?>*!NG&AP9L6? zOO#SdX^dPd#jP&&rj&N;&aGo*?`bo=-8YGHx2rvLg_Fwy^rBy+HuQ7(T(RMFZP}>T z&~Jx%=NH6E7s;(fkS+V0vO5{#r4D7oc|!_&vdZ$&^buKlRnnP!j{Y%EhA@;a< zbOXd5@5Nmn{YdPInehnY@#V3`qm6&@hs;C25#t}7*wb?9VDxwb_F<_ceO~VGEMGTX z1LiFxMxb}3#|q@QQinSqh1dHMF>QVtU<@F=l;P8K*tkP$Q5K4;4I;7z@(Vm+W3 zpYznOoW_$OUg;x|J{mMU0u4k`q47`_fi^-(ne-c9SaZIx0?|O^5*e9rgqVb!G?17@ z(vE0HQyNIDQf11O(Z*N1(m-OA-jOb)x07@kqrUGy|6Wq5#uvWs%@@Az7rIT2M3Pi! zd=lxCRIc>xm3j#To=KNQ(nkiNWm0-^rOP5*={cBSW}xH}7$~SI6XRma0>i|V5){lRieUFO{lDrYNmMP|BvK zE2YzwE~OIBr27~_1uEsZspq98D4F&6^)B-7&Yhl68D^Uqm zV$YmbW)l#m5{JP(wF?G2AzapBeWgiROi0pF$#<`bgx#t1Fuvp%CHXymIue&fXp4UANA4{?e^IO+F$n!rA zyB^B+GuE<8~k zGW#SIYWd^)%Cq|%A79{fAN!G{dm9%3RCNDFS6J`<@-^K4jnCf4yY~wNbFOR1=`*kK zP-Y*q?8fKi@^M)AHB>iP*s$JKH=*vzhzk9T;#u^XpI7*ah2Dt{^F&LU93jzUtYEu` z-Jc}7x49~*k}9c^DyiC8LiaT;d;#c_=w@ zAYGN`cv?iGi|#v^pHUHvLqtVR;*%I9N{L%ylUTj$o(n?M{fg)5&Ql#vX)hRb-K(y@ zJQUf-Qf8xjk$p4D#|`S`VqN#C`|?n7xXVM4xqT}1bSTP}JT=PkO&m{ThsZt}gvo~W z;^f@${D<%5wig5Slyn->8cm{8f@l(DN|SC+X(EX$y{>37NNz+PLx|N{BwxOEmk%e@ zb(v0gHeEWIYW&K2xbFbi3B-bla;+rINMVuJYAt^@Z|i6>dd0 z($B;C9KNU#%)?h`Ye&PY@z7q+#>09&@8vMxjh{*^>ll^{Ylam?VO*_tq)M~aZN%J$ z$X>{8i>PDyb8ca~P{e}s~M3X+T7L{(h4XuXO zPJ_~<&tzg*x6^z^FKQGeed5tf6z#UlM8!l!A(pl43u`+}Q#<{du8VKdU7oM~a3&=+ z#i&*I3Z--sy5TEmLO~JfA}CR+mpxgFVzFb29XqxTQ>s+{9qYYkFh1VewOadnInNiW zyw>Y6s66HU+uzMoAH-l#%3$1;6;n`f=@J~r!g!_Q5Lc%Mr1I_emp}rD*;!qUorYO`^uJo?bOzlu9BOJj$Q^r~5 zn@%wwwgX>?uA?s7`GnZqELD?W*!%X5OTuG+Oy>~V3Ica_*<-PLjz%UzzkUfkvC z+{L-e+vQzG>`}R^x(mi#o4Z_}QFIcIrjyZ?RY}T7L?l9~Iz5RTr+OAk#d>kQ2#GG% zRqs+=u6l8~eDjz~<=g8}f(zq%zAvvsUl<{gPABQJDfx7gJ{3!rrz?H)s#64|gGAM- zQu+F%7RA~mg4L)xiS?*h6a{rf>ai)i=lo*Q zQ!I*L6j9)6?7^YCMy6Be*{{28+qQXp9eQ&i@x!6J+s8HRqI28VT}pk@=-XF6j$aqI zZ~+M$#%}Db`|It^+at0$Ns>a+rS;0}51u$g4-}6XF-08LbsV?ia%y(K^3I1Pp9QDHKhDm4V^FhBZ(!HBc0aLdg{69M!;Ey~b@J^$XiqRa>RDTcmPd zXyrPc?4HPqT<;Ok7dasW4`ax-L59 zz&|`LZt9#m+bQlWO?0|k6byBD7=eqTBZ4K_uyg8&I;R{uqM0Kt6^1DWE_U6uT;;3k ztB7QDTi!X>Il=aAwOmMyUVo^mSgIA5+L}^Z6Sp<3wkFlqWQ4lsLS*avvb=4pD(|nW z;jFcu=$)AqaM(JQVcz<+oZwl?!&ys$4ZR|-2g`4Wdp&7z%LER-|ZR8l^`Jqrff75t>TH9wQy~8aZy`T>7P7zGlyn4Do#C$##)P>wV;*?XIm- zt-e;ZR;wC`T4B?sk?B%8dorj#>C?9$N~{Z(j`1cd@c=O{;|hpz86fKCGSOo`Btxdp zJXw~q)ECyGR};%pCS^r)ItiShP68Q^aT2&3##7#2%>iUQ#uXs*v6RJp^izQAYnXZf z9wJ0WgQx=qyW)CNMJ`eGbR=@?D}Zz(MxyrsjL5I^A5d(6MU-*>au!H1-ANPkr&bJ-;`5tCCOZ_FzdGjqyaFi4X(=B?HgostHH1D7SrUf_u0lmQQZ`1_SP4ZgX)8p;Zh<|*%yN{OKHWKP?9&aDsJ-m5%x8S|QyPfcM3+nKe zd06AZe(dni>^&#u?K-jN^L7{mk$JaI-}-AV*B$I>TnC#J@ibSy?&ril8BYxe!fIf! znaj}*_A;=Kx%A_nL>UbBFg?#o;u(!+GcOvC9%*Kfcou2U*qMD!?1RZ9DcihR@U^>n z&JB3JnYhpQ5Z?2B0?+wg;<4`(`1g$h_wiliJ-%1qJib-nEqoKr6mkO%c$r*rxm=1MFsCY5wTh=c`ix{o};1QIVmS- z6MQT29s<@%T8T%8JppiFTZzNGiJXghKux41M~M;`(Q`*8cS0&}ED{z8J5YoJ9MGYI`x`p^h7KLG zw}A7!1Kj5$;QlxMUjff~3V7@%;NMHYWq$$p@f+|Rj{)!T8t@iA0}lJvT^(W7;0DYcgdo|PC+E2zkE&x(vnv}#JF zY7z#k5M=^u17>(ukr=C|6&Wiim4}rmiPj{2xFacxuy56b#DHg>?Ys^CUxUw{h^Mqf zDHM}%kE}rziaioZ3dMMInIy)eFF*nBCEz_Q5sZ$21H6}|2G&|@4e(q6o+H3>10eD> zI1SI*Q9}(iICUFrr~yX}Hq_v+9X337MC7?AH9Utz!;1!^9se`mu_L>gIo;Wj{VRf- z%jnEyGv~V7nK|8j=daIRi1R!U|A)bI{soUc0Q~zPF8d+A;{)J5J_hIUK;FU&z;(O~ z4twuPA?dS_<7ES=bjw#4|Vn!WPH5Ly>TBu1A{rGPGv4c!v01E^MKs_ zD~N;Qpd0Kv9w#hMS)gui9Of}vYpr#GJtqU+IeNk~q$o5g5q$Y<&e;z3Tob4=qPDPv zA{d*%{KjQuv0B^|O`b!NGMbd6DK#pvDlT^fdlmQ6!MQcY?&eiR_4xc?= z*l$;d!yI(3h6e=d(BavDu!JMYUisQl@AtSmR+O z%CW-dSw>p8XBD;b=seFV;#q9E;)LZ%3(licrs%fwFZ7x(e1RYA^NyY%MTwyI9Km2P z7z_r3?Z9?88PLajJ>JXmUJLJ)@tzKu%t4nzQV4v`3Eqj`MLc)JA#qXuxt!->&wbCy z9*P@12M3mCa}acyOeS-ed5sH^;NQ6H`PRr}&LiVt4+!=*KF4uB<~HO7A4)fIe<)pq z?>v+?!gVzHtdwnjQ-sgj2$ww+BJlr~Z{i8~$H&yNKsrv4s|Bngb4g{s>X##o0nem zd}(~hY9eGs5wffZS+$Tu3Ry6?(Ouk8$TbmiOk9#z2B*Xt;%Hu$8=0_@;A-BMnHi5r zA2A_a>6OLQMKJ?dbgNaTQU!HE9dAK{Eocy5K;5Yy>Qbnv1paX;RjK5$3jf5ocvR6| zR?f_I&bgGlxGpfROX&&MrPKw_zOSUk#5iq>8}o(Ksl+^A*YlM|(kDKFkuP6pkD_dh zYQ?A+=;o}K-qjf8Mjv=_!wNMcRK$V=45X_!9C+b_@w>h6qi=+?hqexOlk75r^ieL=5hvI<2=n>>75)ORzu}V6n zG)ik7>_7*!riwEOw@sTnW3P_mzHp1JR;!NVI1U79sx(0gxdVG=bIq-s`{-(}sMG1& z_D3d-(PM<7df%QIcD6d_(p4{2t@YMo-e4=DJjO-RgjBP zZhNFjAAh|prL@U(W0Ks0ViFPggovW)M4eDdq{bO%oN@d130O4FJ!>e5JmZWZPPn)d zI|PS^UgKc{vGFk8cI&!rqvIjS;~~$Ehd*?9e_{9E4Y!};HV^vj0pP92ahH#I_p|%* zP+oA>3(nPiykNZTH=g;0&jA2V%x}&2)wwPYB}fqGTK1vf@E{+eMyM2xmW4qJ6`~mc z0000(6#xJr5Do~0LcvfZ4D(P`J`{irb&gPQUKk35Ne-hh2x1UI#u#G^A%Fm5%nZ#? z4FCt+Ed;99%t(CW>bkbDu(@ZJ8pj+cQDX~+e7bTKr9!d5AkJSD^uX1EM4vPQoUU-p zo>XB_04+e$zYz2+PZX#BPHm3XOVZr_%z8AVV%9O}!w8(RDH#rH(0bM8Bs3W51&W%V z+&B72zxOm9K0q+Tnlytf{#j|H4h`;!2~uKNe_`FixS8LjE(AnJ=tU2uAR4Vaq{Yp= z1U+$RJVM@dOD(7kiXZq^lg|4weqX}NDmY2qpMe&KEyd19fOBBJyNDgWPlt=+dy(zA zJ-4Ip9FgxL-3DH#5J)C-2+9g2>XsA|JH994tO-0c&nHXftQH#700@O}7Tkj(JjbC* ze}hmFW`?S5007i3mcPBOQdmV>awRQa+7p>X!bF(PBr+69@>jc(5zU=tyZquQXvEbB z1_v(v@c8;5;d1}Az6cb{blK<9Sxs-eB}CYGMpludFV$koH9dO?Qf(t@EnkV-tz|>P zqJLV~KMF$m0!PWx5!Uv_nqXoa-XJtUV58krO1)Z!yBrYM93qB&V?JfCfkQLpc0JyJ zTf0O)3%vA^3Jy)~CsKrCYU(RB8fH)34xJC(jU>I7C=mvUG~lC?;UM!=RmR`@z5WN) zWw7A)0*h`7%`%$gX@E|Yr>z0idy6E44vLso#`pMoM z{G_x=$zq}VsUFDUQG^-Z)VwFS2;U}q>cwpUttUlTGb}xMC$wzliJ-l^W{nY6FB0I1 zgmyaQ#wk@RqA3SWn!vVMpL-VyzI8*ctaK%Oa(Yo6=4=#DC*cd60&1q}{|C=+5_S&* zMPiG?8sQ`{0WW~`Up1I{^_vK^oJMktDZHjhw}o{~3jWw=_g}*Tl7%?3TfSZ;gtCHV z4R>$%F+=KRZ0luHlGODPlAl?3kJGQPP(3O38Zfi3^@cTg043N8GnsUhrJ#X$x z2q46=P3L1t`I%Tn<%$)(*}v^jV8^c+#)sDS-1>+vRDTfY=#vMB{NKrf2$A?>zjUnHZ_yp_uyKW7D_9pE~h%gWr;Z zC)6Z(lA%=));I=^+;G|DCL`sI!rUKpwyWIzil8Je>PY<}1zgLOWaKz55{v9GVWFOR#Jnr5^`p!0s7c}Ui;{gUIM%`AVoYF#bap439bJqbVgfrP#M!7BHq#-&rEW6%ZU7D z(T_-M7dHglS@UA+L zz21Fd^GUa72cHKdF@tm$(bfX*^UQKg1yKb{THWd(t_3&|RfD#Y?5` zn0C{nXVXugjxyQUC7zg}d#2P_tYD-5!5V@4>bjGsBf|#c{pzMx#rd|bagFsatfdN3C~9EeQtzR zoQCs8y$aua>Y8L#z^XNpx;)QXwe6BSRX?qyPz&R7s}(`8%HZnF{w`swbPEX|oUSbFwg zoVy1x%-}J0(F{d(fI#&sCSA`I)>z9wCO}%OgHthW>H;LTxJNzpU(ruRsB$Fc6It^R z;Zm-~&N%a=Y(xh4t|=!W(iS2tbg+Ot@rwl5~gfc`Q(!|BaVabr$n*(QH6RwpodS zbVQE?Gv(dmcc^QLjteDNo7iT~2@FPhvWe4le>D$=-|9T*7L8y>agE`X6q*>%Vu&NQ zS9VH(CEKSjGq^`B_N_njD6v8ivwU!N+FskN)IQ{={75wgF-!|7A?bbi=s8 zn_PIitmvXS^Jl&C&_!D|^enGjZ&MSB6TS3hz0qJ6uyh-qaIG_C=rXEP@c$D10Ur6d zDVs6rF#}9}iM4u4`m+k4;;_&7Vh!_i;+uL{!~4^jxw%d#9F`T95=I-ExVCkb7<458 z+gt|uWTz&_JC{-eun@5ZO#0SkRdwYn=&JV*Ow<=Jue|jO|NS|B8L?`ML+r?r=;lS7!Mz(%D{F1&@!G#Nh0qCks!wd1iX~`JKr@!4ZQE!<5744YG#3W#%R; zgpA3f3_SLJ0b!`* z3i1y0nX8PugoA#X2gfeYRERcYs5E`yzMzs%A~Of3=A*TSX5xm_9Um1IkhnZ z0pg}J9=U>y0W1)WE%JKBGfORYWKw}U%qo8^Q9hCY#A0wXK?;SF zHL9<`>2&|Id*!M`uhiC*)t)u=1^&x|EdJ`K*j1onBDpMC=zeG@2JW#QBM}8 z%;5+{Z`=6A9YdPG4ge3E1zh!&6#4mg6-huz+kGc@1_3r)UwA5gp@@aGbmyPL z^jZgSc#ozdrH}CH5n)a)CbmvK@aa~wFGtI#`zgHB9vW<1#B49P*-m*^hMu{An6qnr zkuWHcmC6}31I6%~>k!0d^-h1mqc>t8()DjGIyZau90|Rtv(kkzB5DIdnF9;gcpy#& zn{}DP6?71<^&8?l=phTHH^U185)bh_H#TVtU63KwWo@5#0H91>dAkjHWNtlOl=BS3 z#b#Mq4)=8r4u_&Kp2k__B)njCXRXDw=-Ku%^RKt!%aC8#m$M8AbdSluktA)z zbQ2NsLnM{MU0=*Mm|q)HeSdAzI;w>OKL9PcD-H5j+FYn&cY@0L!L#RjTvO&c-T8Bu z8WMq(0#C@fSD`KlVA@3ES)~#&D3Fz6=Wm#fcvjRE;wyhcX8POFd$_CAgFd+H@R%q; zEr$b40ON69b2tO$3n1`@HS0;AT0}E! z6xGDdSIUJBv+H8;`*dsklT=v_w%S* zT|eqvP*FlfmRA3U#xczN%DIP+a(gKTD)>xS@uM>4X6mLF14y!d3*-CD#(oBW#Rsm&<5?b5KwRFaPq#L{}~YT=CB z4Skk6QW9+%1tl+$o`uib4o2-j6j%s7qM5qC6@)V=pG?UBbRPr7#e&6Kx$_9gyUCJ!MXaK1WXq)Ouu#`u;f0RxZ=!hw+56TFS?K1b z1AL=@G%LV&jCxV#PlZ_rsi77Oodm)DO8m>5=?+Zy&ElNV4xR&O&8$UHF|kMpiHWVG z44%pzeQ~e8xg8Q?UmvF3IR;WBH#JB9Y5zcoEtil#KT>2)Mpu;LpmJr@-%thMO(Kfk zLN?N2)ah@6^M%snPh5!A)_}oQNm0eLE5|-btr&2ib=NCT-rRj}+PK5E6>x#@CsGtJ z_GOKOotuit_DXl3sS8`k-rHF07$`$1P^|K>_lsWt^h{`l;WLM^K-sLO4f&lB_T(eN zHu-gFBqDTaUVMWP*!06#4Ysu6;Go?zy6~e5IN8S+xl9<83ANQx&+X%7Wx^8Sg%5&N zQjC3-B7d-`gh`Z+YDykOy7A+-LnL04h44;Y*TuSDqa>fgMiq7VrFy?C7b z!c?9LR~8qLYZb%OG@#Rv5!j;4?vrsIv_n*9qdiLxgz)fP<5z1OcJ^INnXLoD)J@{z zW4bx!Z=|JP*fO2oG;IK3-UEWc-hJ*4*}G-xk1!9ClHRfenlAfM{gSc=_zf z0{^O-&uO5W`p1^5_5Z7Bg;?sb?*RyY`e}Hq2QIY10+w^h9-f}39GIja)~~pHHJuDZ zdPWT==*@^%NIY6F%w}bJq@dUima-)wM#u;ikgHVrl=m|o`8Ki+c>t|!8^ke;!N89jI*ArlD>eTBcPG0m|)AA z?@vC-C<1r?31`Q-BuTw$e7p@vyoPFzyPM4vw;$INw&EhGKyPn%Aa8-9yn(9jscC92 z*MLqU9)J$%sEf1#?*4Gi)Qx0#a(7>%epu) z*rbovP*#OEE=i6$3a%W%`J~Phy4?tu2N#*Q{srQRl8rpP*qi^gY z#@-`=*5N{-(bA{0dD`mkm@67&k{ahyNKBb88PkX@6Uz)g)AR~LDyC$&m&Pw)5(rJ4 zBSV}TE-r0l3k++tO;A0}mLXURow)W`Wyy%{1?zon|2)KqhFaFhRD{y^6LzTde!szSM~i7vwr zoY=b>Zw>4^51*D!&8mP*>yB7LGK(l=8aH@<0}-@eZn9IogSUqvsGDIv{&I-#i?SJN2AH@K8WPzdPvOGZ6wD62aVuf+o`Vp?N107uV%%TRd-iWCEhlx1ZOVebeBx!0i&jq_iv~mM>zZ0aVP_+onDwpa54Vw zYidmXFd;P(!z)!np8-!%k9#H5RkdtYGDf#v@`7)9}X zB$XgbOc33}IuUJvAoVJM$X2U3q2jx5fV8>aZQC_GlG!(A23=1{Pz|R`E-oP^b|9Gn zb1q^muH8&#}Q6rf|kQ7c0I=Kp&A|IY8wTH|g z3hE{4`}sgAY)2X^ojG0IgERz2nEWQI$`NwAsJE(xQ?DcgZzNxvr>KHF9{m(KY_~_d z9e!2rI{1)sU(_5y*TRz zAP5}Rxg5h|jlc07nNR+qr(g*B_ToNt&&yJWeBA9RgX-F?f?aCJ8zNyN9&?dy(j7vT zP-ud{K!N@G&;oP%U)WDiZq7X8k%oYirbTo3gqSHlf2mCoQ2_=(@ul!X+JU;mId5h% zjw|7pVvYSL(|{1n8*=2qpA3;V-0++4QcuD25^|1^+xYbgGK^kUauNh)2ggKGCmd;x(kh-#ExLR{-NL$ zCes`i#-$gaRrRHP)U0%r3{*$2{C*qt{I??6PnLjZml*(x;Ns)I%avVX;$W*RavHj9 zXUERZebkaCvKJsq@r3LcT^P(_0L;W{shNlcOHxj<31$@S<~ab!0$1g3fG@&9!S)^j zybQOi+MOFIX{Wo!MuiwnUG)KC+1N4UyZBs&kb@)CQA*r45lv4j>-b#!U|+mrZ^4Zc zSq4}DIOo^4f1M{nvMbh`CKs1 z=MD@3_;Qb(%$8aEqamFks3j|spk&HzW49~DWwb06g8uR=+m3MGR6Uzwin+HjSqAY2 zo{q?L`0}K~Z=aOzMvr`gj#w=3_P_1$#jIGz5Fa>Ie`u^2YQ`i%z*QSANG)Kpr>8=y zE)cmF0W+33!2O~w&v4H-Ix^K>jwMOkGeASC%-*^XD*-kSNOW4C01lHG_nHzCa=Mx( zDpgj|381~yKh|qH1l@HS{B4$8MCnfu> ze8a$=Og;fR=Yzlk`PBmd(ojxE7*vcR1e_fBreBqL&3d2>!sZqAKbORBS2x4`1(c|F7?c{ee>sUOAFQ zY8(fTV3&z@Ca=SAV1Ta^)Hu_|>lepsnW#W%!fETURdf>4(0dTo{wERr&V)I#7;G_p zkn4HyD-jF$^a8e8z%B6HyPPAgIO-ee{})3-O5X!`e6?_TdTkYoA@44+EpvsO4|xJ< zbRcz@o>$NfspuHK&0Bt;nzgQo&ODOnb1eX|{ni34L@2vaTb28o@Z;FSXTt?|zN+&M zhc^%HcCeeqx6x69wnWGTIBcit8g*2XHq$$QoQo0jos-)($Ghp9;T|W}Yufe~vVR6< zIjPY)PK>4Xt>Y%Jq)IZ4Z>fG_Fq0RnAxuAZN+A}NVv<*;+U<;6#Kr|z$}T0cNI-;_ zz8XTsJqn$`b%Ci073$LsS~Ae)aogYY z=(Do3)j*dbd|ne_B-LY^n#yGSq|0pyx0A-QH^fh509#HumNki4)({+3aImcSfBK`f zd!`=C_A}p`5?0g^&OXQAh#_HcWVi{hC!{TF?RONh^6l76>c_I<{J-p<%$i!m?4F!w z7eTU)7itqrCG+muoRbczgWom8Kdi3*?B+zH$KYYzvY$LtSTvWzoEfhk`gi|@IR41xN1 z%C_efQRSy70<4vjs-^)0bGGI444UCYik1kNCX?04MJE7)uAX^hDd8Ya2?h5q(-tq- z6i}xN=IaP`ICY6Oh94zar^$YTe&&Q0a8aC7lDJriXku_oKQpVj*Ub1HFwNzMb$7?E z#@P_u>j49DxgA$GwGoU$i2tfn6gc@p*{F|F_ofqqBR*WL#MMf~yW=grK^4@&V*5VG$Nf zZPl@=D1n`0$gq&4GJ!MUX*2>$S=eDHW2-9}a#gmGlV^8QIZDx{9SB0vs#53p=o^*h zc@E$9{OXZ%EN3RjNoO?890qh)A5$wcuV-Uxb5^werYv zONuK?A68DK0>9#>sh?y?T*~)GvKBGTVplsT=OZDaeEs}$d1F__7l5VF&--_Jm;p7)Ys!@#jQp!=fYXeGKJ~Bkg8Z@OLsusef=^LjPeuMK^S!jm6q< zjA-}~#;B#O;w3EMb&`@BM#l6QMS)O~{EPzq?o?h0jwEH88^(jzN1p1d=v+ZQ_vE`PLISC`9EC(1cnGEIhL8B zS;=@K5zOY&mO8ca$zA)It$!onZy^|Se@n4SVeK78^lk`M847`yvvOd31Y15GGHO$ZsIngImT4Q^v{)ehVe2LhwMf{U(nO!u)ZcYy+08@jx4%$IKR8i^PWMnH0* z9l)$Ex3!8rH*5}mR!WS9p;^C}E8 zF}pBTRp4b)onR3QwKz;Hy84oXS{?Gfox9o@;XigU*z2vvB(U^z|&c?5HS{|K~I@>puYq6d#WM_a8ZFqJ(j#vJy^5 z{5YINiUI4N!L@Q}V^>`=i6$ zBoO=4-kRkE90>jpG;1l@Or^w|-4&me9sFzTYc)EdNB~#e6j4I7?-|zwMt(^o+@?TQ zTt*QP6f032sIV3HE9btRNl*>!bq~t#7bJQ|Ab=Ywg5tgg8)2UK3!AEbfd(d}AQPz! zeg5H9B1Gt2F!x;Q_ICbt3M!!IBQ2x&*S6L6(aM{R}cS9Xj;5z>bP-v^qS$Id>B+uwdDFz5SOO7=z_i!z5fYl7>V{X)z{M|;RyE`@V` za`Www`u+lqW-`IO7ld-kEKIrOQi!>&#Y!TJ&9q!f_OFCom4dS1Yn9=K6a)2(gBHxt z!zWIugRY5}!~G{ni;`=*IMa4a!t%f2RhjB7oi5Cpp_-so$swTV(mwocZ8K+Fn00OZQ36>~lu|-q7!E@J?+u{;DQ^83KdYh4IFi_qzsO{xwsWSB7`EbW2n zXpBlWpv3zM;0RD3->V+;8u-b=e{QJG_f!L7v0J&F`k!OC2cd$p{VA^+$w#B*;_$@1 zwXT~v>;d0XPXX_Jd^!t~#;$NJn~4cf;qyy;2zlJfC1`O%!bdP%Oimi%sQm-R?fP1v zIE0kS>ALav6VzCy(y4ooju~*h_s!NNld#r9ziHZ^fOx85EQ)p!p|zJu{Q2j?ZBnX) zM7lhLBx#^W2uREgdZPP{6O?#A6O(@Wr`UQRE3TgbfAh_9-rD8n3z~Fu_@f4Pp8$p0 zy}znGSUM8mh`GE!mG;o!j*2bZm*yZ&m54QlOTNFO4DyI7&#aV7ES&n=C*Ks(saSxxuRHjdH}lT-c2!P@{TR(KsaTRNrF37Dx4} z+2+Z@z69tNC14r7zx&4dIRoI?mK2&xMs^}I;wPZyGl!Ml=wu{y>x2+bR^%m~aOEKT z1BlU(*blkEGzFxt>ECsZtu76PcW+Pzvf1>Icb%Z@+|m7LeS{2_K{P#pi}dd~l>*eK z9Ve}XWZlJuyG@Ar7qzQ~pa7b_bH$#4!kt&I({TPi8hYjh8^Hx`t4gl})rk(VF4R8M z7p37oAF;FB@Ru>bi_OSeH5x-PO7Ha^hBzmMRN(kN5Q5qT)hd&(1k&w$m!Sn+l9xgp z=NWE#qwZ{IK9$%Up~9w})pN~gJdQU*Pl)@nS|&gjZDRhO+94!KoCfQc$HZ}F8BSWdG*_ji-An2~>?3ZESe3T!xxCK5GFO+P%9ja214KEqX8mj9&S(69%R4zqP}u%hU*u2n(w z_h!}|PxXekal=5#~fQH}xeycjG;-&AsZdzt7uF$!UoO_3XY2~c02EN*+>(?uzI z=pyQnE1IhQ--Xy;>r;RHZyB2N{ zSwD?qLy}Kp30rm$8|_aD_AEFCWAZRT3O|z}_38dl5bqI|^l^w++IDvA{=6eCF2T-L zF7ID?2EhqGvfXV_EiD^I)HrN~ceZ}JO4zT3UTNRhzudc+pQf@3*Y!ZBYvXOd)f{i8 zIHnV>WaL6xNv=i_pb3B_z@ym_)@CVS!-cl3*#KcakY~$wMjjBFJ-EY#l6CFSZ?G&f z2r*bNyU+nj4$wf$S;$W=mY?ASN#JfQ0OVR>Q~|!CKte(SC{e`p%df2wfXFb2X8L}A z=$+ymT%X%N+n_@7s({wKgOhLoK{;tv!LK0Q{45DjVp(xSj103kk{3>w-l@6a8wCBwdp-I+O+tswmtI82fd#hmPa z^gTl-X~kr;K1VUh#Om6O@0f=34Q@P@1pY9{29x}foM00`ml>kkJ3+wa34b&WDeZs` zgN;P2ANcjlVdSJ)}1wxlY zaX5b@0cJ3g)NEYNI<@B(8*ts*M>MP{#*Aka7M~pgU(s9B%KdQGSxidIO&~qu!ha>_NzvZZs zvmMmkZJz^HGSP>H@a#MagyP;ki4}y?xFLm3ZVP@)%6EPggSEu%79{aZ6ab@*+L@+# z&-fV(-LI032$qMVGIDD&!jZ>zID9`iEPhZkoS;1NeV!N&7irmvCV^cJTB}*|X7;tx z?Mbn*KenQ1{5HB@q~MBfV2-1>>6)?5U9#_cGo$SgQ``*KJDJ{(1wn7;kosLyKJL9gdxyJP7I3!@FkTWphEC8*P z9C&0L5iy=Y#70YkLy-5wl*nIO$g|i)T1o= z_}J0}GzeCqs-ObEDBFee-3ARUA&wuc7#Vn}8T~(=axbOOhaO3n0`QfFeSQWp$catb zOJJe~#-ip1QmS0iAjJXL@L2d5A_N%If?5gw9ZQWzVe#mo2Lje(jp79<-wVWk6@c>tR ziA%O!Van)L@BsH}s(3`mYLV^C3ed@Dya?|pXFX;EbhAm~bA(%W>kRKL?!e5p4_N4G zX_vM+CsN@HQj!FmQqQ{O7Tm%s;b=CFQYt&Xa6Lbw-N|rfCBk4qC^}Qn0NT>S8evs= z=ZsXupa$$X@b{xy*3x+#U1Y9?7iv$2=%Z4Db7+CC&rOGw>TnQ~inc*pjz-3Hju`H< zPBi{JQY+836{kJiunJ-H8Uhy z;a5|ceWd+eb@0%-?V-a!`1vcrjqR7knA?b1LrvJ-dW~+g%xg4(}JJ zN8qQWbm(p~TD%J#$pxi13t0kN{`c`hDW)4(3uke;_QVqAXWWLIs>b-GhcXc$3f)Ux z6%`-YEsDQpCwMc@*5CrsOxrR~6eeCR88>@NhdNhM0w8D7%~Bo5&5^$C=*J5_O4seg zi`U-x69?Xzjmj&;`%e*zX7fsy65vj9Qz~Hv;L(yxFlD$@>RF>1rC(CP8f#7ctbY?a_6R_H?@Xtv#SU8a&a|gkjE!5yiXqaPk&^(gm1VX$)Atj?lB}WfiFAhbrVtO{FB?Q6d^7T&R(JnVN$g zQp!Bbc*GO6ngzU@t6vl9eg%-Np7#W-S>u%iSU?oc3(?v`eu2|oLr|YJuWeV<3qco4 z`s`tP?Tp)U_%RAfNTkJxHdZbARFw4*l_c#27FfLccg&uYyo00i2LiEFiedY~VX~RQ z4SLW^b@$V&Us)A)^4^qh{O7!N1#)$p#R3TorsPS@Zmpty0oQ806 zdM1H$Fb-z0woCtbG$!Sw;L*ZNtw8WzNt5^KMr`lVZX2z~9d4r1?Y5;(VuCs-)(I$K zB02Hh5vQ3lEEsN&$m6xpAoa6Mm+hG*M!@W&9cOlcSMv&MOC~H#Guj-UI6N4MOyNoB zAj?}5X+PLiw>+~2*6yZ*nS*k=EhADl2A1cc>iti=B3`uE@nx|`cr_NuupAVLDlGp=% z+{*Wzh@VyZy|hQo6+jEqAPZH^z%I3TjJV@)mJ@yC6zP-OEOYP1q&wO%CW2RgrHo|} z0Ddz-0F>GvDQ%@Usvfd?oxo_e{cjV2GD{fh-9j1k8^G zF9#<;=}2aY>m)$5>~_#}u@)5IZ~)l@Rm0+gIfgtd7QBNiAl+pcCi`+V?9dkd?~e?{ zf(0}6T`YM9S4=4@my);BVrgkiU?0?7dlBi5pW^yT>L!C%@ayAiOADfS3_7*CEK}ls zs3gV6MDZmf$*3KzNoTnh%}o46V9v@T;$lkm&UaYGShTh5{%dFMt|%glqAD#Mv!Y96 zAbTjjEDg%HuuZkgWXxMbC3i;jGm|mg86+QC5>cN+*fIclK-Ao;QVCFI;3+7aKNPDe z+$o)2^3^o>d@cAF?a-vVb9I4+p;TNdwTF?S%_KUztH;^?VIubPH#vtk+n6*Z(1l~r zuzLG4aB(k6mhwL`vdU6YY8qE@r>rWEgTqULvU6zih?dQvisg23w!x zLl80qoyE7^Js3||8d`z;xyag1U&j~Qp1#aUCs5ilyj{@j`Lpr;4NxGM0Qf(V4g5G_MppQNJ74?Br%ZXJnSmj1H$U z*v=_#SC6{aMEtrMkM+=l~`Qj%7z&}=J@#|O`1XK?$AStxUm~LMy{M zF$RVm>{Aa9tg^fX@d2?!iR!<3@$z zmmM7a=n393fA6!X#m5`HDiK09lJE)4j;O>0Q-^eJPr3L#h9=_46_Eh&t7%CHktV}? z<3=F#xma3p7=7LidV{-3xc7wgX7qDNJua4B973OWhu-FH67GFSZx;H9hCeS}d@KEb zFmJC+5)~(dTPRGCF&sP}*4cR9{Zchic+I2Sq{4@>^#B9Vvz zu=Y3amE|6$c$b_zlSROJunD3 zz6UNNVgnL}nh-ZsEJb6pSMiZFx17ob@X6%x% zpRVvIaAa{)XKbH^*{2m2Pf*-3nT4!zWuuaElZRzv_M2`r(nAZLxGN6RH3>Zq4xD(mgTxnu?0}{|3 zPiOn_LR#c0WFsDs?A_M|2gL}$>voSvXUk&s9Q3deP~jGm&)xbO1=rflz~(D}la?X2 zWfT&WEk){+o(r3Y7NY={mCQolxY^Uqes;A5Oq9sADksk$G1^_paSH~_YHDe}o(!>d z*}%e5tfjp-Fzs_n(7`^%A-cbD)Jyb0l+T_mAxn1Xd6Uu~vF^8Pc@=wgKgCF*fXB^q z2io(dZ)PjXH>r)R=@%r%8=^q$V=v-v4$yFqfjtec@)l@jA-&N+nN((`g)I9WHBe&) ze)Ul!$PWP?$5-8bp&LxryZQ6@I|Bh7U{Xu1T*(6d6oN~FBUV>?(Hb_J@2;N>bA}-* zqMv~^<;ehD=tOg)>bJP)CqqEB{m^o?#!bz!H<5|UKod;0KSX%%@2)6xfGiz2CY=rc zX|_Qi?&Xe!^`N#bi9|vgZ{kgR%@AF#M$1rxZBU#PdD9h_a%Ra;fR}1xI`6w8!`hn7)&BI;JwT2Yp{@d()z;;@4bo3XUmwx zq1*>Es3o^xk|`kdHx(Z!>Ry3(Y04mq2Dj2wDAT!AdXKoU!y5$bRL$D+i_6I}Wa z@URf0DukOySj%u*a|_EmSLMfmEl-8U4Go}G@13-KU*m$mX&eO+8ki5i&;DMk(D)p+X6U zEcZ5`3|J#ymKzBWiIqZsi+V$ZN5D|k$r~?QO0=U3DxxDdT|8BPJ*AMDA;Q`c{K(G= zVlarJ6zPhky0W!){iIFe)7_*=I5YLi1>0c`O=Uw?BS&5P7w zvW^EK0lomHKv}==Zl}TPwm_z)_8Lhfn}{H2-vTz3QAm~x34pb_E5(NNgV7>E$eqB;&ViUbnMKE;ZGe z01@dlE!5*GPIA|YDLkxlo-MR20z=~js+omz{VP8Ut-OO=k`gwNKX?`m1mS^rD|&Bw zmVZ%krNY%Kjo{PqU(vcf_$Tl9S;b}v;-ZgcfCDe*VV#DE=R z@!sW1MZqJ^3NoA*yoUuzk0+9%SP831CtnqO@sZ+rXn#G~{Y#tNcv^ZwGMw8+ENotj+jj-3)%{6mUi7RpSJUc z3@YY#_yCA)xn*aGOpf_CnS^9?8%*H(GfENyehxi`4UevbqHsrol`OyRUyb~0D_SH| zI5F=d)D}r)2LKzth%fg3F7oh^G0go+H9$Q2KYc}04wr1h%GQG3_5@`}-NqDbHlSq@ z&RMDZDT96#iUUe9pkObi*P$DIJI%mTF_1Eb>!q^(DS;{Ta)1O?+4XB&_weD;ix$$M zC+4ckdIj*y*{nZW&z7|RlQ+r$dm1$=Vq}lr?Sj!j3mxgQ-l3A3b8$>)8L_2_@$^SV zgD2I39c1W9ScY3Uu>T1S<$vM`FL%ug6RZTs$pjEWm@S%z&D&OzL=q5-uDD#V`&>AA zoGRV+KZL*hmGqswg{z>#Zn-BTC6a|3s65{~hEgCsgFh+s*pNBpACKZmX^I!Jh4l6k z))N(SM6`TuiN134I~>~)3G}EBs2ZE{-zRZiib<_W7C}=#5Iq*%E~FihW2ddfl0SnS zG(dJD;-Uo-IIUJ3*)PgIQS4qoJaJ@Qisyl`jq3ZEK~}3G?W^KT%m~y0kc93?#$yee zt}`ygt%MorreSm|Z{#MP+nS8rVykG^O&Sl82`p+@8A8h&xky}$ld8d{q0<|j6njNQ z-;f&2d)+%E|ANStASICWOSkU#(>7*I69z z*{Pfb;|aZs0lpixTpg#_ewnc(D`ezUI%QjFGpU^_Ge~c?n`kThy4DlNKo%GiBzb^R zMDu4$!YAh_0*slMSv5LR-_$|u-RlOKjHb%>jugz}E=kdM+M`OO6&t^rN8)-j_Dk$i z8mGVwCUUn;BVA8)nMf>Nlfg7T{5Y*Q(wcC+vCAsiWsllAA;GywNOa(qq3HQa7;lxc zcCRJnt0aCQ6FcD!ofXlf#M!mGcFGD0t3#CmI1~^2+do1)TMy(Uns_BH3 z+wxdwC#66TwWgDNZa;tJU|Kv5Q@6M-NlX_^aKQ-nuy7l6eDXfW;IU}?Ou%h@a55j z(<^m09Vg@)u}`S9@#Dyme@K=$$s(UNq{g9njIpc#C zRVf;Fxo$O3x@v+6RFi?O11UzRairSslxs1;FB??Pl8b`h`V*P9LC0OuKCh~=& zQ4(yEiO57<-ISO}zq%q*n>tx*^dL!uNPcw3$4$_~JQUJXq zxd?obzuC#%0Y|fk2m8hZc4^@UYk2~Fm$$ay9-R^vsP`@t7xIuHnn(HMhJsLX54RJJ^P<}^VS)qM4Gl-uje`7B?N!jZ{fhaeW zY;ujvGOPIoDXeYOD7ad|c5ZTnW@Op+y}@(i-1@q;8nS(B-@=>Q+$`Y^uB&QORceJ9 z#l2DN%#DcP)^Fk5IQq~kweajaea-xwXt&Vr_XtY^+;@uQ%>QtoS|%kM<^n~*x|mig zle9BoH}9}sF=V9^(laq1UzzU%?gD2fc{+X3*_t+;I2J2;`z=Mjp=1J|1I)_rREcSD z(*u4vpNA1hspIi7pqp?S9D?hPBa*un7ytxFo9^wvPfj$fM_s0-;Q&Sy5Gl(Y=@qDM z_OH8wrGa%=oCd44Pg7bUTOWO{#hNP4g*EQq(n)O9*yHZ}Q$2i@oY;WoMK@A)F?cokk9RW96UUI{$ z(JH-U7;3%pe(5hsg?17HQcMv^;`!7gC_aKSKk8B?OpojNn;Lu~Ryu&1!_GH-fu2Db z^2yMDb&8KZ{qiZMVX9H0y-eU(lF=&+8&@@eCctLpOCu%?0_5{B%UU!!f|=-PX(Xgq zR@eDigzof?JA^5M{)};hhK>re*xOtdFOO|8}$~^L`J(=7ER$D^0e!j)!?zf@a6uAFBaZ~eLZ=C?Gv1=$Dl+aP~_vA z5fRh7ckkXTIC~sM@qNC(kr$&_oiA>Rv2k%E4n&iHla_VTseSS(mIJzu-+KUXI_nO= zuVCrl{!*Q2H)SU{3~+^<>~r{akb^|#$&VE zDz<$k%!6K%wz=yUT;W4wm(~eS(y8C+?opP*Dj}hqLjIX)J3}Z$q|9lG2c;&Q(#|_t z>NXIOic~_?26k&x8lgq1Tch#usxhnZ!uce3LCP1NVO)=$m4Qn_&Lm3u568W+_aL7Bm*chTNKI33b<3hmPR6Xpa#v=mQ< z4KBQBH8Sy!u*HP#h)Ilr``81cQlkRrM&@j6cg_w1>`+0hQiRRl1Oa3^5o759ZVIQWFfXp!2 z!p6Mr!l{c_e1D#VIyt2cG{;`*U+zSNQ1_|`k3l!8r3GpL5vtQPUaf3>5w=>Kp`V># z$(C$PkMFUYQb$sthl1EXN9@LiN;ypt&WuDy*g6FxxGi6kf^>t#LK;I3`bWQGYnROo zJ*r(Zz&Tn3d?cLu%nM3tco+I$3#Elfb+n#+-VmTWx2kEA8t5o+4y$moE1y87z-$g!SGm1!5>T#2=&4R+D=A!fbOskfVZvu>-9XGxD<(lEJ}@#e;*>_g0na@3nPgj^ zovfZvBX?A$5}&g$R0tyRh8{?`vvWSpc3<81e1?zB^6}oPI0Q`NSLk#c5aBa$*(vJ) zL7H?tH}$)~SPAaG^(`(+3-WV&^3wFd@q$F&k@@7w4Co#nqyp8VYIGLqtQK8T8K?n1 z@k6{T8f&&aLW1mu)90|>?7V-CtD1tySBJAiFo2K*&O`gl9@3hmnUUFrKXfp3NQN#- zsa&at)?(dPvlsgQrNqrU=8X^j@66xUfRVWroH$iAZXW6RWUU{-9803C}JD{lLB^VMNgIO>^%>(fjT zRzyxs5KQJ(s>o{#xbP3@-#UJ*>*`Ab143Y})@1B+^(B|owrEt`SN!w??p|@`yW`dP zL+J1?c2i;HL{lciV$Vgqjaq>k3KWIvxOeRH-Gg&ZSX6 zdAe-hq9VbHP7cgk#_?TrAt?!Yw?~)W>Pb|Ogn=9_h}&`lT2EDUG32{BTkfX_SWNwd z^+sW;0kfvPIM!5G5!Uo^Fval|Lr@BgA)mN*-1LVmmSfdyUZZ!B5mRqJ@6ZI$WV!X5 z3~g#8sf8THEEyk%vZ%XaXYT=-b3D+2rL1w-V+lfKN^URE%T#Fgu1Q2Mdq6axAD=>Q zgha5r0&(oO_ael5x+<<@zK(BNtuO!_*YMF<&L=182>|ye$PV~7g9SfqVYrUTzK4+6 z_j)2@e?kUMC9K?#p{^_@3wwhDu}97KHtE$z;$<5B0SH6nD^a$8kwu<=D8&|nO*DaZ zwg&(sA=}p;UG^+`$^5lzIRJ6@L=#$L2E~2+^tqmR^H&l^Hf0pyO+=B-K#|jUy=$z68k?^lUTjul6(f+CB=9=h*~BdjG?0JRS}tcRiwj zrW;9e8|2^Mc|P-)y}FU`8B0J)))U791I%7+m1enc>w59O_b+t7eFc!GqQHn$<;&;# zvyc>ge?M`lZfxxBLDR-1>p1|pl=nH;4244TqW6)&(g8DH9N_lLm6967@yMa2zW(fO z?@|sb(361wdPy3Pt;^I_4;femHW=lp~w4AP8Y5*h9en z_@BNy{JUua5bQc8KAZ&Dk~71Y5)Exm$ni^fBJuU0M&_lXJ_X2wG^Y}GkkC*!rveAI zcDhFB|NJ|y)E1oDe_iiaZcU07QZflQA+m%`p%5ulsM~gK6N}x`Z`Vy`IO25Csn`nu z(~})>Yz_-*l(+^Pst+x_>~}Ki?9&2F97MW03RjclIo?S-U9Wo3%KW4-UnLZ4%JN3{ z%)bEZdp^+0`3pBe80yI9g}gqn9z?5uAdg1D7Z`Jw<4S4o12Mova}5af)TagmC{8LKjf@3HNOP@Fk(LPiQ8ja9RQA{6Z^LnK>X5Do$M{ zR7bnh`i?&Ur3xU3pc$-$vWx=?4)t=ih9n>~Lew7;zWs@Py4#0ve%cr{lur<~k88p@ z!YtZ&xVYpz+$IVnlO7TcBT;xZoSp;t5&}y35J%(KSVRsz_AYYcW4Zogbz5dQsq}xz zVujHT5#HW5z%IV=<_8J&hAeDsJ-}UW4e>a3sjR)&3=yd!gEG5t4DsDk0FBsQ7_Nhf zEJV~xvA5^!>m3z0u?H6htf^~AcO1__q>7M^7t?>n?JWP`$ei>i|Blfl#0Bu;*@er0 zxT&)q*rPY05|azBN7&rk;#<$+R@rqINL)9`!27giwVeNBq%IuyHQEnl=j*1}Z$2a> z;8&ba1WHh!sZ|hI$|&jh%~v@Nxb6mv{ohF{cd?6nP46D0AwrfV=5Hrqrw=_h=@z-R z;_MswGjtZ2Mwg)A-?F3oT^Qh$0UcGcwB@qZ25POf-3TG1kxj_nd+#0A3Iz%U3btBL z`&@uyQ|e(^hb3t2o372?o>GZ1S7T3&I30_~%H6JrY+6Ki-Betit~x=jhZ_c$E28M^ zSe>pfq*Esht`EX&cApu_p4egbg-2$G*_L6K*oS>IR?flNBBe+vaRpooe6UF=gD}$a z(lWKo7p1h)O6e@>_>Eql8O|=O7m&j-#u(1D4I4{hoag+!Q*Jn%#ghCsh3Puers`}3 zFg@Of?rdKU(?(t@DW%H5VMMaQMx^Ol@zB|0oK_cGtJ?oRFA#O=?6tYUlVVf(~r5T%)bIvK5*q+cDYo&R9KWv+v zv__oy8=H2a2|T~i&Pb66c8xgHg(h%FW8|Zmpqw71H$#rtbfvFNd*~Q(=GQ86JBBq@ zqLx{@O%cAwLj03Nc$+T@84N!;KO1rMBVf)FBMQW3EMrJd?`o{WA zi%QYNon_>-aa`AV-syy~4hxl9VJc2%#Q~W@%uLLIOaWW4X>@Qv;=uJX+!U}qt1F^? zEGb|xh!7U+s#CwUT6DHrt(Mdgb$H>EW}Vu8;RG7{j5;3M7`-OVbsdlq&*Qqzek*x8 zQICn7Biy0uhodJw>UmzLold9I>2#VW8FzT$2)cD7NZZS_{X!97^Av+5Yj34RIGwP~ z;fj+c?UcD|nf4Z4@>6n`lboEINjrUuJ*Lg;unBt|-Au7Z^Ui&{9T0cgPAAO8h+|q! zOK+<3N2F)ZCd7*JMd2( zCr$NJrOrBeS$2%^UW_;)t1&KPr?)$ZR3fM$El|{0op8ZT{mmunHFZMW+M#*7!ma8y zb-TLVam%{B^fFyCx0gwChlN`mY(&?WqAP=mJ>L^zkB~Q_3t$*HqD$xYG6~Uzo75@xs-7L4dH3WfHZKr&*)rf5;@{QzRpbM3H))X6gAAvV2W$l~YIM z*(`cjTcoUQ+hWp=EW2bDlv>_?m6X?ac-Ng~f)^)2gwRH-hdN=c&$ z%GME-#5{U0?A{@(N{12&DZVk0iQ0i|#dz0awTfMQkXCD0{ z=Lo!md*0DJSbfw5$qR0QsAfkGA3j>#vrKO@+_Mf9xaS!AZ9%+vTse%eZG6@y*WC_FvkmV4^$&!c*$#Up6S(@NHS)SlKSxUJp%OWm- zETjB6)i;fsw8qlX($doQ%BGNo5emW{9dQICODM0&Qi#)J8N_cI(d8q8Q^|*yf|I8T;BACgOOAX)zKo&4ki$UgiT)%nla$#ZfD zmbm<3385!GlwgS-NH78s@BjiQPMq961R-!@<0J%5Lf`}hAjmyHu%@PkoWpx^&}ni9 zj+1q`kj#PGHO^TM5mX0I-2@ z1K|d8$e~BB=~1(((8H$YHZ?UrxE3-5-jgNpA32{USKv6A0vD2TxJ{nGd2$9mBwOG> zFmM6n3;g-iI6h`@p!w0F0l}Ocf#c*Gl(+_zxDOm+QcUuOJSi8jfE6oNtW+RxM-%Ka zK_o8_aNTgj4Y!ZLx0wzo0yhY`Ap$qJvPbl~A#YkBfFVHS$~tiF%DIIB005jEoEEYT z@5wTJ0GWp4WCdJEPQYz40?v~g@SV(nyYd2_Jt$Cyz(4Yc%M?5a9*EM2NlH^Dy6zfx zXlg}C_vlMYOG`^+#1@PqPK+yrn-2N`*~@|?dsl86H)#ze9S`qN(if#rD?&QDM|5X< z{T?}>CM!9RJi}>nl3T*w+NZf+t7gQ2{9mhP!^EAp&GI$1qX~V z#;9$}cC>YwAt$*E*?UEt`5{S?B)Pp3B}s0QC`n?oNA%LN8Ogwsp=e0f(KH`Ylapz3 zuBpipLUSQBH8dL#u7%vE$w0@+FkDDBz-{u+hvXtLTmad~pS!LBf&>&Rat?5Zc%%qG zqyQ=glP_Sj!L z#FNkS|9H+hJ362rKWc7na=yI<;hwqvk*8=u2#NrPV2m+F8ErZ0@-m{QhI`)nM@|h- z_K3VGMO3Oxa2jz$3Ow(Wf}@lVcYk6FCs9e`F&63Gl7e z0TFz!X?#W)3BJNNN*}Iav zFdZC1P=;{WetbQ?oKGw2mlmTfA3#n{kDB|?oSGghtdKJf_ao;cj5tsBh9)Rwg7dEI z5lzsW;uTGB8gWUKhwhI&Vl$#PM_q*E4QcZ-!4_(2o|BW4^B*}iIX@@I?va!8gM}Ox za(=G8;czrL3&|_oChNQ=Pa%m8gy@?j$}Hp+vQExikFLqAxaDxM%NeKUIbD%I=RP!N zn;aXQzNj5jkfL z8KNTvAb^}#;C}qC-~Qjj?e+h7j{Rfi|6H96Px_xLXBOV$KGa91?-6=r^!!W?Ju{Lv zqYexh9UHHWjHM0^ByWmR6i1#EENYPCQJHf|o?vDGi7o2z^aWeqpj5cKyr_?bzesJs z2}YNY2+oto0;?xKlTKeAo>UZMq`z?OPzY5BoR(J>3AI7f2M#_YX7U6k}R-rbuXu9YXCZdaG z5y2`ni|C>*E}~0*R{9#+ESlj5Oh2QTQp6tZK~uwS>wUei_1ev3s5MM*N|_+#sN`n# z!Y6HSSoGd$bXOWUY?*$suVwm>fPEj0q7gX3`fPQByn*Kx&5A7BV)wc#{1f&it)$$6 zVfV0m-()zJ43xTF=39&N;!4+JUR3FN%nPEf$2{+xckVLhWMs~g@+d8=bTp$Qml<^F zjfTr=Xny<%c|89r(CpYD^O+7vhIJVkmP~HR$Q zW5U42&ws`Q69pib&Hdcp3VxA0^<&P(O)fJg7vxI9I znRDFnSyy#Htgpp=NWkXK#R+QMLmp>+U zZHJ7^*J2`D$DP!cIg?Y%oZ~K&Ph~zQvUOb5718n3HyaLnQ5Gw_S(hD^Wv^X2%Z}=l z)MH(yiy~Dou_tzR+##vQ9^*uI%ooO-N66Kq=?dcRJ@Yyw(!1=E+l|WX`cN_ZgWM>rP~| zKBY{TjxXeK9V~1u4B?`5p>@28+;TV>d*ZLw!l&*COD4-n-l#-DtT&M@a~@xkH*h$K zI{Pe|F4jjhT|2&zHD$VFz980Rp8X_quRFq$ad9&C6baJtCQ06)N#Mw`&nTvAQrVt; zR*}!{bx7{zyY2|;lixLHdu^wV@+4ZX0}fd3!-20 z`r!#;AeMbW@~r8)AI2jEjy=Db31aUiRy!l}liIUyGHfC{2n>o7%E|O?FV~aH5Ols7144Z*O}mD&*HA4@JJ_nDTH1#MY*mxm_dH4Dz}dYo$6xF8Ap!ez?6hUNXwaw8bfG#V(&$T{lAL~VcSPGT?YchZ)_31N zjE}-UUGJ!4@6;(d-8EoH895TT>J0|rr{D4Cu zO+(aEr-xy>f>75^PbBuJPEUZ?vpW5@x=$UIL)rJLEW6WwQTcwQ{l>oDwH&1VK9xfa z$?5l|?uh37eq3gNrQ;eMxnMEs&4!CO^0!+@X#bwJ+)NtBS?|uPO%L^jC1~xtxnj27 z33}Q4$@?zlh$!i_c5O=LqD?;1+>0>_Q=TmNQfl$Fq4#1u@e-XmXPjWvB2Ik`5(bAi zzN!~hsNU%2MZ?7%jMAZ>_#xFuF13~L;wi+ka;wn~9o{L$1v0SgsAY?k4r>>+i~5?b z`rEarh<@M%Wf2wXqaSRw)~YiD93Oq=ov!d=F$8(4{DM3I$1uUVBLh#!qoV_Yk6J1W zLBL=sB;^!QQl&DzGkvJ5R4SE9^+utt>QMKYrVgc29fi6IbrlMgQmIlXm8w&zR45e+ zr9!DxDwRs5N~w-QU8On-bred4QlV}-%G8-mCKc+bW8D!H>atUUN}*D%Qqj(cef>&x zdX-A0QmITTl**)1sZ=V}Hxs8)sZ=V}PdS`@LY>}zsh^c#dBLJf({$v5#g(pNePHs)M;3Xy z#YYWr(0miwU6JV}NfCP1f3Ln4EHARz~YVsY3IShGKDeA=KFE5WWD@JspSeMV8(wXS0~q%-U*go#BVu zb01WXJ>)WzQ&-w4!A?H}8!R<;ioHfBvg>zqLm##x)`}kzu;b$gVy82p0LvD=e9SHS z7CqSZSc`s0z_uUQ^(NxPIWy};Bn+AH%M?lWOP9)$((t5`T@=RdQ~Oqf~3<$ z$LL8ZrSwI^h&xs4py$6BaiHK(zO+cA4oKP+OKF0#UMhJk%sr&^;hj=s4&83SboP2B ziofuBQb}r&BDqgW({C9Q`fYdOk5p=VY1g<;Xu5s!6>;c$pkVBwv~hajllFkrx{3uc0p zwWBeX80^$-mkH9}P6__3m(*vIlFWgFn#is~6HGD(4oba=Z2a&-sX`M>A_tz-dNfQ( z!xXU}KKG>Y>F47LY{PtZX@c~2N$^w8x+AG${8Z|xR1h~X{!-J_?^9WJj5{$z8~W|~ zxB{<^A7)6MBz4uX?#K{cMy-14vQv_}>T4kZTXoepW0kTK*{TnQ3DO+fewiR$rB08t zY%rZVm&!a>;FOUi9J0dTY~!$S1YrUml6rVqtiLe`HV$bR9Om1`;iGam8+H4IK9ysP zdF=3grcH{1hMBI^=_$PsB*Ju)*{O#X4hu)@nN7PyAwwt|H6#q(iL**cefNuk%9N(I zQ(p`dq@SS(oVx0vg(Jc-T{=!@)zVmoq~oJuCP<$lQJSqXq^9?A1*g6s{M1!nh_8A` z!xWLdc^t-HxI}{KdwD;l#>dzPDUE#~YJ79-6wV<%YT27yO_2KRXpFIKj6XXdJOrn{ z$qvYSDtA|94_}qacY>!6Fo(B`WckEwDTawFKnPg@l*d7*@q z;UZwIq^Vd3RPD4jJ4-&+T31>Gsom_eQ);oAQP(DCG5KbXL!n~W0;5nJl39Q6!?|?? zUv+%dHx(-N7;$2b$}u@ay?ZK$sIOG038GRzJ%#!%&L=P5oOM`rxTLjj$tqmMrH%?0 zxWEOoE2THgyg6JF!?eBJvOAUfeyQ)n^nEz{-g@hU($l726WLUU7^X2fv#2!0OH*)& zQJKSHBnk<`cvLG>>bCMchKCwaEfb_w?A5q!2b+pZ-J^WxNhHLb7-f=7NtwF*DfP)P z6O@h0tj0OKC!ruJtTyE}x{1!E6ys1Y>O z5QDUq$mcUeT5074ZV0g#jKc)yrxz@24I$Esp+mXqBD`#=Sgp9$x@znTr5Y`o7LSwK zL6XR4D^{O(R& zj-zRr!x)Cf2{7{*oR_FGRt4uIDteO@NGE!Nse);-0f%VAkRdqOP~>VnG9pO{w){5x zCAW^q8lLF1YQQ`_yCPc4A7tPVTY52Ov3?M2Yh#UH3rUOqNz*TU(((25WZ-ZKPqQY7 ziLhkJ%QM0GA7n_PD}#;Rp1sLQX*e6BSWQFb)GFz%y)s8iS}V;2Cl@wptO}vt)6C$@ z9f8wkp2i9~1;^d2EXHwo$h4UfH_Zzy4%ezfKb<9B=o!SDGXj=vFLwHcG>6j2Y?>{0 zS>@dbBba(8NT*O+>l01;!i_euja4ltDJ_Kwv!s+1jiS*je1keu2Q)q67;WHSLV+xm zmDV;gEaZA}nKf;w51?rA$6C{eZ+d{k8Z<96i9Kr}nL+(o1AA>|;yLNp#3J!AscC+p zW@Qn&&O5PPzaaTZkHB~fO}D4nq-ppJEPFMYxmw&W@)`rZbuipa|b+(sFH@ z<9G22b5{m9^wFnbXu>8fd1%=~%NZcVFu{45U499xm3>HTW5+%oq|96hf*`2TOqbL& ztpn2RZBqM%iG8udmVJHgfRI=oCRl51fNc(HZ>M_00vDB1RvikoU9usul=39;C&73> zw`@U#v*9c~Yj=Ptjj15XES?`GHnFX3q{`}S~2&Soly zw{N(8^%~BQPCZ<8Dy2fF9^+2TLm-{PI<}jxBWL+lmJxq7d`-`XF@!uOCD|Nv%;82` z?FQ8^R;xtznLtYE&Gp`QpC+)iFMIc0e6#FK+b*TdTgG1mWpaDF89S~ zhoA#!0k%DK_UsF35U^}1d8W;jjoA;&TI*`=)E{jCEx;j7%PQOBwmm-j%p#pUZIqvL+-WPiB{-ih0yS?}6Fh4i%-Hk*p)9HO(E;3vDG#Qy~!nQc-9UZxBk<=Th3=Iyx-2&U#dx4)6gOtVedwLAN2c{Z2j99@0*q3`7dx;OUBmajMV#M#@&M9$$uo37;>apdoc zb`pESM(KfqXboHTYi;(?n|;?FQ^MJWGpH$>h0Cbh1}>XP${cpnC7 zM^PC&QTML);NyYV4QD68D$m^`Y zLG#?hdF|V|bAi;|=Qqq@Rp9R7yjSlXnePk`2DV+1)xR9DY)>e5FVcP2a;V+&POqph z%%M^Dy35W86FCP?IG^g0%;U$Sa!~gzVYPNnYgC6sUi&F`*k>g6{aQulw z&gIq-?x%ACOCb-sKBy|Ph!DhfQ4#7w-wmDX}17dwKh{j%2slC)rSvpG?ZaqG7UFKM3mR9NDZ|YO9o4eeL?S>YsG$$b9|$ zT1VuAb7%h0ODHt#&^6p}f%lI?8)TRw$|#T(${p=Bdo<}%XD=?s#FFUV3-2O4x^T)hhgs2AJ?3(v&pQXY`kL4 z$oe&rUAvka;OkHt@Mg%zNw5tZ%9obdLMS3^^NCQ~Kl!5TJ>b*!Pa;kXvh@X+vuTg_ z==mNmRIleffW4mgeDrKPN6#Gk@Iz0b`AtUp>O*T#Y#}K?&wHSCqyRl%fH+4->n%Yi zJ9dn#Qf?c^^^83<2pb_sHy^Vz;={+rr3Oo>BDk5zr8g6qB&l>^{$M@Ko@~X~O|MxC ztI4y!`Gt-hlKMj5kI4%)_E2e>w0a+y?F8#vvkwXO)q2r*amfOyvEO7eS^I9QVuEzV z!t$(QUYX$irj-fOyI_RFiG@A}8apFz%=F4b>$vF3V%%hwWt%B8wKCJ!%CcwD+DSA8 zcPyXlip;+60?Q1JX(kQJisqy;ZLozj-n_yi;4sgSkSC&p$G+6fMG+) z4>w=|45B>o1Q&Q_R3AJ*i5*k{#tB|ujA-(O5mGRU1BxV~+*RdFaDq6{*Z~cg@Dc+g zK=GuCB;N2M2~aFS20s)*!?=ORg$v9ma)U}3LOW;vv44PrTjGtBM2O9M-)sL^;JOlf|OUs)onFL++e&&U--?o)gRCAY}^?FFAl9LW`de zQz3yCAoKtX6F4Eu&@l_E09zt%FoO|HFaskXe6Rx@XnDg3B(NdJ6r`YoBs#DHB}lQs z7iP3UgdWUb2T)*9527pqvxD+ z&QFMoO-KqZ>HvlqWT5Fe@fc)a1mM!@$YqQeG}e*J7*@pCYRceNI7$?ZxL5&|IGAX| z0Y#X?2~k9G1C2HA96jf>TIh^1MaCeEkwnj50~BElDHaAZoatDrtWr)nYpn-mnm$I* zVm1X^l#^C_!ZOYU7~p%oBPN9D$b|_)y+TJWZIscWyb^DgeI96nJG<%gG)o#yS&KRH zS>9T!f6~*4j$RRgt9BfFY!MyV7h=~l-Dh&9FMQ$wd8L=!8+&fIj?5>Nrcy0iq?wh= zZpxueYEumvIK;H)bov@HF#UGw9+9HdyzXPoOwj%SQ!saV0y+Q^*pstY)3vouk6*g` zwr$(?+5v6S$|+xNf|dvzUgo%aS#4Ppw68J3+qu&%yOYznu9Lyrvd+lX+hbP*NN-<^ zI3_syUh9tB)*iNy++)8V7n!g{x5MVfmtX0d*-$^>PA ztByG0)v(?4&2&@dp*Hqf_MtZRJj@*xl7wW@blD>+T5Jr{mZ+`mP+DpG z6Ur-XGt35ii*cyIa@NXWO>;+vmzIO08C2;fYDX?(G|^zk1rQNMPGadfllZd5x@44NE)>E0<-QKk>Wux~L;kjilHy(v_UeFD?+*Rz>jves7Vjs}>kOSKJ| zBOmJAhLwAZiP^5vZ`)SesBOp``xtkyc`UqH4v92V(H5<|zPR#5 zmDcGGHmdrw~V_K6=Gl2>{t8z$y)o(^qRWK9&2BCtW&bJ?Tpxa z-{xe|JGt&;IgD|rEOjnREaBAB^4Y65j&9rbxh=JqYqY@K7d`>y!)t5ae(6rkx9yhu zjXm1A?N}&v+PFcaH`Gp#Vl@Mk6jYc9eV7N$_;ZzB2}3-*pXJ) zw;CGYi+yrPN!nMV1+EjURT`_3O&8eMeZ-zPt91O~1AT(c6A+@8CRB=)C_^?xk#Ykp zEF>&>u<-E6BGfJTKY~b%++D3$Gp7M>bcPTi@k4ZLZcO+|@rIheVOX-6Qo5=Ysc1mOvnG-)h)bcjSC^AQW zhCFn+lCKX$x$$Db5WY#Ex&`UoCtpvoMTSL}E_T*;<8sPXN!?4n#0Sc6e zYeZ_9)KN)N8LOt{0O1BgKUj`Gr#0B(g#ooC>uIIaN_KOPpDf28@C_WkPhr3&m0|AC z7kv#l@X&=&6*{9M7bnQkVf^BRGP=VErb7v(=`lHVGAT?ZWYEAJxj119hG7S9rmKhi zx!HbGWT#y%<3**xj$FQQ!mEppTwYb+bma1d7>()9Pb~)YAeC_Cr~U!0c$q9eJ;-GF zv8R_^BL#jdIRerdU|5;*g%eM>kVAWZf+LqNyf~`Dc3tYq;cB|vs38>&Sift9??-Ctukovs5B0i;C#YSgaoeb#!=IiX@t@ zDYtLYEt)p91G*Q(j{QkvSd$$Z`*N6It!>H?d-R!6RwIUOJ4u+0?(&hEWCy$|Yy`fv{MQz_N6m@iz? zF;A@;rNTNTtFKNFl?qXhkI6x(Z^^3hH7t*>%BcI$BB#$R%d#xXg68qNc+VvT&U~g# z_N)d9j6J?k+38sI7_Q*#LG!AI^Q+Ss)U&Tt?|Wk^_1XcYQ2AX^5a1I@f zrYwX?rQ@06MFwH1S%C)~#cm>4vjXi;*~&9wc#p8%82e?sq+`eWZ>v5wh|)Me5N zR%pX|pX zOaGXrQ&&=3HRTK~&ae?dQ&5>I%o?bmZ=L zk2>S2&th)k;1(9tqtQMB+D7{wb#!gXs3(*#y_aHd`*p3+cHN$I?{Zf{(%rlCyVMsV z00jFKk}_nz0i!2X{u+-scrh-vs&i5dw8cN$togQTD1LjjOf*!IMGuVM7ZWR9Pq0YhRGy_Y3&h9d?DT3I! z^UTe()OS~XSFQt%eg05JOHo(L!b)=R6^l!z zcNkJ$m>YdvG`Y8LUk5@I4}xp^VKam^1fDq$AwWak0{Hxo*^7{@PlN+O$~g{)AZ1L9 zvvuo~)*sT&Y8_Q%Os+mwb=5&m`MA7yac+zW%AZwzo>Vi%rGZK)kfyMwP4AqFf0%{_ zOY?zBqA|6lJlkvP$cfJ1wPa)9)sj*D!fJgJQ(Z{Pe71BGdq1w04EfrIBdHOe13zd7 z1_wm6(P~OTiSg#)nSR`nM-1-C^#i6KMQwquVP8-w(Qx{d+GVWlMq09)8wjvF<=Z3l zX%fxjAbVw&yzU=vF=>R_nT6P+{kugEV{S7V9G<} zbO(b1hzJY9kO@-qZ-RSB{0#o0%kx)0Gf|*0CQbf9=qcJ~OqrhH)^ls6{_GDChK1Wr zv`$K*^pE5eS5tl;V5qzitATw$1a@$9bW5m;f)l85`*ak!hpKf$Ws=OzcSa80oKinjv^X*tOG{@0{=-Fp*Xm5^{>>g1qF)BAOZ*K07npdhnN zEhdUUyAGxl5wz>zrmzUprWQMPnlKz!kmfvDKl=o%O*Sk`DS4;4 z^{G#0OcS95M4t3~3Z9&hAtCcNOF(~ig<*LbkW3*@4w!zTtdeWX9OPkC`7o=$xC**y zEuq<)Kqxh|tre$;q=Vl)`TjVh zt`P>F0DW#|zBHiw5pDFUA6YRPPgk7p_BC<9xRx`!!U!1MkxBz%2?X|fIWg>;j|EPw z#CZAe*W>Q+JSA1ybjVemnJf-I^!|?}gS1)$JGi0e3?}YhJ}6jKI*LG(B>ZJk zf>;CsyROW{Mznv-%GW2fWb;E@D#I^cWx+aPc0Qw787@FoK zpyzqMr|dhV7P208bz43yd7WH{Ok!c+6}GEJTI5cZq+|XjtH50}E9pzVo0V8X*QL?1@e~tcjG4 znCjt)Tr`aF0|^Mk0;iv?FhzG^=Qj~}lbyxErkkUL?Yn+Yqi%hYz_i=N&pDugM=0PF zvz2r-E^6jLq=eWMb*3(^!_C8oE^xh0~Iucf=?k?v9!*GFd+vLVymKd@$Nhxp-pZ$D%|f1 z??RKu-7oy=4ZL5z^s>8DSpU{t;pmh^4!v&RSfVfE@1EeRq&xLt{JTSnw;M2zJM%b~ z&)Bgpfc4XMv5`DY){p_Ma^%4`-!4QyTqkIf9YZHP;55XGktt$#AgRZ6@szJjgu_mx z$9ytno$9Mm&PF6Bum?Y2Wz03Mx1i)Vnb^X3e@duu2|v)MRo`&X*&lQfEUkXfqJDR8 z9GcT`?9J`h)@l;byr*neIH*C6)iDv>Ncr&vEepFLcDOtS*|SA>9|$}}XrOz#V<*l` zWm<5XlkA$QqS;-)bTHGR&hOeGWX|_N zyDpsB7c((-P1G#-WkGOscTLs1iNaGg;vesTlt<$H!T$PMwF56dMg6^w+S;&@2SaQ% zI}2r8t%^s^y)Y>ITe|w5l;j1+@+)!0;4O#wEU0*0BU9Pr12XS31_o!QVuZxYdS@q; zw3?~g%B}4Me;Q$Gimu3e^JR&4Z9;O;d|;OTha)NImBa_@9$*f^$9BDjJ(8_3uOqY; zkwa0tC0(lCG#@4yK=S_AW!rWTX`Dl_>C5q-A0(wKeJ`_OAv6^Z3yQGrx_dH}@B{9N z?kqYBE)?G`=_Sc9Y#D23me?K>*z!RkQr1kD2!`-f(xsAP}O{;G-6lXzBRPSHsmx*F^q-Hz86$zn8Wy_w00)8o*PHEkXpnOMg| z{vI#9$aDm@fvk#Lz_a#6y@OTD)2Tvf2&3FYpkTytCW`%ZKDtaSTQ46&h6OAq41J=% ztd8Z+-$F{_802x!5n;^9FD-9L%h zyl}61oVP}G=z}W6y=F58MiV!F%|Z>CR&vARL)s2j#i{zOZT~J|1K;BtXPp9YSxMWK z;6y&?|3VQB&=z&4bac?O!3j<}I{%#EtBpc=dFJMfbKExQUZlH7-fzYZ0J28jrEV{} zXwbBm1*S&Dr8^Z??xSFooBeW32hlzeJBf2Yk-Q9@y#f1%6bf$u+1`g=sz}miA0k`LhJ`El<;>JUp^pe zi>)L*m{e*%ckM(PZ7{dR1O6CC3Tz_zMpPP;lWwaJ4riGu{B-3jh1#7Qo=_Td>M*D& za)>Qpec4VcnWoIbP*oscKq_QeZ&|+E`^#8uu}XkVdgBNnPl-Qxv!?RUp$>v5coQilWBUY@|A?I;L~)qSmmS5P$lj0A{T*oCC=l$-&E@8iGz6(;gQTaX1%b z^CZr+nwOy%sO1%D9HzH4oImcTjwC}`c*eD&C(6wZwJLx!a^gfeMWU5^nI`COXpWMQ zw;9I5Nr|bf@r0)4$RhYlMY4ecDFu~{D~i!B^rBG=B8E{d3h#sXzhcvnLdCve_>12T zF=sx9SnAsZ#+QcPlO7ps4^x4z02Y48+y+e35MWNJxn6{f1!)OzM(8K$g=?sZ1#4vy zrmSBY>eNDHbl#M$aTt;yZ4SMJrPA9C*yZ9qmPiJs4y#H?@YGPQ{EAOuj9wF22j9EH zmCDwNz=$16jUKoU)NCn)Cx5J6fq667n6>MQ?@>F1EMi6p8?y`{PP<(I#{q3RNQ;U; zRRVb6r&O-41%;>&Fifms5hKhkPBMYv@WbX%LA6QA>8D6E_T5s9sg#o3Oz7t)m~0xy z{*aX$BOf|4HF%&3urANrsY&KF7%ygGp6kRsm6B6HR#zsa4olU^`!*HI&OM7FTU;&u6t?w9B!+I?N#2U zpS0?Z{Z{i zfcNMJpF#(zo-QIPlQQ8a4-O#gmVHpStOn0x1S7L|UwZb*>g}u%rmDfuL@YspwO&t5 z``|+w;G_+rh&7Q%5zB(I00lfFY-Q1V|Xr2n@L+ zfgj7D2ZWG%@rE8c4?8~$JcRwQE&VV%p}k($#!qt3Wbp)>2o@fM;!l!%r2hEKf;FPN zJ#*XIpdlvs%zkb&u+s&V;L!H+0sN+31c4Mf%X^B|^p*p?9XoAbvU*b!1l=xJDtg#I z{%_R zt0o`6Pff0KS0`1?!YpsfWD{fnmQrB$GESGQG)K73!qUf&rGz`wQbHTu0{%`E<*t?c zCiEcRg8|z*$$N2|U5NiMb2pgyOD*3Q;|9G`gd1gq|Ek;R&CkvXEESFN?!4m*KHg{) zgH+j^xjC6AA|#f&of1*69%y99Vt`MBioK66{_KP8h z~U6LvH<;yUshFf@};AG+(TeD?vdEK*o<|u?QpW+Y?Sa z-D*E)OBU)FtdBe?AX`Q}qOrVbhD#u(yQoI0U=IN7@K~DCAVy%Cz$(o8 z45(MRlQM$m)eD!rNGi+Z?ia%QufL1qjkof`}p zX_?cpo`z}5%dM~!?e!3x8e%-Bv))vPj?}J6k2OyS|04k2l1HrQ*sj}DS|{M>gx|;8 zZhdSg8b>!o=M3U$=kx9$pXLU@VV^yY1dy6K9pI_0wgL2>S!6935Mb3H^@uVqL}a}nJ4x%Mdh##9 zXt!&9f;@*u#0s(!xcs;ZDS`owcxb zmZd_uLISDUx5kyg_8yuqUd>8Kj<17MqUFTPVO+gXNjkW*P_dE%X+qd{UpAtMqEj?! zSEmxS@^Wu0xlyAK1cRr)r`{H@Vn7-OdkVyT1H!K(=Yye47jE=PP>J_dPKI~~ESU2O zOAD-h|HWK~EBA}H2be4ii7cA9Qmo5uY9MF%cUFb^R4b_DKWR*P-LcI0MbtVI+>4VM z3*M-eK|Cw|RuCdVGr4{OcacK+^&=2I-2^+*$R)ZJ?H?N?YSj76qS+cEhakwu16UB) zgMTpPOkQQwBOb>e9C|F1Y^$!DbYJC^t3AGc8`h~kg^aY9-XTB#`D8c`h3qJclIUNA z$YTiZLxlU%`ve;n(qv`Fl%Y8-9EGUYUnMZP%LAGYDB7-z(MGqwXwzY+YjQW$!)AoK z2&@;t--!qa0|}#T&ch<~e_Qd@(08g^izye)!JsivkqB1^E+|)_mj~z%KmHs7;3I|J zeEUk>5|87Gj3hWUK$Exgw7wCcd zm`{Ex;5Vil0T=UCWVCNv-bKzAq~v_4_MduS8b$wE%~1HFdBu4eQ~K`}l;oAWo!@bU z2?;ybROI@3&8Y_9Vvfgd(&I-Qd@=nH&l;vypE58-nw9T;I8OC8ZwwS&N$q$bP?!?dc^{e+3%!6o7OSG z5s-f=xDR?`ezz4+W{65QX*YnU8+<=O?Ehod@pqd+HhK?C-9!x zL&{1*sotR~4oOa8#YE@M8P@;o!|`VZ>$%v~d>>H_<{`d(F;0g<;gtgVvzje7p(HQx zNwrv+cKw{IDou|RU?qR^V8dTd>(7kOo3~cuMCLYtvE^H}R&du~Xds#r{SWI`PM?%H zTU$3trigV!^|>g+Q4htoM@FqL9w-bmZg*YaxB|MAf>mz~`E|NbQgTksxaY=$ta;!J zeY6(mV6^dqOC`6mRpa!K}x6R`_VamnGq?tP^HxFLKje0)pR^6Pbe(BIl zwGg(04!rie3fE}vp)hbYQtR@=qRtA&8i)SHE?UP*PVctwFDnZ#UQ!90srnYc>fJYl z0Fk@1 zQE(<|=6<5p$`^6+M?f|u82q>4oppZP-Iq1F)||xL0IH!o3wt2DEsLj%zTADFFa%7Bl8x1cwO*_OItnMlPe`qZwNy-7kr57}mEhUp+$egeq zNw`-{EosbSTSV(#ZTW@DFGqpBg`7dyy~bFTjhXsHxK(9BsIl+xc7~W;=?S^;mbiJDPxxRGSZX35_P^d z(D-niv_h!k>~ViY=-0B9jLHz^n=R0)4sR7(BBnc_t=L(QIjR<|Rj?|%w3fVLWEB~ zZXCs-c}PXoR&IKH9Mm=D^m-&HsJckKVzFZ3etm>F1=rm8#QZ40L<~!VbdKe9*b` zPV(N@)WnW+3*T?0^_%9qt$p#n$0~=`aEMTNk`O^ zn^I3j-LXno)<);8#UvH?A)ODkD_(K$X*y5``Se0#$Ai_{gj(WnmT6R`mEN2NDa5zW zag%L&6=R;Sz=)eBcr+yGxWu9* zae&egx@*leiK{mgvLTXpoi{Hydr1%J)0a#vx?i0yS~)m6iztg{Bt9Dsl?KmPD~;Wb z&|1m;flzA5)7R+@H|mF!Md`$4`U(rlF7}8pv=7qmi)Tn@jNkoJE`fk@G!4B%eiS%jqxfH*XffESx#TML$mv*++ zD@;{Cj3l_A@_UXgn$yiT7 zt)X$uvgUpOyqk>Hs&ye9ck}nvia`T1!Ujhu)B%Sn!Alo^S;N5)ti~Sqg4khr6Eo&P zH4x1tHaOIb012Yx z7S!VzOhcKYF|>M%u=U(+i8quJL@{bmv8o&ZF>e|-(}>50$uOcQxaPoeZw<#+GR=aC zu8L>E$?6r$TDnZX=%E3t0Vl^{ZI12H)Z)5Rz{Y_f{qe0&i6svN>gldg&8#}_%aiR; zIlA+6CnEyE`cb;B6{qgyN*@dEG-_6iuz2hxWF-+4%Wi)x@W6hH$6!4ej}Ib>o|^Yc z?Oxkal5_&=Jg*MjTaY5Ym!c1~N1h2tdZdFsht%U#o>mR_VGw=1!WNphEYmx`ih{-= zs&zTkzt=^xxQTwWKN*TZ;Bv2Mr<Rz!+lov6wCMAq=KbDEBcHfUoW|Z zeN*HvTElvVQeS5sQ0IL5bhTpqOqbU+EnwF;&xJv}ZR3p@_*7oG_BV)a9AZ%wLGU3e z_lBg5P8js*`Jx+&menO2=RHpJ3YfJWA|KLRoYP>Pz-stgMh&{|I`hC`X)qTb*Wywk zqt+Z&c(nMyM@9RDAoBmEvI6;I!Nw#;h7#(l%VE08i#asH#$-%x0oNd7W=_J^+}916 zCu7TK@2up|pTO^qf^Emj=`Mi8 z&lBL_qLK(V69*=bZtgYQ&KEM~z=3p-4jEWyu+x1X4eDR6&fg@9{)V~Bp?pG51HJ-r zBw(lbj}`h{wWxZcDS3q0UscU4 zTUc(j_6q<^?mZ|;oQPmD47AthTS|;UA3}21%VY_Q+-5#Z9m0KK0|q+5d#O(}AtkHu z*?084z?5e+9}B)%V0@}mz4rCCsVjgf&lY^orqGT=OgmK=mB6kyIM%(&>9W$orVtei z%Wd7xF6J6!$aDfHCI{B%KLkXfyhNFHy#rBoq(vpe=0KP!M*h!MMMHpvqXI=e8|T7U zsl@9mD>*N^2x~#>=-z3qkgjJW$%efLqDKge&-#Bht!=n2L1x zxsvixEhpRR=ohUAk$3stuDwz3*$Rm1bpC2S^OG>f$(P^xUFhMp=vQ#@fo(2kPx_xZ z<1Jif`CO>9S@G{^+V&v=bgXLq53s$^H!rf&dTR7ZYl@B&*$K?oZtJDivrvwzBA9_f zY$`0$M1;lpuHS+}dd~oWB|aR*H?>~60>Yfs2zvXVr8fWd0L&`unRt>Lakx>yX~~5#!<$( zgQH@XF`a>(+ORihro>_dh32H~L3^Z-%+Ke_ z)mN?$d}mU#O^sWf_JuD1Bn%MBZkk6&1gCP4LL*$fGd zF$%x`;ZH!X71(EuP%jfJBK=Hg%Qm~v6BNr#gGec z_$IkehUGY&Z?vg!LPk*IaMo);`yTG=wFnGExd(%{`W;SM@%xEc;!jPF@8OeK{T(`D z3Wn6e?8aHyaxuWTG+D(oaATU?OShAs{RuBrWMDnubC{mtNHricOxjX+ZDIOObuf-F zZPV`Yp05yRkO^c8a(JOv0!i;v|FR4m`-hN(b_5HKz(wqto1~^~IoE4)Xws_uW1Cr* z?xbb1>XLeS<-j5>8Hvuiz%McYqcSmQ%}2!)2Aj`m2{Z@$8#790<(wd{4A-+823~{5 zTL~gMqhEfj0&94$%%@_j*`m$Evf^VW&HsNtNjfajW$WS$Q)hu~33el6q6m^Txp2TS zY1E(+U9;%TYQ|e5>*mqyybZBEabvaXU8DRf@`BB2+xV>lD9{pPx6AIGAlr*21e%jd zbMdueTWqU5=&vRky(VHk_u;SS+0U)JV-jHKO1Jb9W(>6Y2<_ppF_z~U7M+=-plpLz zSA45l?`YHkf1ou$OB@G@VGrL(lN)h0YJ%0X5wo9QB$`|BBM@0rEx3+u;5Xl}2G5BK zx{rf5_U%wQCL#B9i_dYaxie^o#2HzS_+42-99$%X7`czHjHs8c(<17G6VT(vRf!7B!jp$SdC4L*&70nl?f6U)XN~c9ljLj|x+l~|M;-FHov0&$ ziFHlv%AQ>ps6-an?$aBtqAclTbAcneSGJ~(XGZA>0V3s~h`d3ANC7OHs~UD?48h8A zZoS?U=nCT3CEKLJ7bxm12a1KG?-#p*+8JwZcwy*Wks?=y{xDZo=2H^uaFKDlv~axX%B zW$=%;^1_TB0DXVNu*v~V5q;AXu#2p#-sgPk1;B5Mf$9_IP|G4rleDT6#VNu`z)ZNb zoydopVGKKuWv_`ijwFS1^|4_9eOG`BhT7VSvZMB0L0ro2EN6}+Du*f?; z(0HUKn6e&+YuwFv;aTB(Y}}s@w1NJl@961G2p3eTK!P6iL#JjzLKej;0b%19ahM6z zoRhqDkXkZ3jD<~sE92~J;uWFYX>W5*>_R~2twzzsBMYn?Y!oX71q-30_yKQ1MMVE# z%Jbf1|3ij7ZIunhlo({D5>;De;!sJshL-SJ*^FBlqb}{eTHCJXif#8|-XfCfx+)bo z-47BV;8d+;w|cEV4(;YP_ZhVu$OaRSYRB^O3zaepz*V6hOE?(HY&Xo{ulXgqmc?P@ zw}LyxK;E-P^vpmuj#N~cu&<@SM#HxZ>S2vRs+z|Yf|BGx<-CuW;|ZKo4vq!)(Z@bJ7xT z4iQAA>183Ml0oAVhOs`G!an|q6&%}+UE{nq7=G9rMoBs{T&c|uJQuAjk5RhcPe~^RyjdCqT3{ z&i=!%>3_jgYDtLx#k))dU^>0Yd04~IUoT_@J}J-PauCcJ)>i=HNS~{ zRu*{saZxbFVBVYXvK+`~#K8fSr;NBUkW`CA{vwVhBt;ZcL6Q}XUE2)e1PJr+!<)9e z@uC9?`$6n#6b7POG6@qvO93rp9wyNVJDY?(A$A6B_JDctIO$2Y# z$bifhY!>2m6~jA7w-=JtjXjyU#NZ&t*$yh8c|s(RwM=Oi|N3`YUqU=Y zIq^Uh`p!WRGJqwlY-mt_sv}zu?M@m22^wy3064UfkX5V2*1rr|E0QuhJ~qf|9)&dqdU$nt4})EP^-!{h~vC8IdL`%&nSuohyJgw%O&d#j(OF zMK|6hupX)1PTDc|f(vTPMvK!xANmW3BgM^{#+xB*cCLKLRKEj8=vX?`fF@b_(Qxn^ z#6~MIoxlQGqs$dg`i=9E33F@QPupXu+hau=$vc1}Ax4 z98izcGi#Dgk(xCs6Y7gX9s7UCz*PCgJ7gKR_MPrJLtzOPqdlKsFzJnkO}`pG&7Jvb z%BmY-+;Q^CVO1mJ>6DgiJ#u^sUX@-`QYjsk=HC=qX3$l***o*|pmsRh_+EB3`YIYD zghSK=_~{U9XF0TfWuymM8STAJU5TAvy=xK?aPr9-80w_ma)%M6)0tA#kPbp}Bq(!u zKwK#9Sv+3#F&8U3H={72I$0Cp*Db;Zsm;J8H)mmFsu3xFt9fm;xs&NlQ=1_^!Ha+H z2X+U-;*c+#;bN_gXV9S^Tt z_)sfY&5Ynq&4`i;hP-6+DS|YHYtP9h@52L@63g|Ex3Qd5FlJrbR<>{!n%&df1;#vC zlX7@iNA$bbIVI8STs`(osSb&G@l~nQzEfUGFVq?NDVvSY5;VPx^ik;eze436D#J-{ zqMY&%+#(g%MTPFQ(XPr}Bd-o5&h! zMxcvQc(myg6=(`sU?3>1pH>qkk)dJdahW`CTjKe@?n03BKjOk+gIdO2BkqA8LN*k> z?PP}I|7&0D_>;Q!i%7!7g20Dtfc8B9-Y1RK3uj+FTD7w;=SUNiT@L06hXzEywU1>b z{N;z9acAGR+$v9Lenm{Ri5?8=Op40M@N2Iv|C?@V5B&s949G^KvCyO5;BU!(K3xz& ztnE2UoOI#yUASY^NZ7+#HucmQqD$XV)XJH^EfI%*3e}{oJ&UYxno)-+LU5ZuiMPE< z1FF&z10H>F{{B-_@*PmaV++sHw6*q6b@3$08lI+0Piaq*7})eELHS;L+F(KVt_qZu z$)X4fGxL=q6q6{ZGX!nqUS){}VXxHsa~Y7DHfG0c;{&j1T2Crd$ZaD%}^?tyFn8wzKMx~dY0gO@L*z>H+#ff#>iAwtpM>QU1^lTa;|46nTzqg|B?SPra z;)x-k9PGK1ED+BY3ZRH!>PShl3J{g;cJis*urBmqL~4zp(>oyrQ|INqA&{wa%-#?R zOkFuXf7g?2s8l@7DR%dV37}-ocYnY#OqEV*lS0riD+;Qs*1m-QO$4sa^oN4S6vhw% zxtbABe{-yCk4#6y6=r~m*v#l>5C_27BdDLwG5y9R?)b8#nDA+v|2$FDlM=Vu_SA64 zA1XL~H}jyigR3R7s08{`G>Smu0N2|3xOeJeeX0yfpj}^--iVI;)g{PNB}KOnVC3@b zNmmTK<4Usamq-%L#JIa|;kN{>eZ6fR?zMHPsL(V~98Zpo^t|X%)L!#<9*GKxU!wuE z^6!FB)ax*E0J`ohVXuhpp(PJleDJ}B%i(o9dG<8pzh ziahj%Fx91UQ$!sZ27XhyDXvef9T>%}$g0|L_fF|%Cfs4H&qvKOQ1S3 z1}E@a;qj`Qh_czW=z920>1c%1zP#0eB@Ul1x(45g3T(-5p(Kc2x$(< z7{Iu4du(U8S9;oWyLWOA6VvTp;Xw5{C*-Gk_O2EM%x`SDR0UqmsFzZ-?^Tir@TOZ~+%fBi4Uu@1-;Glx;)8RCg zvcjJjXr1~?>AmTqfMD~gJt#GGm8pvbLZRv&*x&(!+dKEvKXgoz6M@g4TPG`_E06e7 zCmh{;3;Vzed{xZ1?g-bE-ii5aO{qDBD(f!N-$CD9(PF&0TEAV1C*D5j>N~5A8i!Jz z_oy)J&Fc*5HZX?Z`$YIkuVPk<0nAnDrtbLW^mfM9 z2P^{SSazkmpMy4lqa@5Cc+cu@$^?m?*G+-CmA86ZRktKzKMQowuH3%QHdBcn zAgg_CG|dTXu3jNHLs2ESO3$My9T$_ zlleuK)m998iqFz&A|fyHb7LP=x9*b_)2A*>qZuBMG1K&<8Gj17hyt+xbPP^FdcLy0 zvbW{aw8dF0*2pWy-LfD+J{3#}amQ9vW&*n7kHluR{P)okpMWl5BdEF2W4{~6y%~m7 zp~2bGWx>PYr<~m*d}R*`6KQEOa-vA|+W07G<;}FxEQ$S=$oK`Z0VO($*(jwE^^=7{ z@+2|$&@Z?0CG*!(vEBvQ8nKJ{g(ovE+|8kg!(i;h3r$aahaI?VOutg?uA&s@(J&AqkPz-1xnUkw@fCkG zTS>P63s30GTzdW6EJjb~(9~IIVt}=8H_V~R_9I8saPO0Gjy3|d2~d=7G=CV+}Q}ZDhc85wvv)-lc354jD-teMh+saqXshhA54k&;?XG2Q#YU z#*AVlgFs#FyUfJE4T836w0#7;5O-3}X@QgiPXZ4Ln`*2Nocci7Wim%{OPUN}`y$xn z@eZDZa*M@gyn2M0RH}$esk@b`dT=y<6`Ro5%+ z@SJKH;~C&(@&(%RP&kCe1n_4?SPzNek>C(EjA>Mrye??)wESWnV7oBi{{3AwDJ(G; zi<)Uga^JaF>U`i$a=}<38(lT17f}u-hy(3-{L8=%guI4& z5(7P^Rgb`afC;AzQp2~p5H_IOF7_2Q<0Y$`GBo$9`+P|;g~XHd%d8xF-&Rbvc@pfa zLylMQ3|!NMLxe2p^OGjs@=X(ll9K0s@pFM;k}TR}7oEFnoa+>;3dPu4;`;KqeLMWl z09?PLsOk{KnhJ}-fm9#lJT(va|Ey=i3#>~Ck}Sgk5#SsDp6L4-iC`EN^XFv#1JG0D zU)SyJL_3d@xH}VJ^R~CjWc%y)3w~}!VaC{_Hv6oUIBkLJ8sGg1M(AM%Q-aE?qf%fq zOYicf9C#|x=A@4`a*EjU3f^nvoKtT;%IoT3+Kuf2RKXxMU?C9nMbw`MWAwSTB4;cu zrM{hv5(btnc*e)h`FxpnT9aa#O0E;nXxWLMwa7d`X{~fzwiTVW*xJ_^6Ijq!CxS8E zf2_Mg=N(cShL{?HVW{di7Zdp@g@wys>71vAWKzya^#~?J;yVhsAqFwm(FlEf^}+Q} z5HK|)c7F_GpF|XE`NX+j^XouWRmxTsZyDEKgKtXww4yA_(X%>E8vH7P!aUxKo?Fq<}qOd?U472tbBlp+)>Dq z4d(HcFr;loS*;IAFW>#9l06j0dd znW7^vH%YinL8tR)>j3TQhwwSZQM9Nqo?Y4IDC#szVH5)*Ko~~W|43Y;nJah&m%c0m=^>OR8B9TiUo7dWGL>cZ~)+9__(mO1dc(gth501 zlH7>yAQS$#)hcSXK3Pno^WLjEp*d~GSK4u-#DNp9G|gU(^N5>mBUi_X$i48X;1##G z$zgrEW;(Sri50HLbl;yWc6opvjiZ+b`x3TCgauqRo3cZKSg}24`3zb7W@Y8+6WGP^ z=hG1NMIi}9nNWBxPfGGiGkwO6x*|ysonIXzw7dF2^v=;6q);?P+177 zJFYh!%m?bOAXpm;1MA>zP+q4802mQFx7Os@F&nSFq=CrscBU;%&5HaVSMVj#jS3(Q zX*hnF%^OB$HKX@W-<-`Rlq>VtlRO*Lt8>QW?e7lDeM8)xjc5kh!3j zEYJ0zGi7^m&JIjs%X=qD$fn(fsKWrs2S+azD)HC$DkPI+@*)B`{|x+7xfUdRh<5(K zR*w5HE(DI7&9EPKBraUa`UQ`GKGhy0SG#SRE%cVntQYXvh^H7zaaOhjgU4>4EsuoQ z9dbBvnnOJ^2#UG_ymUwp$xG^GV8X1<4)fCQ^hmrLMR5%-cc1SOnN0(|nvaG7PGK_{ z-<${OWFQ@81bRdC%74o{09XdmBB#o~Q1hei9 znsyi$l5@!JQ0agke@Q+J5zEqWAi?(5ua7Dtkn_3Ru^1ols6QW&+d!_qs6AxI`TKe={a9cM!6HrOxwLIkm4j61as)hqz4Q+KC1gnpBW2L=XMUk7TQ~0ar z{IY&a$X=$ZR4tyxgq>99MO2-Q=a!^YMAE+v`1idD|kWi78&lH_vdw+5%8V1RtvpZg;DCv)sWKyA9 z(2K!EFW_)fFc_k*(Qin{<%mM_lcY8|7SJC9* zw@1oy>lU(!a-fNwLVW862}>y{$Yu2BHVY94$h{BL&U?fTO5qW|tGaAA<`Vksx(Cs| zh@)YV^CH+J{b9u61;0vgz14D3m#hAb@5Ip_zRw5)7M+0bjZ|Ym$u%*oWCfxZr;xE> z!_;Nx${_PPvVi=3%#V60TXA5CkJp-cf6H&Z=^ZE#+b8bVB+>L>Z=fOT63CF7Sw2r@hR(EV z2dl<-9-ZgdHm+#~&~Q`D6ke$2wS&2>{nI$W-v5Z@Xn5R9a0@RK_ZFYz-)8N@-Y+N< z5FX}nG)fH%TLVz`!SSKQef0ezbG^kF@ZaL`*#ja!Ak2K=jt8y+2PO_EI<5LJOETUH zW-Kw!-7bN{;nvX-#$k=2CnDM&62C!~7EOV!^60q$vxOl3oFzSYC|Kt814RS00PIpt zCS6EF&+vRBy2xt;WHr|*27S5AMp;85Y0OA_d@B$I5i;TsT{t6@nCD_C?+sMw&u_f< z%X2((jtzw(Xr~c&u}_&QvT-;(lryQ*ptZpB12kcY8q!bNY+7r{I-eB#-QX?F(}=cW ziyLM_3Njn)=t^g@XN0rtg{-5-%blWflo_bK6c(2mTxfH}TZXQD>e$;a07hbGD<}qv zG31WNGJr(SBHtq|n(6d3!X1l%1jr)$%9BIg`m8LEXKrN0r`|-GlRpU;?t;|9i!rF& z#ox3K0$S5|%ic2$hLfhYt!R06fTE$)4!-6P!#Tdf%1F2F1$6f0acX1C0#uTiE4ljF z9Fi*3;Mhihe&4QOHckJl6aE{Ux@&c1Wf-2}1$778`$Yma*J7XlT|_)Hb5y%vXY5W zJT$+Sr6d4X3x*_5d%0{1#HXaZx;8WypaGB<2S>++X^4AGIc78DlaD7bxELZ$o&?uH`0wlEz^FwiG{) zYHexVBsZlUv1wQ0B$`q*a=-6i7yDxW+u^SSZI&_b#~ivD^n{xQ`q-eU)he{YKo>@k ziZ}X+k-_^i@TKVW1)z$36R-j`F>TWa@X+Fh_AQ4!Ef9hFORjYyiAnu(%f&4ZUMZOc z*%Jw5y`}>u;KeLog<~)AxJ!o>6 zM-ySqZNJfL-|Y=pcZ*!7wsqsW9|tw$vikdE@V^Jh)!;SEm^w?<1+@s4SR?R77D7V~ zpOR)VlFj-CiLap(52X)VAZVjb!WoZ3g#^h8;RvKlx2*6Oy_Y01Y&Nyv@6M?pWyHX5n|{R=F8ZhNFWglTmLckUG`(^ zFV*ie41?&l`NcfGbA7RWskG+1Gz8eN1pvjC88WA2RDM&eJ-p2%2jTL_#Tvdx=YZ2v znvt2wW)p)84}k0#5C^3A7J7>Kp%kbb0VhS#i@H^vyCR7Su64#}AV-K1ZGcw{WNYY} zq-r@V`Vn>n&M$-UES*fRikPo+$N%*8n7bN)aju;d67IEmLPJko2B)=3xAqEloHU=~c1=qa2-Y*r;TI>9aTII>?{Q+^FczKMLLSNeuxf<<(%HF%!A8*h6s6!NFW zX>Ag!gZf4PWkfELd4Gj2Foni8$ng7zM#$GvlW-1A-aYFrSW89tS2*jfYsg{q_^v}{ zaX)LsxS{F62n)s?nuBj9XnXXe=J~0|l}xC|B%`FnN3|{Y|CIn;kIzB1#=kY_%AWzk zXROW96shKj4WWSa@IK6g29)a;ueSHt#%PJ8+P>WoU@ELlgv+JrKp~D$2Gx;wc zCW`E6p8F%?D~3l42hl4Yyvy!J8(Sy81%CCdmJ}Zdtz1fZ7&O(_qGo;}#A!>u)Qb+M zswqI989rB=OlZZ$RN;LUOUw6(^AodtbLCA};p%4JfF{I2gwg#0Od%Zk++-cLzI|65 zzhgPvJ;V;KygNNLqoHNWnPvoZf#$t=R4>_gBI)km4#?g#$$inrt35RvsE6Y!1&aE1$Qs)%qZ^ScXGQo6^Q>(sV05zrnF`$wMaYkic-e5VRDZvnA}D?@*B zS9O)6+-zv6zfVvex6cAXj+_5H#pCS@x=mXb>a9n?Zml;^_LuPavSDE^^!|M@Hc1SM zG+OR`2D*Yr_x;Gx36ub13(-L)qB9@Z_v80lztGf@lbuf{Bm9 zZHKeNL-QLWezkY>tR(N{fI>|}ie-y&C9GdsrsLWa25dY`XpbS(pLT2{pDZ|a!uFAi zu-ga?3QjyXLj9bnQN;{MbKD;j2ZRdSaAEVaiMn9dL|ZjSM@_V~GNBQMg3`tE z=~|ZTxc%cuDCR1v0*2xS_pQPbl&Ccc=xov$SQ5Zo->CzW5gn+n7dPN4?a?_GC8G{u z4X`hV42QN3JAYNSAk+bKRr9C!7n!+!vA$XSk_}|f@DZo2Tk9T&GlCU&wBZ8?eDk9! zR2uZ{Nf>@}GpdrvWi+V&5{o?N~#*m(=mjO>zvNlf`nbJBaTJ$Ye|?64o>@PsS0CE`_mk7M6}U-DoFWD z;8t9!vEefnn`cPu8B4p}z5ZJ)IUIaW0jm>)uh%Qqt7IIV)-wn~TPWx@k+_uv7`juO zBMSz@s44LL&Kor1kD?B6ZKT@3@t^r&f=#jT@e>djj3A4o)Kn_@L@P&Q2n+*tZ)l`6 zm42fb~k9VfTD0!YxrS$ zr&xw|+_s;&r?;AT#(lV=eHp?AwxzFoNIrV{pD|43*`cg4;ADNB6BqCWfqgUCKx)Uf z`1L|ofO9WJ0TU8bmZqUD$;7)bI{n3ggc3*S9ndLz0H}Vy0IWr@cUa@Tv9tSRbn?5m z15;OTq{f~;>b0G}-;(We7))V7)k5541to-R{hfmSIXQ$ntsif9=f!VNLh_e=ZAxk= z?JGtFgawA)s2K+Hf}aJR!2g+N`l9uKZDiToIkgg$L|+6sIj9^tGeSO*5`#bk=Jei< z>Gv$(7C#q|{k|a9`d}>E7h4H>V09cBnsEl9PK6CDsbcrmE~!}=J^{^|Qb6saA?dL|%l6mK4f0^?xuuFCOR&W>OJ{ z$AJ|hu$B+!{2IVc9(ana8`2^j5ay!#Y9;#GQxu85aMP0;8i9GlhNu$(`cKI`M!d*P z2o1y<3R`WAgRaX!8L1A6@a&z8!R$)U?(-%`L7Hig{+=%F?vs%5;D>Hf-5(}mH+dO+ zX}Klv|L5{|F$pt1ZQZ|zw!X5&ZOM`uJ)0z2`gY*}fqzs721=brg7;h72JL8A5eNs% zX3xzg^y8RF;w#c3{^JF#?XM_wYvXRzt!=S`$0?Nf=ne`1TPS$7#8}#FJmF`@Fd{V) zz>vC#Sl!zCc*YcY$Pvjn%dj-({J|Cn(xL&*Qs+tOK-8V-N%z#^=a-eMiZe z%!V+)`t*norZn%$sjJIm!DLPTT}TKubbN^|6mmwIx|pDRqAa6&Ma z`SdV$T@5c_AtdL!$}MP8dm(079w@t zV`pF3N(7!O_<;((4P023@Ni8VDdvad?ID)Nlc$=)9#Otax_Fxln0`y=pfcoor77fB zcU3p~%}yX^2E8$1CJsY6wlr0=gJi+PlVt|Gx{b;>QJpbhJ*xM=ktP}O-TI&bgy}BP zqEKi|!|vc}ejEO+K-0i`F~Y%Ja}+#J%{?zJDj~i~LK>K_0m@bx@w_6B07eP2S&|;)nQwcZXM2N zI|E$|43n9YC4<;`fy+N63Gh&XBp=X`hdD!?R2f6P?10z$%rS*bEyu7P2*XpDm-Rz# z<53vYc&UEPV@c5spluL74lo{pFo_=V`+(b)6Yc$;Pf6f9ZeFNJ@IX)@%)ei{#0(S( zLC6N(|74L+`$>^fPAtoBatEW1tH%%@3`r0VHdE_gE1T2BUTbca zs|827m>Q`GI02`kJTOzZZWHs&MfXN=-21r+VT^Txy+U*;=X`|6)GOktAiPNs@%V(g z29YchjoMQui8B->WgU>Qf+ZUBuF{k1fghg$^_~EHGLS9~8S!n`FzV)~>l2sw-4pbB zOR%jE!72B4lx~0y-?Cu)R`MP)DqN!s!g^TPK8HU2sD_Iau`2V>BE=7qBeVsA^H48S z%UzvV`#>Oc3cf}?nrJ>VG%D_*r_RLVQ5dT&>T}uwW;)_>o09RIlAGc5$a#iSjg~!m;#4G*sefoN z>TI89LvoH7Uxit4AqBT#1jwG?@D%^-FbZSdQKrNqkKjlGb(i3g)?65-OG(sD^{An~ zFn7`;qzY#C zMJxxk?@B$hK?P@nohbQ0;k0=;oUU4cV>;FhwI^6|yjLxVZZbr!s$fslgGn&-SsFJw zd!PiUG9k(x@ZKP2s%ky+MAX(o$E(W;hVPVv|FX4dA;)}5Hbw@XbI9xHic@RZTT~|J zjt2?g)v4(UY*dC6Z}~gpyaJ0m=)(G16DZag*?|xtI;h#(V|xPp-93{s2{npa$u@u| zl+~|K2qISWSK28<)73y1X>e6JQ-=Sc+(@NYJPy|oD|W1vqXKUs?+l{Q1#6w=GXyeG zHzGdH2BXiazA@DO0|$3AE);@|O|7EQ@Kt>6E*u6-5%m*p65CQNStpPndt+OT0lT^I zXb1{3rkC01*zwSMHU{Wp1vDCChb6hgq5GL$GaK*eh^STj7>W>P*UQyJqx>Kj{V^^Y z0XBg3lFAD+`8fjBvVe0PU``;|di3rBQ6WUc4k3I6d)jP?o4M>rPxeuKu!tweT>+csRymNBzNCU9{;0sZG0=tt z$lIK6B9--;lm+$4+CP&pi{T1vSc+ZHg9l|%ogwXg`&)Fu`4KaxO3wH9j>Ft9e|o5V zH5;YiQ!{Nm9r7$^&lktrB6+8<8KVjpUCT%sGGGHyM5&+KU~6DohzXIIJ}DA@Drg*I zc|AqGCD5;bl5O2I)pa25OMVbYdseuRFl>(Ng{^^cAv}*kP-c3|nZpK0Oh@mjF++3019!j5DD!T}VYKu^?F(U~dT897N{({B@Yb=JWF_T{0h- zab9~9o+#x++D$O#hb*FZM9$txqsbK|v#huxA&L>6N`vGR$igoq-Cj^fip)@W7UOPw;Gh_L|>H^Ta(?ZFL& zEQ$g^!8J`Vr2tSq>Vz#|iYx%ize<+Wkwgg+K*^QceV)%4K>bKx<@XV(2v9%Pz6wL~ z6PQJ|Gxf4K$8kdWy$UtV-3-&FEq}Q1)(OYp?nNGK?P}n68Qewr-NZxF3g;X%fsY=FnT=-w=P+{AQ^QjFDa>Bzwu^h(t9{@Yfv@H@8wTJCuVo1G;fp7fa(-!KMYLbRRuyc zn)MmHP!LbR-yQS|M^rbUV9C=Sf3E?u7;ILO04V*v-TQb8p^(s#1U$C$6-JrrHVRn* zpaWpeR!hDR5Ni3Ti+F^K2`pX4xo@Pp9Ye$2&q2$Y4qA~QRKILkAfv+Lu;DXLkmaH9 z*?=K2Dwrv5;D~8O1&|HqVCvqc(+?0`oa&bZ)WlUX#bB}>K!p3MZDwE-$w56?6H;+| zDz`-O1-sW@eQ)sY-L%M*iYssamp9uT^l0AVIx4<$_!~cfwgIJTCV;Xw_!3ZJDmeA2g z4tu;@@yb4|a&`$oCzU_dl75eSNbPM>iH&9z=|)}aUFOj=-KhB0EOh{i`}1>2Obg#j zvFWnadg4c6VTXZ+aq_(m@Ntk{Eqs3)bS9|RrXA&?$8-dVWYED>9wx%pA$w?kjpJKq zqnD1h$Ms)5WD-XtC%+g1e1kVXLt{xufdZ2!bHMM~a-kQ!PN4q05I{N`s7md-701EmB zpa5g%5da@Z%h`hK&jU&PPa{D^C3*#~#Y7p`>7->sFxY_ozxUpr;B_s>n!d_Lu>Y++ z;P?suxY8_1dbk_eW#*Q9K+(gBt&yst6`RcTr06RWm{%jhT*~FL$rB*CIhM=F|1FC( zn|j6n+~|tx%*hkyyB(_S7LP4cTp|56VNc;e%-cCc#;g|NHl9dH2$$*-%U{Sg+q#|J zg7zDr_i8>BqQaQjiO~TlVgk@s9XIZEqzN=uPQrtCYEzv+Kf))G= z^q@9qkM?Lcad_$LHG*eL92Hw=QymCpmmFS$R&_p-R1WNMOrgXv+xnf~g0#PY{_GYY z(OY_Gd&OZU{1U_-;_eSg%4A|jJX%Vn#B-532NFCCX-*@21U2N}AS7i}*!Sh(Ro-^f zf2C6XfN43n_XBHFG{=m#W*D(bzj2aYDXSNld0Ae)_tfD)dsv4g{b2ai66bI$oyPMm zI4DTO;i+e4EId$%h9FNq+YUdja;iI{t(x}?2%i;z@wjO5w(pk8-Mg6}*~qq0M$}%~ zPc6|3iI{uICQ%xUA{ADD3LC7N96Ls5Ex_itpC8u&kL zujpaNVi_b&ZJA4lQ~we!o0=^FA@v)1i zsQ~4-i-#|cw?dts5VE0I_VLo7MFvWGTWs^wI*?>gOJ*Qrko-vsagnwXveaEnVEr~hxsBsPhs>UWtdXRg}U3DQ^QlRhoQe#TLSra;O-3B(z*C_)!dkakKU;Z#M!ty zqxxJAsGZ1g@srAvD5Rq4&&7{hJR4QAvI`$>IHu$k0OS+zRu}2d2e#l%e;z!gc+UMk z6!!ul^-q1~KJ1L2U9jM|2q?f0#HD;rbwG|%=N&Mo44Hc-KN%KQAYZSNgh)IcD`oke zW{l6hngeYN%7p=`+H7G8B-VHk`W`hF1Md@}Qf`9=1H~%IVayPBE+2O58QCG1L$i`3 zswQ;y+C`tj5kS>kXT@{u(!*b0B6Q7~;wu4Ma}_vapLyU}2;77al~XNsR=kL^2SiCc zXu}*_T1K?tP~t9t&k>KZn2opCdxRydrGQz@IyN^x2g|6}TA+=z`Mvl!qlMM+qY48^ zEiquQsWx#E{>p6#a6vmzmwB`TAe=?kZvCTZplp>{hQDrMnK*<00NNRvYsEXJ9DFWp z?y>_CFu7A@%Qm!!Hcnw7214TiJxUa3hCmkuIa$8}A3RDhzr0Jr(M!mQEs1GwFzkde zYRqA&2(c9)?_Sj?lhB+W@9;+jhZx_Jd!WmIH(zkMIeDbrV7d{?%mm-F;I4>6A2M{TEfGo z?-QD3rUc9^vp==lL9N` z%~Lw1j4)}ndYYuw$fL8cc&@lE|w6;fSPd>O&zKUuirn z)>rl5<(-Ew7)%<9sjm;oh^`-BeYmUVYml!Gow!TL*M~*!3c%M{2OT8U_;y!P4R6)& zpni?tV5b0`-_iN*e24&f%mLTyyzNeKeKlZkNaf@ecSw5ad>jD4=R6#`@o~rnLT=bK z005u!aiE1xqG7&OWSzbSeVFP0`0-l+FbFRM^L!BO`W~$FJ}`V?!0Q)D`fSt+!u!De z;&JJ`yL7%jj=Ml`ef{rYo{xvwY~wriHr@wL{fATE(Pj^@2OfM1+lZ{wZy?~C=fODd z!a1*leSm=b#e@6h!u{$hEB;^}hS~h@7(WmU1OocC^R>4gzuO0b`}HHp`~$%{xL=zI z)BF!CW6OkTD9wA6rk*B8D)pwPlu}AlxU;F|DTdj|rWEd+qlSj&RWumJiJ*Fu3WZ^x zQD4IJKit^|&cW7OsK>#cw@`nBSKdPX2Dy^Y{SU*8!ZW@!<552(!dxo-kXTzzjQxi= z_a^N9V}BGby$$Xc4)`~)2N?F*2Cji&tPU_tNTz{d@rGPaU$-#7cm?^(=4ZO+eg=lE zFlA8h-4BT>44d!SV3;HVsV^}^Qmb^UkGN1GLCjF#^64eUTY3FewcZEo$f9tlrtAsZ z$ig;uV~J&yMJEsFh(4CY)VKX&9+XhRSo9L(!ED4LS1ba4u}H|1F~eff3DJd;Ky>Lu z)b}nF4h~C3PSp@XXnGjt$Y^F1XFW4Int4a2*$in`x2vra#Kt~T<#&PTqj zsLzQ`*2aqp4;G6u>Pvq&jeA%$^HzMX6QSmkcDGIIYz^Aow9DO1%fSL})K*L95n3&+ znyvJT5o)$yEQ(4*IP>}m0rFz*BOzi$ImVw@6oDcu#^R3_9t;A(pB!re0>zzZBf7kH zC`5Ftldw@dE2}JT1&d-FCEaQ!-RjJMEI)LsGt87Jp6Q6R?Of+nBSjfGD9KcW7Lq{V z1CY<9+RISVFk_@Hq&5ppHJkeeJo6VV`{hLAKKQd1nFyGDm}qN|vmpvwRr1kYOB- z4D+Jwo!X;g1ll#k`xDR$=pEDdJD_(ikq$xx-o(Z-fUbc*TtNiTJKJ~mk^6Bvh!7m@ z@?tA~EM+QU?7+Yi9-KVnh=Qwp$uQ1qK}ede^%+8>kFM9vbZuBdguseuv4cWDLOFhw zPvuW3WsDhC<3yVbQkA7cXal}g&A^inQ>pUoBe|;9Vos~{gW=>WSC+QiKA629%<~V% zWeDq$p_DSi{B4%f4zW4mUL*lT6ed6>@S+A#AnhQ6EV_j8gvt{cZa@;^iIg#JI6_1b zCqSYF7C!)O;Q@;$WSBvMQH2FW%&t`7Ll!79oRW+oAqX3^uwn%lDr~TWlPOe40L2nv z#CTtv3?4J!AchS@fHb8E7$&lmfkYK8lC0p!X$uwuAYp?Gu^v{iB!-%FLm474k_8CJ z2p5G3z>vTRqZ7~oix3kuOokYv1{84dg%c0T4XD`ig^0@wJs3?PazjxJU%a3TCyYlt{t_FuoMQ z;tLm08MA~KFGf5r$mqeLj30;?Isua{24M2VmLnjbGJ-LTa7|grz(^63f|VS&0K*6?T(HE6hxwyJ1zMP}K^HY3 zBEm@$Lhyhk$xsYeOiPZ$;tmORT6u#}#0fXhI7Z$k^fw zlu|0ES8ILBN}o`qnW+&BQsgcg-I8h6v~a@jM6q1}de2ez0w3{b%UJj}!|y1v`g^E9EFXkEigzrL-F-+F1fG#?WRSaE)JnJF(UiM#Mm04J2XTfHz376q_8s4FAdT=G2 zW0|QDt>W*V#9{?jYo@c)3UZf-h+x(MzDkn^2KAjz;MnD!n7?o&e6Ir@Q+U7&{=LWWm-oV6JeQdd3}oje|7{$qJWyJFwwKx*uC-;m1 z3MWQCuMNfJupp%ZqQ=TbWJwa_G2o@P>DA1f(B1;rB6lsV;F&1M%@CX#zuGqSCZ!@5+-zMPf$tngc4L_P#~$lS@%rS^ip=x^r~0+ct0fGQg4u(od`4EeK1w--Uhb-UpBu% z*!kcXJD(;$ev@5ku#{3t%WXL@&2RVKZQD35b~Vp+J~Uldf&4uOV|PCIc0M(KJ0F_A z-`{Qom{ZSnG~abT_)Rs(*Ukrjv+#s8mjj& zae9_q>$%Q{^)pbr0{^KsWkO-eDIqA z5I6Cj^B!7n-Fu06#Axx%6;GMgIu>l+ZWG$u;L3W32agP43QbJ^FfLb9uHS8)Pc`Ar z#C4qZxAfBVK&*h9#CTq&&tjQNSL?VcunPrerF&!-MfZ5=ib)DjcG9-ZVw!8M^=AjU zUEodRI-lAF2AQ>O?iD$DuDD5T^-^I43&d@rdt{XE@hPQyES=IlwK~x~^qW2KyGAfb z?3>h;9ou$k%vbw+?}ZXVi(&3?6Vq?l^w|{Zm@#8=6Vr3?P2Z`3^o`OxRQleFB}e)e z)8DZ6S^NVFFLFmZ+v1io>mgH0Q?e_PGM%xZ6nOCOtc$8DTeVP=QcA0B-I8yycT0Yi z>cBDY&=KzL?x}9MJFS&EggS7{Ij@vbdJS@LfT7`1wYRIf?BAHqVcD%Y-RGQstDUwc z@gS5%-GgKPWbq)#>nmH5GR&@Qd<})PPFm^5?CXBj)>@sLl70Y2m$Y&!JLj~aeOh;G z^(p7uoYZ~>sIB%XwOn_1cemHF<0!7-k8AiR!i#<}o`~Zt`4rnqe=Bhn+o!l%`W^j> zWA;Vgb|=W6AKFPd=Tu1>S?wkc#^~=-%70{w(%pT^F;j|hcXxNe-Mt2PcYg@sl28UJ(ltSo$;4ve z-0*yC6vqeM<3Z|--0vzBR63sLrw!#&$R!N)G*>rJIW+C+*{z7m9=wUap}GgYfBaOt zwEL4v#~e;|%OA$<_4`S6U_foL)P*P{q737@SN~Qjm0lo4hM8U4L3&q5yztp8tGAn{Gp+^=gy7-WhK3(^l1b5xfb}N3fys+%l zQK?k2E>2_>WQMW2B(W|xmzKJ_B(m=AQesQvl+a_)ZE!k$O6czH?)P4| zlUh5eM(e!PwF$8ReB7Z!2z$^d4k616V=PTNBUkF3Lru&D;(Wz9(YAnb-07|E0^%PjsNjiba-CWVyge zwzx-j8uQi|*C*{PAXiM35?3L5*dT$ZpPTO2~xIv-hr zF_v7W1%?^f(>fFU<=a0xVg-42@UuoF#GRiZ(d9s0=RZO}4isHfC>D#sEh)%tw95TH zr8b1c*8ezWf1W8%z@=Lk?3S(GtbUlgoIrH{5V*VB`9q?WQihQ_ zYgAv^5+)PTzViw3=XKwMrKFrN&(qGDsnkByc5jJtw$dqGzU8`;Qp(@%ydGg!qdy|` zYuFPWgfiM6ifY6*+16UO>zds#jdESqHZ|9`fO{Zoct^Ii{+9AeC$82Q>I?1S!c-Y% zCnDHIchRm2RE2U3qf)0b$i-dWg!_D`TSDac8l*_M{>(g>57i%3(7%9a62p0MF`gF( zgthilXu??}c+)+QU=Ux@H}QSQl#3i+)}I=Sfd{KUsaos98bh<<&+Z;bVh};M;(Mk> zS*T*@7Hn7j>8TYvk{H_6?KgBQex<1|9*iR8_$xTW)h69b+=L@)68l&8c@o?twhF>| z`H}`gw5lbq)Wv$;YBOj!NVYQ~j3o^N$#zMR3>cJBn_L6%hr~_eWOp|=Mn=dwnschW zw&~w)$Jts^l}sj+$yCz16L?rmc($4FQh;MlCET0>9J9iWqE@3RaelV0vD!6xr|skR z!6Mhq2RO3nVKdqEwYO$7SY+g#EXs1H)V9UUOS?GkWtY>GiG7v&I!&I8Ffp%>lEMkL7yf9;k6b}PaehanVixo&d&0LKh0vJ?(&AKwA(+P)Uzf9(Ypbt`_n7=T3?&b}JISy9)C zz-3q@^JrKsEXdg)+X2*KjV(*KyE_%`OSrqcQ;Fj4?j1Mblv27wDCd-Ns!&i(c|}ec zro6(+b5421-QC@R+;5E~-gb0E4z1Xu#$n&Y?-91_RdO5|F#`x8gfP0}ElZp!wyu#@ zbeTH2WnGeJPHtHhW%!tJw+JK8`tQg471_eUT(?i*r>u;n7G7I<*uc5EKZ!L_Nj zuLsm2v!0mZ+2ocXEkTk3mEq70?HcN^Ot@B5~a+^KpoDF#h!I8j`94jl+3!?>)}x~#OT6z_0dtWuYiVjbF1 zj><|?P^mmoe3QLWr!J+-N>fl(9$`wB=2uTrx^(FYB|>53`2;vXj26*lCGb?AwDbm9nuC%NLAEb$alT_knRn0FQ1WJfZEd`>blY+fWY zHia}OZ9~MI;cUerORj~8s5pa`qX>xhHsR`hbid&y++WS}00)f*BBN0mRn_p1Y%82Wv$pf8 z)K(lsY$_LavUGPUwP~y0AT*~vvZSTArJr4?yZ3G;4YFW!z441Ym9us)J1M1<8xuzehqC+Xh1GRUpH6Sn-ZLsgzPmDW%k^eQsK<-jADa zIJOO(UuSt~lkV&6ynEZ#;7|Rd_(zuRUIUdLLY8xgEz00pI_X)g- zDy)#iUH3kLLGHcxKEaoY2sGOsx#B)LVoKgjoMti20h%bNL=9R5Ic0`Q000C4gBAb) z7z_x91R|jzi{n6%+yfK<3}}KvU{ErXNCSa590oxYMPV3+K@0;CM3gc{8Ks!f8GEl) zrj#e_azp`}skpnxfaK-P__ zZ5Kfqu5=^$!M1-8+U_Jsi(wDPv;9>zKPPa$X#3*o{GBx0k&nIS+DMuFV1LNZGXJJkc!R~!3&L#uzu%*Pl_`7|;D zpckdf08B0Px@-HX>RhC4SMYf-Vpw-fa#eh2g&r5^2WRaRg~%7C>JiL6`P|s}iLiSAX&7Qv_wBJA1jB_x?UlUX2d#J!%OXh)Sy$Pr1JzQT6YQQD$m2HS(8XvBnG zWgt{4+GU1;GVyoqErnc{zI4?|J56<@AwVxiGe; zL}l&Xl(3Vh@zYLZ1W_MW$vg4ogiy=jwfuZNgl@AB__Nv}wgXG%~asPD@hEuDYa^5qLCHQEPIB9@j>pvKa}^La%g zes*rRgA-6ev(Bf|cB*U)a85E3jTj2Z;97dT*_skoE*u|Cc&gnJQWc`F4=$rq8^7}X zC&S6p$@$edibG#=MqwmGxpIRnOEUyGryrg{$(;7F5uISM7!~6)!oRAYA*1^>N zWs94p?gCLRo`Ycbrbdog=$#XraVf9pyq@ZWF&)va#FhtcNaKo`0t5b4)U%_b0(7>J z9%E3Yn?Vx|$IRRQf;|Pv3m$zdPSjw|*=hag2|W1(`VQ{qPM0VeG6(e5Sj?CMGfsls zl;bu5+Io#l0U%%q!8qV{!1Uaba$@Qp3Gytke&tvo=6Dwt-JM4OEuJ4B(7O7!P9($t zKjX&t^%yUuE}V+sMnlT)mmA1)k=cMz*t0dS4>Fd3LbO&i@>JwBs|Np{$liU%^~fmR ztsP{FcORKtHt8eKW#DxcY-I_6%t1HP-Z^yWx3Lkcc7soQ{Y>5W@q#`T7-aF|Cp#ci z+sl}p!7|m7iFgOCwvaWquWxUW3`?{`d^#Y3|Ecer4GR;_U82d5MB&1t6vT;N5kY&H zH#PHmgaB1Q2~AS~r#uq-M{JI7Y-ptU+={&yfReZer`9Y%MwpxWOOb3|3cDbf= zhw8&FsHrCuqj;t0Y$LpL)-FPHqLlZcLK-OpP}D$=l*3ll7A98A>4|`Zn>JJ}N?fVN z)PB|2f9V;>=rz&HU-K~AVuE%)?IR@6g>5bY%L;@j=NiM3ooE1Sn~TW8>N@j~Sai?_ zDYa+R;^+)v8_cTec1Cp!;3${46RigMc()c$J9jX2zsPt^2N#`As>va=oqTQK-zC9JST=r$J}jz%C@%W zLZ_IrN&N`lFiAkvdVri%lyBYr2|yuYPf=~k>&?)wl2e-#?3XCi5npUtn5a`E-%Vcg zyL|&wIF10cTPOwa&Du(oevGh5xiLiSWZHC9c47j({V zq5;?#LYDi%`SUIiW2++gQ3~QU=W4ADNjPFt4#-ZL*{f=1te+0BS^4ONx565(L)A}i z&1^WBbOsk2OoEB$df!tu2U;J~9!Sov2NU~q>}(wkE^&t15YO)qlUd>31!H*@8E zGG`=J3(SAJFD#;#b|Kga(4H3-wS3 z?{cf^ke~5D;ZmQXHa6pr#8+{N`irP8RnzGL%UK5fp|W_a}=f6$Jg~o z!Q2>P!jJC4k@?m(s2SadXb4_}eM-+Xw;!{cc=JQyv8!u`BbB1WVX-@lblD%jWz;f8 z6R50FV(jN&RA(+90azx6?2`I&b?vx-b{J;i*#us?joN86A<-A4IvzOcHfA`f>Gw5>hEPY zR28TglDcwAIgKfaa~ya{P8em;R3Kb6ICFZjcV{3Y{9EhN9qK^dOk=D!GDtux1rm?Eg>%IxEg}n?w!2=jX-k0 zMr_TO-Qj9GVofKIPHn8(YBg zbf~(o=^X>)sO0Rg>v?VpAa;HnVkH=m3Enw0Rf~aSD(H+{#;)Fx+U0>qn;SxJB_R`(YOYk;sRZDCd9IwK3iNWAN^rTf9x%@@ZJS^g> z4ca32n&G&WGSVL33H1ROrPddIoya`k};i_4;q2tozcdaFp}h)Hi`z)_kZ z5qp4yOgBCPCo>Y9;#! zZ1GG9J|s+P&{E@RV0hPGJ=w-(eVImnY6uhju6NMA-~O{j^jhW_+M1(&eo+N27s^(D z8Ockq6``rWtyaQNzF!Hr`+d%F_q{kL{T*T!)0<5dha@R-iWN=Szp~o^U`_cL;rx$L zJ08%QY!_=G7Jy=a8faHhrqYeUP&MLI5tb7Z;> zpJ;0F!hE%o9&aF>NrELYpPAj!R`vV9h6eFtgCke!NQM;fl4ac>oS(bv+ZNBf ziragx0NIx$v3XL)nZqUj~yrx%q`R z&Z`KyO`Gu06XdiE`f2B7n@jgOJ}%rth^@u}#2#X_0UGGrEl}kIb%r*^1mAVpFG}0p zS%_RWaBZxq&-0)sQ9k)cC_uhG#{)(*Mm623exgG#lb zw=!n%fMaJpWX+{LUGVr*_`4rmQ`;49sU4V_#i&~2A+)Mi3?WG&4je)!8uu$F@--;c z4Q?fRsG&ZyXwEDSchvTb2Om;JM}wp-Gw!D&q?i0v(gha`j(zJZ4za;cKzTQ^ zuZ)*fpBD2#oF!>}v-%EcMMyCRt$lrL0<5zqb)0XgswIiHFCMbvRGV}&w#^5UcIzQ3 z?~CEVPgV(xTgGW;Sl@hT#K7VTMGOSLxU_~`(FP&>$_I=%LLKH>5;N(IP-%}}wE_gt zybIkJ?1VzjE**>IcRM(m)Q78mFu#vwsZ3LO5%^ubkgDM?0)9;d2b9AmsgeQps(^56jeiappp-RjBHY(Wy8xJe7 z8(n-(sN!EjO&j2^(w65=>dgs>ZL{!;1btFRu|F|d9#9dE!B#YMdRCfnxMo}xW!e$L zcdBr=69j01TUE0BWpPFunV}3wu$RVIQYX6ei4&tkC*j(UY%Hpjx z-(JQeI`SevD*;}D81s8Z9Pg=$(_01*I z3F1Q$#5UD@RwwQ%-(e|p%bHUrf_rM&-XHYpm|P}7V#n@9gJMuOn;K%$w5!=(QfE9- zohDZo1f|>N4iG82=B9=xySb3xirrR0xoHxgZ0hL%XAdBP92%XNx1`TB(MIWC5i17t z0PL1}dq|t13W${KBF8E@fn$HvCCxJ&kOC;FR}Et^yB0EZ1aOlk;*83jB1?Vww~=Px zlqOc1>z-SL;_`pH@AAtu4L2QOsM%ZD^<`*9A)HhyQhV#wSCa0XaaJZPzH;E_A5(rm zLpi^!B@iavI$)xyoOsf>eF#X7;O|~2E&7^%k?qst9&3~Ebhi)}KqCc<_SrBWkUazt zpdKg!ZLc<;x2>)YL6gd?OXOxpVWVoAIVXnwZ}WCkgb@}RQ+?y}Ojae|ALN}?T5atInm7OV-#$2^DoqAk?;Uynsv@H4Lrrz`c_Pl52pmBJGlAl@#Ty;@0 z05}K;1JYLO041^MqAw*5ZB1*ctBQb4;H^5cZs*4U$7jB*3Lk&S^=M7NnARK8+FvbA z>(7RJVVwLAd>OO^z4f$mzPE6@YJs-45Qp-Yl2WKJmkuFR`>K`5KS{;32?Vct)}=1U zq{l-#b>TO`RH(lWEj#=cpvg`%LjV%$*DCMazhbJDPWLDCee1OoOWMjEmd zsR_f)-SUpMCqO#bz4j!=nj+wUf@*;|a-x;tsLbuQ|4<+5)ckJ)>1NL6ek)eiq=gFz zw;{O;g2i0U(Oe0-m*-;S;mtxwOeoRa2BC9P2M@EZYC#@#-G;W~DZyYC!i}@H_JQkt zI>lUnYLX{kvasxXBYWX$i&$s>Y|>6X9yM0jMpIF7#6Es9L>W@EjN~aIk)cR{b7z(Y zFGB;bT?7c42DZn+AVWgdwhF|FjJtA$&UY1}6F_EoaZn)-f8xmN-2IYYpys#q+LY|$ zdPNY?GznA{7eU)Bq%&nsGUOR2uA)O@d+LYAZRN*4!(V7;I+;P-EfA_uT9t&8&EjhYb#NhoO9^a|o_y|qB#c@&UW4G#_v&#tdY$_4|M*%P{zkh& zz)+6RM1k*U1%{&;2nPiAZhn_m9zbnRZ?p2UbwHvgAWQuNW!@y1jn7xxo=hSkpu|ar zwD5e`yJwGR4FaZ^lV*Ar1Lm(9D?Uc*+#inW;FBlHDIteb!y49T!Y$Z4_Hf#PJ(`<( zKp|){F*}gMyTqI3*nkXpMUyzLT3)WCS5k#Z#5TH$eZIzH>QK6+W0km0S#VEdrdqSn zo50NK*H-pm28!4RJ~I4~UIOgcI)HN*qo+IABuPXC43#Tm41cNvLu^Y*SQ)lmk;}FJy$p7Ari}{(LlqU4wYA-vlvj(f%juq)Pk>Ujj!5DSNTEPm;MRv0 zfD;3|V}BhxxA#LO|FvY!QVZ0&VI5cFppp_3M8=OsQh_3?&0Nqz&a2i&X2~R!`|Sys zQ^AdUC&{vmuPWs|qLfYCyUzduc@6UNB*4EQ@_ha%c`4aNqSMZa&#gw4_-S>uMqPz>++^;&so=Iw*nh9IkK-w}yvUB()4Vl^sl!+N27J3q?_a$Dl}PP?y7* zn(Q!Pj)5hq%>uS+qHo~fWKBJTOQ=g9hi(-QpxV;XqMFi@0zTCF+d=!iqdC!71Pq=$ zMEm;?`iY&4`<3HRj=r8YF~L(lz9BDE9_KrngDB{?KSe6)j5RgsRf`-p-HErUo@Z)5 zB1}2cNL^@5)gFkE?KeEDTT8-E0&+VeV4_eC(Rfp;k;3!>5Qgm>w^z1&L|{E9C;M|Yu~ zLqtT25?0_O^l%#@=I>yhbUJK=D?AUClI>=+{)cM*mGViQFo*6c!o(s?o-Luhz{79} zOg-=n5Gz zvt6eDoi_qHq-F>LrY#;(vIoV*+TL+NnsihN%rT|lMqmxp3z)>I8=yoV za1Qy5nSDIOtEx&*Cb?FRgsAX%31n9BQ*GQvQ@4_BrX{~uETQck-$O6pUQ?50pxh8r zMJ09A7ag`cp9k5b{hq9Fu5uC5L3%=)u}@5G__vlbFq%evvBMA6zk(SZ{8BSQ_In9U zL<&h{k(PwH zSz~~I&6X%H9?UVZR|ipX*ZjHmkjf$6Zzr#~1zYFxYG)7)j{BI@GY_A~2}k z4?^W9ho1JaF^16Yuz%vhAdgwGs;j$LgEGP^kR*!8->tNEx5SZ-shPptqvcKDKzhWXZNP5K(d*77lw0r~XwoS=hN^q$-)))KMlo5`!6K zEGv3!RO%sWWoZS+%>er!*A4VzpW@|JaKnc;E7l>)32|fl!T}m7UB%uh-UyP&SICjC z84MLAoi?6Gu1KkWxEv=w#i@e|uC=&5nm^n}ZED9Z7%H^y09nlC3(;Z#wD>ANxA8Hw z*!smtzH*q?{Y7j)iDHKaA1&7kg-KdZf0JshoqXsGj``Hfj_-16yP%zYCfX%Kxiry= zIwGIUpUsp4cmmAO)c}^7LJ)_UV%aE>{#N?Y3`M5+zA5^t9vbdfJU%<(S>R?JmZpz- zMk(Wo|9#5TJVJIs=C``|>}&m5AT%hB0_Ak^!sGK|ty=uwG?&h7-qcJ%nMSxicSL{@ zTI>uwUlJ2|2$d zdLUV;o{IW@YAvK|lB-+b=O=T62voY4rI%L`VoR6pmTY~sG7Jl@og6c-diX>EaPArc zzf>U!{$xR_Aoe+RJ_CcJonDPlI3S?XYXpCNZnm`;n7(^6*9zdJyuXbC6$Dz_G37?WKJ<{1Uon~63D^-!qDs% z1pknrsQ$9X!9B-&=!D9Q-bXJGApc*BSPZ-;%K;8i{e)k0WFbCei?@KB%*G)Zbm#8bQk1?n>?uP2%ix7o@%m{qtl~DK?5Gfz!E4h%v zIaGz_><`RuJ&WD4^l>yZy2qzUH3ujNQi)qW3X4P9#J&o?p+t$B1-q2rlyVMA_pK-b ze+i{R^>MFsbsTI_qulps{1UibO`D~M;@1i`Kuf}=Bu0Y><@HT%&nYakS*h)RLTxJ- zffSHgp)NY3YTOhn>bg}`0PQnNY2H;%$vZ#UlUsZU7vlflcb}t(~t@=zUqmeQ0W4#LRZEF-V(0&6Js57W!TAwO&RHO?6K)*0b z5kpms3~`{{h>xmc-UQ#Y$CZ|A03#w^pBvdj)`--LV-I>k%Vo}`RkAuDm4KHd%B(4M zz7Jffz0tF;41vugL}@~|{9NThZs~yW`ml<6*%WvUs?WW;*Yr8fdb*B{Gg)y^4RM_i zFUfULk9mk*xXaLRku#7%*iC>;r?xT2gN3-&l*@*L9`@Jx*EGduOV94_Nhu{|l*}6W z!h{z?sE725?reG=V=0P+CeObjiv!DW(k%?KAat^y1qds6G7dm4___H8YkF!NicV;g zUL5#KAaU z#Ce>h_@$r7)o4F~TQ=cdxzcDm!Ik-J36`&C0gJe5Y+ZOY&)K?vEmP}v>|6|R)icwi zX$!6HuQ4~PC+22=0IY*86^FHeELwXRQ6}y%k^|0{zg#=YB*E7hH89maT1*{ouC8oy ztFdTsDPtA&vMgz&lODUs&;z=d4D5>rpt%Cgxd04Ed_hrUSu|iN1A%;rTysswOgJMB z0JKrVD2BrFIcTtLuNIvhM8wz%4zkNSn{=T&uX-#5qA}vcL0)i|Axm0|9SmDr%^;i( z=Dh%2S7`DdMCge>axi1ZjlRLARk$Srv73eN4ySP zGa8>IQ%h+PjrjJSfPn0wBmm&h{XO2Pc?fj#tG=}g!J2vcol9Y$MnyH?^$4g(DlIV` zS_Ate_DQ*^(Eo#<`h02Mpl8!_ZGGxmIa@EXx`NClhsj0PT_5kAezLh4`fP4yM-%^uQoyJcR?) zSZ3YNUhy>SphT5ctQ?0ppNe7~AmKCUfdBh1%A3xmEO4xA0UQ>V=h? zYl)3=bu)+d0QQ7t&2zb!8y^kq3&J}rl=>Q%LF=i_%-Glh)FeRwyDN`IP5})3%Al-` z@R!TRui*G%w!9z%gyzbj@>9kI3@RAvc)AdicE#Jsz$-{W>-LXl*WpfBp#M|G(Y`4e zR1Thi9jU|)G%#J2{f6ODVX13$Wu%5>MUFqm(&;DylmMR*rVQgZq)gBU8EbnmqXYyx z(dvF4z!G}ZWQ_n8HKvgy2A^N857OLS1e>>4s9)MyN5&x-&}^o|^6;+7@ZX z$!<>_X5*M~b5~8*E|IL+Y>noXm@zWG@$f|ac)DI~XUUL3EBCcyo8-_aQ)EGHM5EXI z%Yk%Po^>h=^aFvEE@;OVP19ob*M77wA8u1?SkXgyqq1ARhLs^v9kXx5OntYwetK!p z;H#&CDiP4t_ox4qj@aeh>!pk~gNCNaBr-=Ryfbd@I7Cm&Ib0+tIbD&B-YopoqyWq% zXwAq(zy?UMKgOJbB=RI|?i|1k9<W;1`KqXtIti40Q8+B}xlswmC>}uN8hR4A9^qSX+e2Jf+KDkkTb+&3YUvf6f_RKy&Ti`D|5*D7LR=(^C0tiHKb_U~o53 zNtZa;GMG!}GO<}4`lT#O$L<4hEQH+M`0rCV!BEU{KTn~LOBH6n<^^JrmW(2VZMNQY zy89yfIBS6JOszhBfi&7)otWYy_!Li|WZFwD4uyPpscj^+ru<$GfiKI)s?$e@<4@(? zT>ajax{;BIIxuT{QTtl~L7TvMzeRiGp;Z6(oePJBq4=5?;j%*$QycAs*hlL@xbS5T zz~W+7MU6rqDW_TRIsK9US!J0VE>UDoP_z}m;y1?wU1>~AT;a$H=%6eleF-Khh0@9D zL5Zy>tQKR4$SNBK73M=qq~ZG99)E{)OTRWz$Z{hZT5A7yXtEO5 zIJofAovSRn&F@HuE3T9l?Ri@=RiL0+NwgG_;(G}DQUf>HGgU9jQ;y@V*fX@KY2z=; zZ!TQgIbHf6HS9*uT!2qO>1E_td@hmO-vC>;7R1<@pdITaJW2vAu+NRupvk`(okwCd zt40NT(ZXfqNM+RYz#i7oY3e4j*hk-TqzQGmGPVwZd3@@Jn|gIX1AyU)tew7Z6JJXz zJ54SYmfvv%`FHrih=Q;hY{jKK}eIKhv?qN>unTvX)MJPwwYoG z_KQGaybJ&tic644Zw3f3V7~%RW7#s_Q(1}UpMPLVHP z!DFAgj{`s&gQYjf>{jt1@B*WN_gwoa>UT6CH4ZzvqagltF~X(uSPJ_7AQmJ^%r-iX zH05QY<|3i)11Mesdfov1I$BxW-_!cn^h(er37Ln1 zFjinv4p0jQcl<>lBiL%$zQp(q?o0|OueOX2H`YSf4bAm?;fReJv-z9(tWJxCt`-o* z6xSl!L6=*GeP)W1+Wd_XcR~fiWIDzvf9HuCg(9`r-%GifgMwk9(vYEhGo(P>+~IrZ ziyK8hI)}G|3+DafI>6^47#5MxfY{KglF`NZBn3`KRt=t$OhXqxP|(adCOw~^BrX?X z@rg`3as;^%k{QQoHA_Q-W^0ruRklJNEj;n0C z^eELzu#t-vtA`wT!E3Syn^u8Qu4wg=yy3{JW%#)N>UKbo^th3T6BQ=WMMn08{MoK~ z1SQ9_K;bQMCeo#W(Uv-W=9&^nMY-=?E|Qdh^nWwsC13*DRy8NN#kEiGq&Pg3H^poZ zJHkAyPiTjm#_qu#dan=K_0QK<1T_eR^$`|kWi4!YdB_iKCD2jn7*IP8r5Plznt!Hb zr;Yb9%Rw5)=^PKPwG2#G8UQjeAZ(ko9Z^H8O=mH%0w8H`C6ydB0MHMnrpv2_lnu@S ztZh8Q6N#N%KNkZ>yUMIEPKq#6wQjOpNq-W#@zBe9)xa0omjBtl$cm$V!I+FiDOdxi zS97&IzisEVj*x-oMMLf6*wm&gc_jh$yaUB!7-jy8rHMz52?Qt^38+L91edJqd6xX~rjf4*Y1L(xj37bGlPLR+3=e4^A{YFGgx8iAsW zf4Valj;YAkQu_G$Hiq2Gk{JBSExyk*`9Z;h=}uH_9r<|kp`L!y8`;3av(f8=WKyeBDC%&qMfYpa2h zTR7vL-`6K<9twk>oK;vZ`*lXc^@VI(d;#JcRIUQ=AZ&udEmNwVJxyBg$p1t0>z|(% zQy>#K3K_RF)u-o-O(3;$pA%4FI?w`Xeiej;K6p3=%fnuweQUBnd8{x?6-ZKagQk8g zV8|-k?pvvm+SoPx{Lt(Xq?->g>6F3@2pB!bt>K<)1-(nOeNtWt*qr(642lBzgh3B+ zwKXG+qH6LsPRP+v-Z)vDYLF zR_Q_9R@o&?^{yDNw5|yQ?Bnh0}+<4`D$7S|Wf#ZsgM#A0Q>=pX9} zkLh3(mOHU-9foFQc3U^4HYi8`B*oZGfY>td7;yPv!OwY5>`r*mmF1N||r(-ux+c%vm)6Tr!}OI6?T0;K7B zzaaUI65KwBL(lr1gsVTScY#8jpxe2Lz5~R6ViWJs1j#}u6DM2nLVS?#F);f2Y1rSr zf_OPJV6)z%&*qJv{QsGN{r^`HVsZrd)V#b8M#-x}bf6R0)oO6qkIU|j4GL2_*08w)ZyqSf0GL6q2 zRmSFn6|u5M)Z%CkwJh7_qF5F)TN}wzf;rmE$g$AsthifZ$efkes?`9lmv78$sV1f{DjpCW%}wlCT~ z)o3#8!-VYzbdiiT)PSx*NVjvm45J%EXXo)?7bOiCb%?SeR=9{KT#=DT3d5>Ccrnod ze^@_vE7uV>uWuXdF=sLfCD*r94OD`dQmW5*&y^b}pp&{acY2aetlgk$V6kAQs7u~Z zJ+?YV=d4*AkWNDu3G2@kiRorEM3qzsnWFima%(Y>%kKvilv~zrY0TcS$oAzB=roLP z=!9v{*vBjxfxF*|{!rLCAu|FkYXe{%5Gf~Z2$zpR05nL4ClV;j*)B3l?jj~@o#+}Z z?E)|6W!spEA$HD>@_;vw{tVbcr0)a)5xk)_R=icT%S!;jn$s-@L)4Syycvj5IA*7W zT}SqZKWoPl9k7)gOt^bZ%VLQ>Kj!P~iMK_WbUDy&`kO$JC3|O*WYPumt4z@z8 z`yJzp1K#{ry-$ZlK*)5@&5fKXC^7qIMI~FO1s!~JCea+o)?PO30(zZ-LvZTw8L9EQ zp{Nh@!CncoJ)%kX#;nSAL*$gjVAxFqz>3wo*+NhVK`zL}Vu7bt+CfVktlh~SZ%XR% zVosUpp6j$F0LxGVy+d1TJH((g+(qby-niID8*7%>yW1lI zD$=a3YVRaYuW76OKdizO$A=HrKrDnszl}kOtp6-Grz$gf7a)sW5jYp{l(rQx2OHfl zAks2$h>x|%Qvb<0<|rLxU00SnsrMzxtEsz$VqrvMRr@~&1GgN9*E)uAdM^&~0wzr? z2-Z7^J*VPX24dq0ap+GGI7@EceGZ`Eyk|fyviHX&4DF^tDm`GSVhc#4LnHXbUg}OK zLNk-x&VvV;_U5N?$75HsPXCB3Zeg$W)0RQ;#G~lR%V0@Crk`c*{`yJB<9amqld&H5 z0i+*ej3l+o@laLZSO2jWwWSsfb_wVX3<{`*p(!2(3rYni=0W~ua8hWhG_)kn0@!cT z4-#qbcJNtX)n2*h3tgN`99l*q|GtZa&HL!C$@Hw*&poZ=QbErE^+X%qU#va7|8?IU zxNT2G1mq&7_ZKB~J|ntRtaW?tVuB_ycK;@P}Eb7yEOMXIA04#iCkhnq@D#UlWKSeopyCxZHJn!lHCl@%&*PbYci^$^bQV>HML*HYzqjF~c$#469jlk{cO9<|oMlwsK#x5w(9W>jir z;gOAMQLWlf&M{MB?JHL@@UQ4Ez-FBY0I(6b!i)|INlK%NUM(HMuO%OgX?@-&rM9Tm z#8d^qwE6Yvxjrl(@3aXhkIk6lOb&O!7TE=@H3d~j=s|+Y9FKK?RR?l0(SA-?Ts!PV zy>-e8hqd=uP;Ff(`xF5{J%3EwsahxEPc>^(C|`cwya5wPdVbq>NNPa@bS$`u>;N`* zacIhzX)myQn4y`2A*Ia=587i{x!Q!Ssz5NeNQx|?*Q#x3Gg6_hDkM0|s6nCZQ1EgB zO=*g_jubKx^Xu7+)wtgJ*VS7|L3f5t=^YQ?`pg)I@4w$CB8Z+GAd-%_glB2Mm?nA& z`oLHf(?JtJw{7HRW0E z`NFS~5C@lM0BB~9N%h4CLHSi8x4nQNkY!`M9N}PkBSLTQ`PT=2N^RYyJH6oMnDqsR z&^RI9Dz%Jlp%mb~^Q%8{6hQE4ojYVDlr?k&SuJ3+<8>CY0E1ckS%_iw`W&r562t@5 z_2ANIWmMv3X@wqe3BDoqJ+WSm56 z^IUbWX)Y@$V7Hwdzo3@~T)j(D=`@QFcI9pnqE7HsFaqAL#rw9)xU7Rh!B_!QOtbrj z#Nv`S!MIbi%Jbf*flT;}0w>R=yX|s#5x70IOmLrdws*^fu!0e!#3?yQfr53FL8d99 z`wRK=q0bouAmPis#ed!Dd9{B{7*H)0gK@b6v3>GlXR{Dcg}Z#pPSTHZi87wh3vgF+ z+>aPHAEc&1g1Q0z8e_2{{FChE8r(QS-ESGUVh6mX+kk>dP3hc?>V6iAtw8vmi#{MN za>IiSl)PTAzWX#M9vk65;^>8r$hSg#MFi6+xpW z;AdzhLmWeOEUHHba!6M|vK_2gIIbPEJj=zd$ieGT1(MM=dhq?T{zdd4$m@Z01AVIx zdaMM22I-{F$g*#8V(tj5Bc{*O#eFNpR{Vft`9*&^AT3Ha5SUJS`l7?f4qY-W9=?4n z5MO~{*Q+u2K=0^Ui}dgS1(K=W-O{vZj{$__BTaRU6o60GLyG2~9DDcoz?Y<_`5-;r zgbhd-_?Z(c9U$8bBTwhz0E?u@Y5vKHyW>FXD4ya}@VS|AiMn+_@& z=ABNpE!sp%v`UqTt-J2Zc5qXyfalveotA?5khVJO@WcYM4!_hM;jwTPbqpU2(Dx)r z-+tZ{F)qP}14AX41)}!k{!z#vKDBOSZz-jeDajOq1$_mT1-Ifm?~N_Kv5|<(z=Cf& zeF=}yOtHVd8upm@F$Nq;oG$-Q{0>Sc=$%_?nG0Z ze)5=_;^dQiiqlT^+*6!v$TatqE}kS3U&kV)tg@w)Qp*1sD&#jv*w-!C%6s7!XSqit z`DK2L@)WD@=Xizh=a;INt~qN`!n*K{ZrCD|vf~i|&0_s{%d!0be>ubPhT}JL!e`e6 zMEv-yX=vD^vkV=|3%$^c(k;g@9=_4#f}vsG^F4ISzXzEE(JjZWi-ZFs_iYHHOr?;& z9N&hh*^tN7#XWvV-osEAcR$|}0{dxgBWq84Sle6~IuscxKA-s)N!sk0J+tR~T=F>V zUGOmYo}WR_?AVhu%-etb$5Lyn3)9mCAJD|l5`RgzBG&|pRN`~oHycx?dGsGI%0@Jx z(Sy8{O%)bQuFQ6x@Qt$OJ4gkAZG&}w#fAx)s+hl5zl*d2HezTn*kAG`2l*eDn{!H1&cN+hR&ewS+l+SXx zluE_pg&x+)unX^pweO3b_%3c66^Lx4&BblAa`B@y%#*nPz+@JZi+@}0Wf$2O;5IMx zg0F2x|M76})tZI|4V&rKPt$ytM_G&F*d0?B)fb65(TZQSkWES{<%2F!34e2{;$d@; zv=b%I7xX0Qk@8V<&N@w1Vx*^-_qQ6fnaP&OulQZ^=hkOWi;1vIt@g7rWDh7@+2>zpqs_&g=n^l!`j3+Cxhe3st6;l#n5-t_|;_AWskmEUL&8 z9KhFz@=3v>h&(|XS(WNgSx}L^{9tFXgFL|+`OCrud4giD5#@92&H4SQBD9nxo;<-B z?iEht+2!_^7Djj>g?fMKbUvL?e2-->Rl)I4P)aGKlu}A5rIb=iDW#NBN-3q3Qc5YM zlu}A5rIb=iDW&u2|39Vkg;Gjw4_;F6$s7Lv|6fWerIbJM{1g0togKaQd#F}aE?Fm4 z%{k{(<=DZ)`vC+22?!#ysdLUb=bSm3=5{tZa>#I0IrRVMK%;o6^7G%ti_|+9Eab4N znL^c=9xRQ+`IgH@76i!!a59i%lcQ-?yiS$!ae$O+1pzDx_=3d9(JzYxiNryH3N#qm z@r_Lm9XohPSq_;ICLAhVXrN_~(ME2n7h$$UUQq;${P`g3D^5}QSJm| z$$i1X?1~q_h?4fBTRDCNiIF&D(uU0da*iH9g0vIau@EOL&@Hz>!(@{>Z<@rmzt0x^j>=bT~_pTr^% zK6&lWIp@USoO8}O>6~*;Ip>^n)|VHT*vp@D2h^N%&YtaKY|fj+d`bgAgpOZi$dK7z zvcEsU1JEsitE!^PBW~&7ModMLXh^#hH=xjS@c|(yl?${CSMu0PGC4FXePb+Ea~lfOV+TD;myTU@ ze~DN4Z+CZhcX#)1>FzFZt9pna?3VxkVfbMNdHTgZpjIr2B#-1R-@K`Mt@4MANTpt~ zw>4}J>S(;qI zyin3;|JB%#_CtdfL#(Avx)fHeuzWFEaN}$1Dg_j9%fL5a8zwGo%nxj7apD*t3N23D z0s^4LiCw@MElw;y7&2-IT^B~~uEU;cM<;PiX`{mur+|Wt7nv4N-WX)?N$h0%!IDuJ z^(hrtfG%>#UDx4GwWF)DR=Tnh#%P&|Rp1Oc1-29?K#@DJTk^!f4Z{Rt3=AXGn15+# z;#E5)Z1#dY4e;3uzm}OeMh3d6lOF}?<$znUnkgs7(^G$H{i!X1LiA)Lf28e*p`sg- z$KN?Qdy+gj;?Ilqy_);2R#+5099=W?OE%Hs*HGSQ-`KbtBPdi1XN9PByK1eq)>yR`G`I1tBV8Zb0xn?6x>toIGyUKn=93H(Qa8A(=}jGUSGPYpu=K;=iGh54gw{ z>?CrIzxs=8K^jzU{4~{iVUD*R`_cz%K(}5<+nBfk7iie*K*Lhqde}?nG|jin1T)2L zH1s4}u#0jIRqe39Qk8TV#>MX9u;aM*d7t0mdmQ&WywCeE)r0`aV2zI&nYETCTIt_K zF=CDT@RzNyiyx!JFut+orbrgw*hNE1RfKiIYf%2QZG$Yhb|FrMUc$w5A^^EIaxZQ z)|S|y4CTjG`Zto!6A>iIbGyAx70T3iQLE3xPZK3gh9!@M4540uP+PHLF3z}Id_1Hk z)mp@e4y`AtNzUYsTB2sh_JyBK(!jwUwd z9M5#4%Ie-86H9z2t}2P|?N!Ancj}SZpkl!*;bmD&3&OYGgWIU|v>|3Z)oUASG2p2OpY| z>A=C~tpBHdjwkjR#^ek>Ur$+U+=f3#Fnu?*gKzvUOpc6f@$~G?bi-$}1SPg023pml z;!%d9jPWIRT{ucSTNYzF^gN(i_IWN7@d3n$Bh+HqSz^yIwBW^GE=c*tHOqqw5RiMX zX!0X;d#%;hT5Hu=5=EUQQO}Pa?`&p+Y30_mlX9r05&F$OjPm zM8g&<)=iI0;nd6Z9`{~i9n27`h{vc1(d=e8y0=Nkh*MfAJtp18WHqs-@ocTNG1j{= zR0JE@1Dgm)s32u87*5p=uT?aukEI3dZqd-KhiO>sTQL{}8Wm%HEl#YGi2wwMbXB#( zLxmpLxog2Km&LCZs1Js=Gn)koxm+vn-e_`i4P_QW0B=BRhYUl`CDPpMdTt1Q5bo3 z#@Qs7AH2{gLWcfDd4dGUM_u$#61-121xilvvy?J->Fw_B?$q7AwsgOugxyfbhV3mH zUZ7=iT0khV_-*qEs1?i0c}gD6!|k|8t3xs$LvC`srY0n%9FvpRfaE}GhurAcmF?8qcP`?7I z!c^FjQl-c9Jg>-Yk%|#6dFo!&$HRkpL>?E7ly7V#B1M#Naz8)4!XoO&>sCY; z8}(8C)e|hnO|MCGUDtJ8t+jU5-mcnP?cKrG87A8b;^1AIe=$EOy7*#6s+Gu14LdcG zrZj^^^?>Uo~$d0wrxdP7fbZv(){@|MK#!#8@KcZ8i!{_>b= zt+Gn_^6UKnuNKvX*@82P{9q@MkDPAE57y+X+96wJqGYOTf3>`RK^ob^o)j^TA*r8(q5h!m01IMW*l@zEL*r1gy zDiO8@wn-3Fh+LNgC4wMCeNgIh;KDGgyLwy`Wk>3wzR7T@+)|v7@bcsc4On2CLW3N3 zeoBCYrz%C*z=TK#U>s$~)7{_?&U zlX^+L4PBtyWqOkdTvGZ|73?$!o0@$J$WRz7@uR zro-j`Hm!15B2YhHCS=H+_gdDqlNBSfZq{1+nm|}0Ye`IGC3RMGkspsPDXegYYH&~; zwiSHA8F8cqIPhW#S^VC6lm6a&RVf^A-L7Jby9(m$sva*XoU+*}TDVEPh+))XGcK2` zQo8hdYbJEyBD;_uqfmZ>uX?gb3<_04@;qy;d)D)&H|>mTXwEsO81q7u-0y+1MJj_4 zKK069p|}HWygx#?Im7;BE=1+nybI zbe2jcR`U1-CeQGtXTZIBt+Go0iR4h+iQC-fM~>t~ME)B6C>#09iTucIIOf6_=ZY!7 z+St(btFiZsp5UYlZR+*)g`wbg|gEa1kt1>9Tol-#&29vcWZa^oBs zjx|Jj6m{5ed~vZx5eBaS02OfK7#h~E`;xeE0lG)OR)RDG`h|)>PoOK10S@#l1>E=p zBIuV4+*r;++*r~i4w9z?7HAq48X6ZI08px+#fd{uqM*fzK~UNN>a;j<3*eC!C#C_` z(5;DQfHy5p%mQen#fe!UPPZn80fn?U@e1IO7AFP(NTkJyRRD{$IPnTBK#LQn0OsiN zBS_0k>;ekFMhq|^7n8FGz1fWLCOx)d62hOO%gj9?wOw_x>PLVqhN3Jnmz@X*D z5(DspDT6UGxS+_y1#3JtL8aPZJB%Q52g;CBP#FLR6%UC+rucS|DW+aznnx0Zktu#v z5YIN0vsdwuGZl|!%8ldEw`@ZV=zL=SBFRKzO4pGoM!BRq5a$cl$X_Ud63G;!UchKU zZu`R}k2#srPzejOBo3gvQjU2j0RCB z>O`OF7yoiZix)u5W;36*)>dn+we_;Vq{fJrC+QMI^*K{VK)ls;T{q{P-AoLKi+?-v z!D7{kWJb1>^&)y+5HT_CvP$XFYfN{=G-gql_`y4c>RnU~UGNIBhMHB<>*=obQ*+|^wTgc+6N{PDTIbhAOBi*|Z^|j9OP{m5ZiDzdJUf^U0j(s? z?%G;ukS|kQLl_z=L7@Vg#i#^L8_H4VMg#x?009Y;761Sk3qt^qVXb_c{IzRYy(Fxl zbOY`YDF z2)ZM5(32=)<5{)yA*i?}=WDy>&9SIZuKV<5otaXW)$oAE8|jJD_Mg}@O(6?P7Tr6}SGrvs5kPzbg2({3eEx0`!vX|AFQxLi^;T|MDS(X3PPKg~_+Z^AEO&#)hYYQ%t*lO^OYj|^`|kWNF`>K+GqQm& zW3kP?Rcb|zSgm{4pC$A8BrpYEMyuV^I0~u987R=PY8oMwC!Ys$Mi*EnrV(oE6d#r2 zLI^au;JfG~31htuQF?y38y-1KKW1$RJ7uaml>2;_Gwo*~(H9^kn^yq!QDH8V78clN z1r+CPYK~9K&J1gLZ;<|mmcK~HMo9VI-MEDlMF(~Ryl@bjtwQ2OcU_6UuGfS*!-A$z zegYHEUBT%uq<`G!Qa=(EYC7O3$BZwxITY!rx3N@EHKM>{=D}wr75gUp_Ac9v%s)H`#AIsY8z^C=y1kdY}B$* zD!6dQnjeN0O*Q6g*;;cxzrqs1uJ}oY{ch0_%?_rh187+h`NQ~`M^OJyH9`)k*eFjU z=JQxUB`w|3@*%SY0LI3Kh`5!u@=k0<6)+h1RLH%51YIQer!7*vdm37%lN_we5)_J4 z(kU$*#`sg<8HbP`%a#OEc$B$BtMFiSB<(N&m_;Ab#uFLLoiIDE&#-UAmVPh&TL*kapT-O zz?3rH*>KPqu`gVvP{6p|5)5MyL2sq-o^hZe?X@I&CMCU*7AQ3&(DW4(+=HM2&h_5i z41nS*7wVBnE;Y@zT|dh_j43foLWWKcZTtOgVuyyEi@`z->aL6Dd3f-IEEZfW0X=5) z5a5X`Y&WHeT%CZ`WZJR8kq?96x_I{(-o&mzY-mZkr0W1td8WaT!N(~BB8pm43nm>5 zg+Jw-_8aY|ejeSVCS@`ZFb7U3fu#yD^YCCPVu$6|G>g8dD;ZB2m|ijsGNB|pF6~$~ zw#RI0iFl)tRM30vONuBpkGV>TWd}$BLk`allHY}7B*h_wFjJuGsSZ=s($9{_931eo z{$dZgI2+%^VSZN>joGC~Olg)Iy7y%FhD8djN|T8;De=fceyxu zFtz<_T%@BK&*b2E1R(!Ucq3S0gW^*xjlt417Ut3%I5k7Mz(d{YJAr{<$#As9Gq{B- z*c&t{G{mQKVK%_iJysIqK^GAP(mxOiom=TQ#=X*LsB2fP#(I5EYE2Kqa&d+xgZUNhvFJR?C%N)iop55{2{sj*5~Et+Db zs6IexL@OEpM323$+%XY~T9qAPs&UXDW?a7$SKuxq{J9qx0C~_-gD8<{ZeS%4;&*dd zhuD1juuLaTUjESG-VtL14^%4m-pI`qF#aWiMxJrBLiWG^Urzo?%3*(20Ab7qFF4Ma z4(`P+cH>^mgSdNffE1qUyBF6}7CJNB{)ucp&Am;%mRT4##_HhGg^|C*5qrJnPVA3*WDfM2ihHd2CSSMHC!*)kBd5;-d-i2aOj8pcOAYcWE2r z4hx!?sZeutQ^q-;yG2XpFF7(p`WoNF3lkyyF6q7Z22-Ib!}srf&-x#L|jY136kJ&*W{;+KWpsYsrA;;U!m*L1uKHgd>SKCl+FB zIeCBNv*FULewDG>%8WE)wSk1=Kva^WO&Uc?^C2b(SO&Bpb@?!)jF5_ov^UcSvM4#8`H}Nh49r2?gNMA?s_Fbz@5=DT&H(5IF3RiNg!}te-_9RSEp16 z5b3B_0z{6WKZ#n#7MH1hzGZR?2$3jcvB6nS=x+Fsy^z+{1MojQ4i|a)k`~w$I{>nB zK-;GQ-)tR%Q2hs5sp(e%h?|1qsCsRTFcXl$fjbX>-Kx`c~Sw0Dsr zq&lE3IYhY&)=c668YN6%kW@ z|E7k%%;6bFvAj}lALj%bm;ZWn1EwoQV<_(ym*$;BLaw|7qf5kDFPpzJSU#8#EbLF_;R>O1r`L18?`S?Y0$_Va;Fv1D(Kt-cV#QlA?FDzHw23bX zm&h*-wcQ>>;oh!5|?v8S%Bzome4M>GFLhChc{RW7H z6Y$wGVLv z;=+gK&3S-p$%%SA#fo`oMJa|J9i?(C`*Zz|DjcUWD>RUnF~Sdbk~DxJ@-X95Iy$US zfeSzo>pvxJ_ZYz449DuB7=5gj1{nLx)pJvZc1sqV`pp82HHaDSj>D1|l zJf;A;5(9pZNJ{}ofgIt3u3tN(*H3PrK>v`hvw%eV8Du|rR$t575r#SqFoSZRqgmH) z2fg+!mtx3DKQ1u*krdD%2#_DevNiw%iqFL|UnF<5G1`0mx~p6M$Q#ap<+1^}am1KZ zm$`LJS(rf_ASU!=BZLiNnd=rtzneHY%yR;q{B^)6)g;hCdY0+ZyfG63KajkPw4^=E z;eI!H9RcQ1Ozxrpbcz;+0W`s7O!4Vp>&1NpQpRNT4fLE>v>tp#_4_LEm;o!$0wxn8 z@~$E<%&uEd7_N@ueWtg?c>s6&y)X}u2B57#@YmP;H|5Qp2evyqH+?<3R4}aPm6sF) z*yUJfd}3O$Y~RAjq?`+3nrH5H4&rqJ3i1!GP0CE78vZbKV;N3Dl-BH|Fgo~&KT<#8 zFzraA5scvp=<)U40_#R@+MARb97H2u35s|hrpq_(6ntpKc05ixyknA5c~J|D)riS% zL>?GDmoMiD9(Qm{Hbn4+ErUDO0=bC1jQdXaeEAhk1~hf6C?#Kh&9DC&^!+TRBv=W( z?=s!fzu}2Hq#(VzLfM%r7p#()%tsG{Enr3VxM&6{yOsLEP=#g2q_G*FZV%@1)B};- zC~)}aY62Y>vYXT)ka8bL}mp3s!7(-!!lrJ`K%%O zyBfktg&AUrXw0Z2a*O@?cp7I-kt)zSrD{I~Tcf1Xzxh9I`XS3fTk6yOP$0T}3fBX& zorX2eh1}iuvVTGk2^1Ckt`yWo)a6Orjtm6a_#_zSjxg1Ji#431n8@SFScV1DVRI8S zp=*;9(q_Y{-V8Mb6pg1YaynO)y8}vF(R%q-85WyLfn^?QCDw@D@Qi7VgBdA?^|QBH z+6aPOVJ4}eSWD#P`ilh!tpcEyi1=A!9AW&3vq)Oq-k#AYKFdX#AQ> zCJ);%oNLM(nX)3$cuF?GJ$m5(+EP=`W|$sUq*|&|79yZ*^|dW;;!TR6T=0a7 zT852Zaev-;phTzC_=56dFWrM>d z2Ay5JUXJ7&m{roSqjlS+@d}dc2m8Y*>7dlk-qrYKkpjVo9MdQvQB`NQx;VI6JHS>; z4@fX|W=^M`rWS@V=B1>eK#3{T)VilBTs?-=sZG6a(5`^F%FZMv(|2Yw>m_RVhg&XB zE1od)O0MXQUc8c%f(CU0p!D38-dnXrPjAnx8veStvWzXo?aVl5c32HaY&Y{q7FzP<$A6l46M*`Q6d z_l}=|;dVkNQUBh`E7;nVHzfq^j(Cq4fo(QHP*i|jCX)69~LnNN;S5{P(?(>mTIg5ICSAon0 z7kuGWII9c#3KR~mxPXl-z!*+*W{FXuI&E%4XJuF{1uhfScn@0T0ln4&g8+AzY&Yy- zW0c3#2mGR6Uj1!mNGU8D*(V@P(OI79EE&(2qdGL|NnjYv1cI{@!SORPe5LJ9a{XT7 z09QaA1{6nh?E*@1>e75AMkEE*X#=1XGK8mV?CG^=?K^@;k#85^A2(>gk+GW&ql0Dn zG2U#zkuj}7Kv@@jnDa!?BL(SUWN@>QO@7B2Sp^mX7e(id8<6{)(Dj*%8DWjjWY$+q zYc#O$3E%p)WZUot&Gms?<5()DKYDkFZ~?hsGS`Bp{A8M8Y+9)e{PPp%>r4JyRNn2` z$kswn4?4D7RU;yw^#+ZkEF7D;UNLC-tcrX*eICP34**IiwN6uNJ^?)Q2O>68Of$)9 zYE}M)AUHnec6!bc0O|?#DS}w6oK*kI06itw1CZZNM~{5L?pl6P^G#Dh$Y~0BkemE|Z$&{lM&-*lmhgjnw^)CN>$nwhqEY*kP#gySzeLD~oL`h+N+tloO=y`oR zmJ~#U3?e)+2!F8l?ZXHE!uE=k`0ZgO5fMN2IUYC5cWA{iu|5?iesN5{#zFjAe+nUo zhtPsXclXYQaaT9v)U9>0{v*)I#dqiyXCWVvpI$h9uLkCOl4nS#B_T7g*{LX?ry(?R z@QEX!h}1wj$s4slu-~L3^p2$$d*hFRr4M;){8)|uL2F1a)hK&TYq0uc9@sm&V-iKt zy%n=DzydJJ$zF9;afCom7^~+hNdCVI5f+?O8v^tcDZPp=O@X`S*@P|ulnBzx+t8H) z$mHkzn?Y)A{Oc)w_c4%l~aWOD^#P=G`N^izpCPe>8x#pRceF{s-0-(X1z=bl= z{m=7_9Ckh>=(_bVMD{6nLL85*RnMOAa7DvgvjsVh0~L)wzkg(;Zv=LP8>JTE2G97$ zjGyd*P8cXlH8M(;i75(ib*fu__zyxWQXLAQT8<2SB_~L{53D96rO|HeoPZ3s zcON0^VQfNX;PP1g`q=%{4FDIse>j`yv(G}1S*4YXF9ytir2|0HD;9MifH`c5V#Xu8 zW+PNAxl$ZB$sbGA8~?BXRlA4~Q#!`YSsx*dYn?^9V2GQ z?*m-KV7>1(Z}f@zfW>K!YEI!ci0^vw>9nome`zgIu9++I>taJZw_*HFjUfjf?dV~& zS(pZRjA-2L*KC;cH`CA4wOf1t~om9yjrv#E5K0yb6x|DA(J_3#mC@~YfP9`}Jf z66La!10}$#J;)zQv1ZwO{qu#+6|^1VBrPV+ZW}vain>0m(CG=7DnIwV&cRXx=%fUz zpuEZ3@?h9etB`>b3YCkq0j+~lRS&2)okQ`4=RJ!B^l}YrfB@dO_bm;OP#p_;?n!DF zl~ps#yn@}3tCb=72=phf0re;AQH-R?;7@_|op-dOP}weda9C5D%7{guO0}Hw0)np7 zadb!DCW31o{VIgkojw}wcTYQ@G=B!`kJopZR1l`=mc9W=47!9Ny_gA|M;SJhtkfkz z-=WCrc{60XVb&6O<+vM!P+8i@7gOi&LPizC+mYiN6HT!h&2lJ55XlH{dlGaXp+{$% zpTP`5fvCc1(rKsyeP5f_4Ig(c#IP0@{zuE{>Sucmaqq=Fu2x+JXqV2DU2Jc+)4yzG##1$z^Kl6;34x$CwU#Kn6e;t;&Z|BRll% z1=0g>VVAb{-W%$>Uusi96U~#l+?G@4r7Z`Q)k1wbZ$0wVoz_H9n8#UY+l2qnv2u{s zY$Q7g%{D`dgj4ntj-8^k5+Jt_QwnQ-xABsQMOG0{H9sKUk(tslhxDgv5{Rs%VyhZbY!np@B)3)#bS`u@`3C=X1;Z+V zSFHW4xENC8qY)-Z9aL-hF;+&R#)p^9zVK{F01Xa^kAPeE2TW65swOmx!X67lTEX{T z<-qmI3-NJo z81-GP)C{rzy(oe-aNGbJ?>@F4lo{ke%y&xpSn9Z6MKC_awr~(hK{GG%kXbUyRi3uZ z($!^oK*%w(_oKoeMYpSBpk{r8Ep^uP2T5MONpVZuZl3jwWq_EW_nNAW?L~(QSO_w< z@Iui1Nuy|&(!tIQt0&9QskS8KYYxC5al(jN4r+sE-F+As@&6@TN9dI$F1XVQ>GlGsiwmkL@Hwl?5MnnW`aw+ z;+OgpC&6~)S2A|tQ zQVD6T2Q(7S$&e{&A9o(o7^-<3ed_Tu+Ax5 z@$&WtPnwVsyCI7r*XSS5iw!fQh*dq8Ahc+98oT^S91wL7UGsi-*xOD>aD#BYD`tHV zbs0(HR-Q@M9bSTW-MN&GQ)!1Dt+_831+Q4QTdnKf3JCcKEso}}9D0hO*MWV*f#1ri z0k_BCJQC0yPyWx+q_##u-M$9|xnEcifW(gTJuIL|&iOFeN42CS&)Lso$224Moc4D^ z2hTOEH?5CrKyJGJU{Oo?4sHYQTBx$R7?AR0>l=+YGL_kr_SZKG2ig#{;_M}G6BG1p zfydg);)fnw{hHNQ=tF2L8aSh^fSOmJ`i(|N`XXEbzX2%8-~wQ3kUTMM504bkHpM*= z+_zHOG#blWe&;n%8s>w?3<#sRY*L*Wd8Rr@m9+lw=k>hX0bPkZdfX!4G93g5Ug>)b zG3E@RL}EnM(bh?6X%7lKDn#aKpI5ns(N_|3IhZCextltZ{pX0Sj+Hw`0Fh_37?oD( z_Fm5W-(Y8r*l-U4W{(SrTU%U&%EY+z?r=)nhEup7Zt;#qnB!W^u#W@phKja8j8MzS z79>N|_sqia;r=cm!jmsuA)E;e-)OW9>!|%lm?XslrHYwg3#BPs7ooC+E8pIg|#puhCs2bekl@qd*Q5;mnnVZ47`HC8Uw#sNwEQB77R|Us_Za29- z+Y)Wj^mXm^KGADP({jC7ODYv5qZ0b3@%GQWxh~#aO7p~#_u=qtUoQ7q6%l^#4>EJ) z%Ve+=JVbN;D2gZ<;8tO<;Qvq1iXejTN^gA(rok?;fVPaE)m{P1L(EeS{*n_p$ru$A3~8qD$b@1%r?lDw4*x!}73@7j$hlB; zX(<0nKqE7jPX&f){Ja5GF!aaHD0#SJ#E4;n4R$r_cFL=92w=*rZut%#s7VB~=O`n3 z`FjQH)J-Fv7+sc9jGf+viA-4DMTYG{Z$Y5Zd54A6v=@!lX1ES$ZZXA~J|$gt6*r`_ zxHlY8^uGw2=zozo?7W8G*dEb*2|M4v6}LFb<-~;6R*2NYSP94)Fq)IIuS2+b2@>f{ z-WyPErt^}$;--*U-Nv}v`#J7nxb0sCDm=(7wF)HmnT+AMJn=7)hKH5Ty8jzhvC)Ag z+DiYscThgGeT_TR=9R?;>-ZwwcNy<^p=C_sN_dW;MLB;KNuBt;B({IK>XscrX)jnH zTZmHc=sb=<%1c5_TD(UdJb@J5Ecf_Feh!c6Xxe)cB1n$Zg{*L+A{s3cJKc?70VQ%s z-&?*j!cew?7(p5_8Ps`%E&xu-C-~=@* z?2q{Fhfb}|A0GtGpcUH=Ag{8nXKC!_7^vybU*XhOLDve_BAs5#QJ0{rr0xNB%QG0? z*$N0|?f+4MQC+LxT#yy9Q`3H5)e-%cX5;YRuRl#Xov9{Ay=uz=uKv^6@YS80i7;+V z_VB{A$xdiM$u-+sB8M{A)3yQokk`FashH#y7llFu6u3YD)4mzZ(BbyCcj2Dn7S4^H zILUHHIJA-_rMCbC5^9%kd1bdrr9%VoO&Pr*So@k1OdbHv^2izHER8Avk7f^l$>qNi?L;oe%l}$;s#cR{#?uSb5fr}p+*td>x@b#u70tmjm zS$QQ>GEQey9J{ca0<~rWNt3m4SH#rEbA3j7jfnp4%EsPGFnK255c89VU@nr&0_5fHJfbTun ze}`d|t=ssWn$BU+4ymd9oyOLQ2%#71D$Cq$5&uy-5{$CVh6ivYhS)Ss(cg$w`V}K$ z+urOBbweyxV?M{wT<5MEV~ReP-af`_05xPDFc=~h1*yyzn+a&em*JQxGQGON^b9A> zm7UotJuJdP`*u5{1D^Sc9u=e6r9hWEC&3~pHm&rchgK@~@%K`!|4w7`&qWRYBCiB9 z?|&j}E>Ymgmo13_k<!7?aKBNZ*fW)7=3+ zqVW-#t!__;DbN$C;*KhtxuZWeQPytFQW`PnbO%px5s-iCah}LMHdZ42b>LE)QD5T` zrPk$JK{n)^`zuJEM!#Sv_5MHt`rZ*Q>ojDglA&}YNrK`KW(W=SK&SwMIkP4uIddYJ zO6P+#+9k5+4y=*|l{CF|abP6a|Mi{LzDkeU*>`{#`c5M#wtAB0xX55dZh#6BB|7Hc7QfqGdnZLCFxzFMYTqkSC}MuCz9lYCiK;{+iSWhsv4f z>}5JI`}unS3&IN51e1%aOe^?8F+PvwDo0@{IP9RD(3tb~Ir6!6-W?BTNJQ`)I?Sl| zV0`2KiXl{s>Jh@5Aw#rru1L$LKa(D=z^aFf1TYatM09!emlb5uJw9%sM4J{_!PP*v z9wM#n5(j1N&R5D@L2jM_Hj7lYz!oP9@`<-bg%o)fQcPFXD1eOFQf^Emb!ZCp9>^Lp zKCwnVj4d}^7NI{6FC69c;(G&}?&i@H!}{-r@~P+CM{}v@1L-mP zSjEpcdtggPiyjI;3ME9mG zS=P{F_IFoi=Qs#XIL)o}vy;i^goA0Dgt)huDornev~+3VCZy?>l+fpEW9e^gI;e(3 zl2Thu9*wmDcUv<~u(vVvM##8eNO7xL{zWUlnj;G{-#bjJejVwiON(;)8P(>YWQv=u zWs3J&LXk)WDra{Hr(1kseQKGAvM519lvFhXAVc{Y0g{ajO_1fz=ULxR{OEgBU>EMP zuoT=@7Z~8rhy{X@miS`isY3DuZSEchy@Y>s%of?Kpa?q!FxIPJOT1GZ{%V>bwQ5$&9 zwr1(!Y`BV^VVJFk(fuzj4)^}&%TX7N0u&Iw8S`LlkiHm1paNv2zIv9QiguICXr(ED z`~Z3O>T333nM%g2;8h&FVZ6rbSb5rxtCiFSTHrKYzXd8W&XLcH*RIO|C8*311b;0 z3_tQ(#d}i$z)NaO3=>xDL|x{ha<%Pf)CH{#{XT4mnDN9Nc+jerg0(b0aDNHdE5~BK zZU`*5fwP8H5MtP*F1Kh6wP~kFglC-kQE85@hl@X1Vx{ zFlI>7y?$3rKvsaE1Hp_N55TB8dZ^_-uc$gH2y#LwXUDGDBP>C~+O@wReu6q;Aiqg7 zmJ`r(a{03wE=}MuQRe-0={>dOE>wVU!#HilvExW-mcoK#>jt1(YP5Vqeh_AW!ZZF> z3*+{GZXT8Lq{*|L4W!_22=)vonv{{VK>G)f65UM6< zAgF_}yVWZ3Fd;5uaT=Bm|n7`Kz!s5(G~7z-2Gbv}sC zbC9fi#>)%I-nA#lS#jnElpQ`u%D<9^tf11eEA3I3TYbV66zT~Jlt&?wssdjyM@@WL zv;@r&ebkh`Fv0ksJrt^26P$h*t_XuDZxayf!69KBV}0v!K#@qBt@?F8Oq0&f^e^dt zU;_Pdj>K9ZgIh`D<5Z1S>(2(3i5%=T4ON1dES87|+9t_6D3U_RU{rsu(j>F@6GUuP zTS5{*@ZGGh@?*%0l!V1!ZCwi5FuHJRmbOnGrxjG#lrBubvf`)`a5vzdLZ#g2`Ohpb*!42^shI!?* zh*ZgaKORZu3@*{QK7Iosnw&Bm!fc?fbhL^;zXhUANl!6NFxRE4bn`(5A5v`Lj9+6E zC>;1eKA<>OdS4=sz4-~kCYB3QL;%t$!V8QPE+!xtfGg(V4|J;ht$a#o0X=B8gG>}! zM2(^S3?ZWbGv)@Zz??XOLI&ErJg8G}$rB8}yvW!fNHV|kVVNmuj;>=(idxpFW#ccB z7V(miH9Fy<%zalyE`-|{0lL3U05=tH6y`}0L9&YfQcho3EVH{0K!!%NMdG2HuG4oqRgp!c}`j!D?cA&ij{(4EzaIN(RHrLgM2vaw=#xVFWq*^@J? z_f+8zv2s<0+Pq3h-?EOIy&S&4f}9;1N^wyc#rRYN2bALtFdFdjlVA-_7}Mc=gnCwV zshh1fu*!>K3zhpXmagk}p@Oh^hrnzc-qf>7ti0M4M@8fNso2n|F8(tS1}??C#_Tdv zpb|L#+eLl3Ce)tj0ca!HkDG)V2q4G zp0YCCRL}(gKYExVOC5DSI|HEFA!g4h@;IS)kC@L*PX^m|$+h0X8iWftrI*WSC>;1x zF^(66oNv@D%|m|tnGFb^q#p`F5;xJ|{l%B z_EQf^FS;Jm_=&OXEUdej$gNa*B`$ph=`3rF@3*JH*(j z^urX`f>?0Dg5QjCh~brL^+J^dWC`rJD>r~G%*gt-4a)muM(}bC`pc{)$#P889!sJ} z4~BGP7(&KC%Ro_IsDUFB5E0;R+)vJNKiqd1J>fAD!VzTh|Mw~V^gG*%;)6fUbf=y7 z>o>njcYczE%l`a<0x)}>FM}JT$|{FOxG^z1KUV_2^+-S$&d+ygoPZ~Zp`U!ZTfWMN|PMO`?!%k zHGyo%6SmE0BTewNiGwHN8GP(vqP}j}J~0PartkT3LYzf?(j*mwAk5cS9wc!GzwmQVG-| zDFBeZ25(eo%=49nVp7F)a5v9~NErC1SZ9>x3vhoG!62}5f|b_6ndcc+W+1&!VCxe? zxX4GIrJZ}0J~wT|1&jUCI~|%F+>I2yCev4ChE&MtqXfH4El$8^xS6Z!rBRBDi&ZVh z(pZjKA$UU}R`3qBPRaeHz6-2b!B^G6Q>i8SrsKWeG2u2|(j61F?Fos3*$1w7mr$mN znegI1+V<#ZcTZ~@PGjT9bbR=;3s5w4tG!>&)*8%mkgJqY#xQ3k^ux03Ekxap_EtO? z_=yq4M|mfc9~ybGt+gVnX zXFzu9D=7e$_z?)Pr1wft%7#hK5Y?w;^x}lZYi9?Zy@BvG9$Ij~8ZsQ9wbQ?sr8`Ka zRB9OD1*RTB!V=5PbpMS5MJGJ*XdH<4W@I>;F*!qkOExW#Gp!7JO9Ow1KN*+szOtzS zKTJbhh!R%P`U*5vLsugVCR`H8X*@dGUJ+;&hg`HTA5rxuA3{N9n1Qr!i zIbe6!PW$@?P?_;5NcU4xo}MOG~=p3m(WYcoLq=J@;$NLYV!HKZ^ENSM$X7sv+wrkPXz zt(85>8c#I9@cHc@ZdtDZ*Akny1e*^UGD0)j4(p_CYgEAcZh>-;Bj0I&jQmXX`^ zIepj))~ChDan1ew8}n1qPeGL}wCli35gIB{UG=ex`S`#y|c;k0&sClIH z2I}tA1#vMBQ8#L%A!>~tEfCleNiInXDD3-y6X5~hiXXyCCqbfMZ@0<<^AsaQm@DUkm;+hE^ z`zk}1FgU_;zp{fqR$h#*LLghiXz#|_ftFI=p0_E>vk0E+w?h95re92Z{j=^;UFWhFd2e{sm#tuhQwL&`_b0&B*5EJ)>Q&YvHRo>7s*_N{ z9n#KSKOXiOxJ-3*Ef3RryN0-!^0wn&2+mR~fsANeM`gkN4VFoU2&+PBt%+PCiMQnP zoYzyd`?f8=Y@R5@Fqq^RPO@6!Q!{T!ig$CDa%d&j#6f= zM$z;OQkF+uqgAO>NyxA<)hzP=jQEVelFnd&=EC5GMF5;|5W2o*z0%*oe?rtI;@vx> zhHAz<#fvd>;jMGWE)#O<*(n{6Z2Wb?$-TTPpKypc9f-(ji(4mw{74zh835YY0!5y& zjzq;1!M#qHV_(U+QOFpsIGVC4{aQ4b)1i=j$p$Mrb8xdxww4pZ3g+MG^F*nIUsF&% zhY4{8gxUOiJZskwPDCiaN#yoeU~DQ$epMdfofZ+MZdL@(+$)Pv6PE1?9h8jVHw5OJ zpwaH_bLC{!Sj{9P(eZHqe7*@7xhK)LZrwxHP9;LxHmYA=YC>~};Db|)3S`RH;}p$t zEs@ut>cq_oVWY2gii~ZC=$jjLB16$N115Yy%0NnXvp&gl*o*Ae$GR5o^X(B7y%gg< z5FS!NM!%SNzOB-Vas0;t!}>tb3;$Z*+T2%-~s;+v^6(MMS0xi z=5b*;d7Dn{ZrsprvxoUT?xSv`Ixud(Kn|*(@vSHL;|9GMAeqB-ReT819`2J}@Z+t5 zc^%b-do-Hsl^-{ma~ zk)f4u%R>YdsUxgNex@NUn2YB%lq`%<{xAl|9ODR7_NS{gR;%nkFhK&+H6!3ppyp&^ ze#`iDx3tn$t3WqH0*wrA9>}E+HAY`Q$`^fh2GkCr~^*T!E{D*9Zg* z+r}7e@c?^3gunjlpy_6pP)BiQ7a37#-s~&}jYUs^wP}uq*0si6W z*}eJfFcV7x?M4#d`wiNCmYL4+hRGy~c6brkIJ2<0V}2e8I@zs&M2dT=Tj&>#jiEn9_7E zor~oPfO@i5=ZyM6)qtOjDvyMqVhl=JQabmT&INp?&M{%a=-3Y+!gI(Zo+FF5 z$T2?CxSHVp2V^`a@gUFnl_2*NvM!I+a{(^Ukt6AugI0O+otIJ1RT(>Ud@coOkJ;*T zcFjJAjDQxY`%IJfx!$GVBFfK^S3ievKPNl>oLiDJc_)KoS^aYfG{CV!1$1@{=wO{d zN3W~7;A0US!2_L3b7k2x&>6Yd#Y&A`6lDxeM)n|-3kWDxFvtHNzCh>X=;Z8LRIqG0 z0$mz*d*%Ujp|i6u5tx)79Spn94a3>xhA8Tm;c>j{U#JteL*X+Byqf9h&Vr@7a#zoPW&W8(tTGr*m6ZuW1uq1I86#SMrfPFdwW{2%QmQgD%VuJA zTBie;1}c+V5i|rW7x9U?<@QV;OdV-NR#BN(k3LzxJY~4yC05F!KntScqB3-)$x*3F zW%Q;p4^EW{yc=_=ES5?S$79o9y9c$DaKA0PzHVKaS+5NCD$K&n)5Fxu0d$>x%?@i(?OsGZI{_Ws+m#^KoDy+je{Z&$kdTA z_<4v%%djN3++4g;i#6Fw65xJYMi&s~Y?+Jd=;2@PW3aqgbj=+D_ckqqY)>U)Tcw~( zpqA;AN^KyvOy47dr)*nB;)@Z!1NPnBK}us5`D)Wvei?$yEEh<IQwBiHGmr82<>Irn<0Y(7?{rOIviFgV?SpfLT z`YB@>;IVL@gX8BTs9!d+<=$IvDN{;G1_B632sa2?X!nTf0cy8Ts=o<`S9P!;9YIh_ zXN9W_KM z$||;PoTIH(a2r({hM-I2Ija8-y&h;>*U9}e7m9HFUtP!8-L2~X)(3oSgD8{uMB)0Z z&y#T(K`)mK5R>U_+!L#7Q{p(s%Kuy*SM{o%GjeAB`Jj-b}> zknb9=$8LR^kRa~*AB;^XFe!vg!(sp8OhT@oKe=uTC_=T!E4s3eED1RTR(=}4%Z|z*6?!SWw)cS5$XPXss zjRE^6A#ggusgTp2+B5xFb-oKe9u_U2Auj7HoIje2?dqn97O4)k4KC}+Aj2w0#|{uocaK5kBMrV|_$5Kv4) zemcRaG5-GBpz1E!XkJ_PaofrokRfl^crrF_@KY~tiTKoS3n)4D;)=W?2gC{=2e<>= z`e9K%_Ce%hT#;8)BNsnbxmnz1Sswjyi#-^}AjOu~IR-43QcBB;Kf1RL1GVdin{Nz* z4F2|PY+zK&4i3d7amf%%I3(eaghO&hrc$qmw0X~lPwyKE8ZES&I|78Nzd47KFcQf~ zlx!8!nTEr_2DWafE!02zA`e=dNl3qZ$#oatlf!Dm5EKJUlcT~4--q=vAHETu*W-Hl z`-ExWZ~LMuS93@J9t@>rdXtHi7+m*k(!W zh6_L|75)I;uxq>?uQDKh<$}scWuxGYCp~=^c!M*=zwtLJ5@(8>DF^yDo(TOLFHb*m zL|kmV%k|Y|pKh;hR4%A|dZP48uY=2v{+X_Ss-Ftqg)?1TO7(-|ziMqm6~6yy)3zPS z*9x+I-2Q21Wgp58MHVXlL?^GdXtzivN+G2%T@thvd>G7N%&*|};o+v$2fO>wU{K(k@)Z>%03DM4bhNzxX zyYnHK#(kJZc5MaIydl;{Z&@BRXCGii@#&#GL{SM@KbfLZj0Q3s>h;CPp5tS$L$pjd zuyd4~FTVIXU>~jjLCW|R_e}d1_SWlwy~X_ig~;{R|Dc2Q!^KhVvz=NGyDi6WeaMs; z)KjU`>AzIUdfMR7elm}LWyMZ4!j%K1+8HU`)IZS@Z_Xzm8vqjI)qOHO>=Y{G_{sRQ z=JPGD;w94rx~L~m{w^x=ouZ;A(@%pF>O0=L{SfHM^jCQ@eR&kmwFHbRbB@yDMX$%!{(i#x=q$^lTaY*trKNvoeQ@Q0 zPyKis{)0JK-o$x)XXgcUl>O5|{(K!=KI4AK-sOjVg>U53EpH>f4`hhr5NrgMy!&CcRhiRDj z4%0M|lG0M?|8(jO;<=h!J5Q$Hg(6zJD@yIZe6A=R)HUrRozkFg9~zv{9JRlNm?qYC zt^i4B`#hO`7o0$?kNq9=ddLLuL*7h}Pwldq_+8$#Z{u%NQoj88MC7}?4em(xE`LPz zvYGf_e!jMu_%##%+?hn(x69Z;a}yAG;9kTW&@l;WSRQn#l(nMGrHtdd6U zufNZ!=XvVxbQ9I)R(k-eoVC!cIry)pgR9)RE$Sjv5omAWIt!n6K!NUeoyBol3EBR& z1@(_ZUJsYL4}q?U+z;{Lb3Z^K5+D8#CC>ErZMp_>rpK9b@b4$$-`o)>{70N45|2dA z^!};h``@VK^9iEBp#M7JFmnsC7n61W73D|n$Vo=vbKw8Ms3p*jR#k6BV92y^J~#)c0hbDsE53K3OK221;47EK9-|NjJD| zeLFV47@H&aZVu&l@vV(EPv(?@MX)=`lcC`pKDqZI;geU$-$8GFXXo!N!IODTsmq@r zLkJ;l)O%~A5ON*I$UTyC-B#*z(hW{Yp2aBxo&nnhK{YVK3oFt<0T?sb0tZZ30Toy9 zGyw(;;uKcskV1r^F|e6a)}V6lUvMF|avn1V=?NT!&=vV7npAHZkj3B95f z1Olj5&6bx(PU82bqegDEl%C*&J7)x*Wpj2str}vA~w{UA7tyW&moNd(ItyvM28e}ryAL-Gz!i`D!U{E7OGeFIv^sH593#ilYedSGucWU z!iT*iN-5)8mL;t%$gA;@n5Kh);h5?W2kq0ny_@nsm$O7EiIynIi*4R2OsJIfoAZHd zk+)hJge=@`uY5B8n==gJqMpol>gg~XPc%XpBX^K93F*B0t|RfDjwaLrN7Q{&b|0ZS zqHfMop`F(p+PQo(%ROk-uE<*zSyGK~6^adeeJ$%o6X?D}_a55bEr#~5CW0Jf@6}k5 z*#@#ZeD1GQk`DkJ^?mOkZvIf+>^aQ!H_WsiH=fLXhof%uA8D16qc%F2c!ZtfXJ+=j zWY1#7C#96Id(Q%|=Dqi%OKC|Z~+F1WA%d)KZtOr!B_oBvirO9

+VSH{tQCxNVT7e+A!4ar#e<@e@TkkD2PJsKQ-Y+?M|)WqmpYAB$+U|&IkgGt9!lZ zqS91;Ns*|`gwPNwKhydprZGSlLM)->X?21aKoh)-aRL?8E;VszR^RFbOuQ_iEJ$Gr zG!|u7&?^lij_^VRm0@U2pi&505%l7MDnUm-GX*jji9ucJTP?8y?Ft>$w_0Ke%EfAl z6qcYk>RT;A!pjtT)wfy_w8YAjw5N8Z9Tzc_OrnvM%qK{bDJmquB$boaAZP9%$>pG0E^ZFP)d-Kbl2&wqBgw^@K^kF?B!Ql)* zYYVvHYE6{3fYr23!mwE}F$atRPHpo5Y<+UqHb!X+xT*e`jwv+-+*H@Dk*59AC}%e2 z#c}<$?Y)x>_R;}aCFlEO{7DT6Oz~-7+F;@{HDj8(Zxd59pRew_!|rZxcU}lj?Ltfc zQ13_D%o^)UTKr(j2^%jx=|;D;dzYh5nL7$z9`BpEg(0%oHFJSAbLqC4guLhaxV@>c z?ql{R(`<4+HE+%B?zZ)&m!){`@%J*fw0tn{@`%uzHwTj(QCwF(i{cUk)2_rHVZR(I z$f}_G*QmRxCLwYwb4SQ1218G$9WypIII00@QanAQ9*X<7>S!!vXsA5185GiBoC^qfUP+*+2yl zL*<{R)Usbep#3m1b+_k57%R_M=$YUZj0&S z0u;!eOqX&(>h;(Pg9c=syPgaHUf60danjU+ zhC>LTb2G32gT+Eg!{LW!7*Z?5#1&MCiNTQr;vnUS;YJL8!$u5M)Vu_sUuXbm-aGjT zLfBFYAk`__+f2!9M@ps|0y&!K3*+Sbv8nG$4k#!Cfx?HVnLVOX7aVUD0D_?=P$iLq zBvw1-4CO)PEh>9K!$Yx-93@z47)clcI66WHGq_m&L%SrgB}F4ef{BhilJN{*Jh*5*bp}x zKK%-z`cs#fo=VqP^zZaBYuw5zPnc3Zyf>xq( zby!K|BqZqKg`K!$@UmD-m@H2g`((;?Yh`tW5m62ye;&{)S|25-2*U{u78szwizZBv zVFVb6pad~+LREqw)a;#UY-;wI1|*Boxb7yWYM*T#*F*QS`P#N)$NL>=oDZHo0D3^5 z5U_t8sbWU#pki2(CMxzci5eAqsR6-CmImbVS)~_MzSL4>5lw(dYU2i80|~~EPY7;7 z0wmR4-V{LwP!JH69UL6hQ|Z*4rOQ$F?*@_jx|cgWIS~l>Ti^!&K@mW}bO=E^nEv>H zz@!R8gqQlRAVLL|pAwO!@|XDk1eI4gOc+88Ae?f+5Ir(ff0sLWKRc^COm#D=3!(bU zD|{tH(5GZTVSbU|pvqKtNdyvHd<1H6l~}UH**EoXeW~@aWOsLWcX#(^n8vEhyPvY* zD`Ghvi}>s+v7x9qFsb=h3k?hRO~Xn8&0k2a`+ zn+vwHMLak?IoGwuwcQK=S~_j%ptevJcK`hrt&bK&a0+@nbX!3#1lhL+Bm;^e!ElYF z55@04W5YG`-FH-%b%<9%xo&D~t^qcnUo&j$zG5BrP~KDtvsfPRe{|e@apDuxGvc+{ z_(!BfDT$US=?W2`utNdxf(tOF7FZ~)paKrk4I3}iWu(mvyQCGN_r|1d)(EmGKs-&5 z-~?f@!bfLG7NXUZCp9OF1Mo_WREkt2JPm& zA$xoTpd&~*LX;y&;q^V43~14!MGF*_X9kLTVtrSNc&xZ`#u+@dlUL4wqV7vx6~QsX ziWyUIpmL=QpDSfNv00KON_}7~X;M4+z*zD`mGyzK#0(s|_>oFp1j=AihE9D~%E&20 z2qQqj3@gm=sqcyzJZ7+!awcFxIre%v#v zhZOXEP#PH!{)Z{q79e>*yp!}eYkRLr)hbbLdoNlq#)lDYmi=hG7hj_3lFtLvZ1!^l z5w5|0e*zZR7FbdDi`2Ag|B?}ACdD+c${a07j4^UtJPilQ1QhD~xV?|R)p{HZ+&Uxt zkI0wAc#jsNomG=0teQM|q5I7pT&eDA%$4}G?|l%jPeu&-Modttf;C>cse&c7!KnRG z1!>R$5S5^$(}Oyz7EU|?BL`GYV1zW@89Hg$z=|n2feA&Dh#|(1ZwzOi)7Q87VEA(7@qbp01&yL@fZ{nZ`fz&^HVr0)eiXZ&RWrN%vEqZCDnfRM5{* zd{wH9yaM6@N75cDh7jSCm7pQXgh_0jx=Q#|WMx&D4*#4QMN2O;DgOs|jjJ9Ge0oZa z!jU;8CGY(*VH%IWJg;=3|5lyN2PVL>RdVaswkIS%KEa zPFXD~e-5tN6%e+}<;E1oE)9nraFC>GrXZ+xlth1WaFuM7t6TuWoRn0h>bp=<~X zpSlc7R)tU7{2#n?Y9&h?$8j7@c2*Gkq|g+ViJT{Zy9dmrilwz=<<`CJx3; zpGN)AWgqva?@wQi%lnsJjrQ(1R!$^}McK>sbTbW^pS_KD*-ZY}-}r2`As2UKRoB04 zH!8Z_s9d%imA2ieo(7}`BG})+-nk)2Y;Z?bmHp(7tZM!c$+^yw(B0j8kXsKch0w6V zr^Zry`Uuf0=i2ES?fX(vyDuIo;Pe)sAE1?$prMQCl5zz{)xL^E^gAVmxF$+rAEK_g z@%^!=YixYKBzisI_a%bqqouZwRHOEm+C`$(%8HdBYt=Pyr%^mz<3u<^L$!%~6^VTH z19NJ3k?66+-%sQyw9<=NTU(a}-jE1xcEf5##G)Q(~R#TD#e zQC-!`2E{)k%3=}{i)xfWJND6rP)@oai8&Fc_TQ)=Q}JZ_8_-1i&R4A(X`6|bB2k^6 zJ@#{pSwEITR3mB%`Z36deC1X~N+QD`1r@GzDbmc29AtFU2y;RRqijn`CUP?T3ugYs z)+Mwq&U(4|zV((rgzNs6d+);ah3{}BWati^1@Ae0ez|Xq!T7y(WHRQ2(6S(e5Pp#B zA%qfkMc8L|cNs#MJp#0VUe{w2h-1&#b7yJ}F0r_--vl|B6ZawXEtV1mz=1nw65@kR z>iq!f)c&tRyGep@k|v2l9jNmn=&_p6-AV4t5PFQ31*5Fa0dZ;xznsYX$$HyCh5CAX zsjt3KecF)Ms3}&4@We88v0^9zsZB7051{;7&i`3UOZ)H4^=UY$0gM{3Z1nM-wFRYySoHoEy6_n%mU5{$3you z>WLb>FnR5)y1tJFHZ<|RKRIhTr%UMUJx`{22+#s2s{73q)qOO!ppoMX|Nib*p>B0j zm3*QB50>s;LlkCqjq5F=DV1}Pk2b@$vzLh6f-@p`yjGG6gH3W?8g zitYi+c~NZx@bUl{-B$az)y-NoWQSLd3S%l<=1WE^KC$|kXw5L{|G5>o5D<2Ej17sv zaAOq@p+t<96|ty#y1N_32zv&c9PKG2R29CMQd%|Wky0qaxTSV&*TXqx z;rUqZEf0Q@towRwO=^W5E3EK(y!YcisFPR15U{JUJ1N}xWRh6+C%fz05Vr3=>Dk@g z_4ZEUodjMH*MjcyPP@%n??g?zoI6mChVK1jee}lF__qGg%3IFh^|&VN7icS}?{7#t8H<1)K^PeiJD#n1^@9wrE;h=Z(5iNbo;p=ULw-Mdl-C4-#v!4Xv^U>V(cvv~A z$bCH+_7RlG80=KKocqYYMU2o49xP!}eXD^7N+6;7MgS=}3_2z3-g1zp(T#~fymLT17`_b7h(j(P`hdEVvHeP4<9o$?0|V#)XqWWcLiMH>^{8j zdZ-r-IIqDO=L^>_Iq8*!5y~p?XT|`^A>Dc88>?CbpUoM50KW-2shqnmU>KjBS|RXE z{B8%d-iA+{Y7}=T=U`}vn7v7N!`>Jdc0=jzZl_{W67LI7hDqx~E)VwafLtFi%j1QU zzm4YPJDDEMa+6u`B;okH)ZSolKn`h1@$8$V_GZHuIRL7?5uiTlw8S(o z@6@loQ!jkix=_;ecP_Zf@6?wE{M7vGGO3O#g$pH>B+Ml72RU&tkfc(jq!OyWpB+Zl z?^I;R56}-LemJ1Vr^%@O?%l~A|7nN@Jwmmcbqa?`s3>AmJ*A4U>WM<+GV5;L&ooL; zj=CFnvYWoaxK^q{Q!q$qH+9ryO7bYO>k8c$fD?L90%d0AXGW5`F90SezbrAv7_o>& z*$p=%uy%C#Ct7O%;szQuD7&)_e>Xb}=PZus_LxyOzRBuNZxP%W>f4Ht zYUFl|<-xyWRQ9bxiI(k_-J*S#1@Xx(oQ-jIixk?|1tBwYR)pnoJ#$dW+;&3tmYab# zctE>`>DS{`wsyn8Kl>nlQ}@>hSINz=Jgok>Enjh2oXgsR6F=jeDa0p-Q@B|iB}uTL z-A-YVFsi?QK=~opJG@5gz*W7j@t@zDtaJWIHA_Id`J>^46GCR1Ov+kOv;Hjm(fp0l z(@A8lGa-aA8(Eg7Y1&4q8i`g++q(Jj<^1ba9_qCfAyvtB63Zh-s3ps45*UVg=Ds^M z?X*EhTv?2B&gqMVk66i)-F|rV=mIH*ZcL@*4vJ8(b9em_CoI0MJh6e6V}SI znvfzb>p9oVZ7qvxzMBLy&EMhS&-cHn*49lUmUR`GRdQRz^60nX%h+Xb^|X+29ZmKb zH=m%Ls)Au$7n8~|EETh=RP3!OkG%ziT|FCR`c52k;u;NJJ30ZhX*^M5qT{|5r*!Q@QBPG&agh#kLXJC@l1?=eUSZ&yZjw)H`R@BwYV9UN6*wGtgdT&PiAtR@1S&bhkU zzqRktclW6KYT7P6>Z@tH^hnFzSN_-5&D86$Y|hDHb54xwuJ^ZIVsH8X=m_20LZU}k z&V~-{WU16}a@|}czonFgqEYAk3CuZXWcZIYtexd6l{D_f?BiR{nx=h_S2fNw`W?6i z*npaZXy2o`VO#fsioM*r9iwe?K4sh0@iQY`ulRCHouTA6n&nC+pk2wP4vY6C=c*w%BEV=~k=xWFh%i_A3pQ{GT-n)Ko*>+u43<-ul z)Y2W8Y=eiv5#vfmx$Hf2u}4)V{{i2b7YFeI}(JLYQSQw{E7& zzqV`(@@n?1-SBG0J+1S4ah?3ed+^o_SfJ*m(T2mksgtc{hT{%D7dJvB8=`-0Zmhyy>f0ASwh^UnHUwu09S6&P55g>kMRvv;vIv9!(K zm{Ufp(?`P=xH!D@wA4$$dY7*IO-@uq8W@Y`aBjblxhCe{NY!G4K}JqF8Jn9fhdXc= zc-r$g=03L*1Wkwkvggd$8q(I+LKyuXp~NPxsBzSK<`qKwdy9HNuTR+O2xB&46-*{f zd&gMvz!cE(?}(rIye@?Wsjrq8*L3;tsy7*CX}2Sos=Cv=`dAo~e1rVNB7)1Zp^FVa zaFA#1nrR0ftC_Iu^F*{=xfJET57@*w=r6-1fK*}ugh9PmVq8`zAvm6T#GQL$<7J@9 zp>~rcY@O;GNoV!Lp!JcQH&>#Rl_wu1M<5WJn1bLnX7GbIvU1P{;7F|7F23qD7Q--~ zdLX+^%lWy$;RzU{(dJ@WtWUnt|2U(wI!DFLzD(ITSqU&<3bMd0O;TH;*Fr)q#*HsP z(JE3_IP}o0gXx?8wc70+3@v8QReuHSrRS zak!E9!|LLeVA0ugIemBG;kffKEa?mc++e9iT)TS^qKdNM)X`Wx#4B(uVN{S%x+jAs z)!`6?#;bRI1byy20Gz>=>#knhC;$zSjeVU90ZlvbPh^I(ErtdZhfs58y=E}kYuU`@ z@iJO|u#ZF^rojLd)5eRJcNjQ(JiEDd4okX5a>MQ;lSQ7luK7flxI%5(TH$gS2R7`0 zFxtQg3^^Xt0}=6+^WNDWy<~Y@06yx5{KzAW*Yi2wSoSODgY*^hJ)c6B`6#9Vk54lVtZ!$o1S?=607_B@j&7ubuhccGlkcKLlC$T5n8 zRKu=CiGU#!zEYN~LE& zYaQDR46A;d?=R*Ezx()~!D)lc^Ui(`myMi;R!09`r8`(6=h=l|hAR^OzS9k-K%e2E z;HX^%R^m+5=Zmuf9y1=ol%AN)GU+!Ku_$R|@3#7{yf|*8*-1r`M zHLJ35MmhNMV}`yEwsbkkL*bc{@Ll>aE&t~Eb=ZMR0NvU{P5CDl>;a2lw-}qx|#mm$CaE$@`fmgv93-1w8s=kj;XgW2Y}0=_gEQ zXrU2l^+$;PbI-03I}r^8i}C`IYPxwtwspwB$tBk*=tjwesgz`P$vBT+gtCM2(nUwm zNnvr(6wxZfQd<&n8k4a0^`i9h5Qw*;$%98T%f0?Ro}mj5z)A}tp-b0SxRe2n6Wls2 z;-g6m8wC8W`fgWWMI@zz<;XUcdK||^IKF)!d}{vPm*FHa^VZ;~kk)Grx6Q@B8|VU| zlrM-ngucmgvz>bayONThk?;%33xp2Zc26gk<#i8p@B-}w#@Z~4LkC0#VW0O#*YPJi zwQ*9o?#_$@z_m##Btcmn`biZ?`9Lh_M$69JAM~bc{{Y>?#_QB@D$75Byk}a?KYuGX zvgd#qRZ>qrV9aPe4lK(FUFg;dbIfP}`3RWlsDx5ha{#lN>q+)~{5C&8pcNPkE4j>q z!1v@ZGc`-#Z%;DPm&)--oAN}(jG6q8LOft>S8igZ&`s@s<- z0B88>bAe)s22rpX55j&e7Ugf~Q18pW4;3yD0DY;l_D8+u#4MR=fx<(We=dQoTL9|k zmA}tkoHBEm8ixyn)lXbDc3CJFR7tuIQS+13=BP1P9mkY(A872G_jr;$;0{X3ZbxzM8uT+zULk4hMzx{fn{XEx+KPfBB%ovu9OWnK zokpZp0rBnhu?dsXgmgCq(-NDo2he2Mk!|55id89eBDgs z>#{)%YW#n4k1%6Goe}V*5{q60%JgYLK{Y+hZFEX(OH1X=Ycu$J>0?WbaXj#{{BQi< z$hEgeSqEW<3!JyjW-#knLWJ8P=V-G}=L~W=1aH~b#|sDVCAz6@ySgk?rt`Ewyb9Fb zu`(uD#~f>E58+Ilq-_LDwU4X06}1E6JQQ!Dqo-I+7p2&=Ox(k%3ZF1NSjboll`y{) zS2S7LBkb%JRe>FI)WTQpeS!bC8*YeK;ZExZvV~cY;e?|9ihgLbjBER^rSt@4xKu#) zvX4*jb&kpw<9D2XR=&sL#*KgIM%~m?nAM-vLXcCRscZDMFH($dl%n@*NexGRq{f8(frN!@5rD7_Fp>eQ%Tj)hZaqGs+oy zv&wt0>le1DxparW?gtVXIVrCBY;y-M$h-rQaEZ0LV>10}bi)XWI%buOKLlrd2HeUY z@hAaFM^|C^QHxn9(~Hpc9mtHAX%r?4c*HNkh=y*HKQ@FK^Mk%+<(&3Ld?&DLQ^FRu zuJ93s1TDrXT9-)NU81M<4ynlaILnfT5zw4PfZt3q5+6DMh0=2}E z4!TSaR|Wq0Y~JY`R)fWQeX8u|7|x*6yoCxiZXja}2P&OWH*n%Z83^gbE|z7r9m4RF zv<_WyoZDP6Af2wLiKq+#&r!`i2FcHWzI}6s7Gz{`rkSImG&dHV7w<%tfn&yd-z;Ao zk(KD~k}vKsV-%q1hH)H)F<=xt2OYooRz|d_>p~IYg+(WQ2k^1d9&6ypf0LXXtPR`D zzIDXzx*3mY#Y<m2J);`y9k7faP-BAIB&sw0N{=7eaksnCHzCo}n!wR4RX={cY`;P==S30#FGgSK0<^ZtA?Lg+}tZ{i#3N`{KBy6zzOpP(!n_C&>c;y2eo&9e=ZdD56Z{mK+<+mX^`2Hf6pr?*FicA} zxh2OGZfx;4>J(QT&jO`ia^?cAkjw@p&rpQp$8Y*E2?f?d?mco*_qO4(wa}ca8NCot zd>Z&+(io#Rpftfsj(mkEcFA>CUQwJ?e1=cvltU;nouw8~oN)Brp)mA_-t9nU@is73 zpKt1Ao=VwN%y`Q6iKlagX0w24^ogYuh@!Sz{ZkwYTY{LSB3NJS-Wlk=O7uzI%!ZIr;?)Qr)ewM&Ck%Ty>GDWxidkoQP zPI*|R#0LF6P;L_rQzN5R_xQ(Il#Mdk|2(6%4)~%7gr-}hwY)E}a;2=1ODEGr#|ym@ zezgG7TmnW9IPELzq>eMeZG8Vb5%TreA)|SCpA(-sSF1jyGKzbOSAr zibf-N?PC}XJVCLn^a;8X1mq8y6xcKhBYcz#O>)bsH^Xy^*A?c{w- z-n+ai5q`gqEeN0<%ces%`)Arf{7WM~gK;qqlRj^yKD*U^c2G^uROT7gq<6tZwB-O0hW<}J&e+$OC_`!R*dR)&UW()Dt_l9{eYnNd9RS-25+Vc+afzk;vuwW}}|aW(HtDv0@AZWEG~ z{|d$9)6XU5)q&(=^#x|qdeWDh6;1Ltb$uhMH=SKEc5^uiMA_A|=543amj>|u^_K^P z!d9Gy^?Bwi9A*oeVv6nbbc-#b+ChUlAxa*q2!DxvL@F5dJUu6=rmh~-c0(XMw(^58 zhvgQMcI#XJke(+~@yI|XE;`U-O$$HXC@vu$c#yA_Y{pS*)4vA@gosB>f%xKP5Li}a z#|$W59(!V!;Ih%lAUm=ss>0cf>U9D6){{lJgKB?K(q(5Ke|4uF)P=5zoU2gWz0 zfaTI8xy0>Ks2vuNo@&xfXE>MPG)kUP^pNcw$}{pnoXg?XSv&*RbiL?g3xM30gJToz z(YBumzkbUhzErBIfd_F89)5mi%>>VRXo}Nm?7uFsg5r+|E;)vs236h2F-Uco`EN&k z1;s{!lOu8k5PF^Jps9XCR)Rcox}XfKGb6hvnXEIk3`}z%gCNN!wFp!AKJ;pNpL9YL z0|)q0SJKlivw`Xvr~+|eT_QB-d<50AK>g>9R==Yr@IFuz!zXL!?vryU3yN3KZi6=B zm)Smdd$%8L9U0q0f=uz!<3s&7HSh^L4MEmTin2{o|1Y<1PrY6v1qiW!jbes)|B=Qh z(1@6#m46yW5=Tw#HKCKt;0D}?YR=ODK8^Y1l1Ywy&KGi6!7574@Zv#*_*q9FT$?n{ z5pR1pD6F#P?#MiZ2g+ityiYU1Rk5Fzs)sY(;K<3dQg8l1k(2w2H(KMqguF@e8xWg+ z-Idc%f#eL2OJHtEpDY(qT_sVEI;>!19rT6sY!`8WgO{Lun7gYKv;@61_mrv)(&Uq?qb2>OT30?=l%ezkNg~pCx)MI^@HJa&>Yl(Y3J7Q!Z_h_f?*%pE;q3U(VGio(0J?`Djy}nP5CBNmJNMdSTrYOJLpV(Cs?CBt z;+bmRXVAnjRCND=Ig~M0TV*pGl*zIF)km`q9vr80-on!A=6Q48ZZO|30B&I*POr%~ ztZhL4WdyA$feAa(iQ<3HLf+~Da z8rRdVqjCi#V~sJPEe(H3rM9xdD5dv7;?hJm`2Se${Npn>FBozSevM?O9(%E`G=s)9 z7DaOTxMj`fD=c~w(46S*(Pplbq|;MuooP@Fr3NHJFNPJ|P4rm}K?XyW`M1LBak0n{ zaI2ZaEB`RqNL8^ZoJ+N4hZrjE^*v^~W<@7`H6_rMFOxv*C-Cf97X0c*>IcNQzw><5 zhhlkE#t`8Z8D)_}Jkk3|kRyWYRt)BrPx((0*jZ&-Sd$Ii`(p8*H}HrWdCB2cw}wi2SJ=V*>0il>lp~DO`;ifJ3wA zBovc`vs8jS;w1WKy%n^e6PL+9zk*xVf2ZyBvprl^o0v`E=XtyJ62$Yt)Huz*1Vyt%9O#8 z&fD+GCXaaKs8VS4rk!D(ha`MhXo%*^i1xJS#h+tcDF_ zs-LdW^dBR70UsjaZlUhh8qe&66Jqh8c4aYiwvCVEOdLLa;>F0TG2Mi|aZ^@z{uA2R z_YgXB1yu=_D!uC5A9MTi%DPZHQ^R_Y_Jlztx9};gt3_ntR{`7*q-=w^b}JUgsWxf`h+1Wt>AgfqM3PY5ZfP>KG{vxLaxmG zmk)>@%ji~v{f}E{wvQ||G+hu^Li{V-OjXwe`T$Ekk^n?NyT69S7GJa5830DFy@vae z{RV*ZYi9iEJp-VkT+WEC(1b|o^~i{=Ocp3{^nf0GoOkzV00G^0qgbgvb@J0Wk4?QF zAQV-B^SX4pPwJhiz^lCK6>SzicR!kpJEfK*F@KQNol(IpnK$KgSYrrGE{wupR@_dc#3=SsQURTfPB0^hcx~efz zWI=y?8<~*M1ibnQ9+ZNWGviX*+R&@3579t&3Ki&eb+Q#d?$lGxa2oY&BS;#l7NT~?lE;TWZ4q4Oe>mA8pYfwn~<}?;JWoW~Y~iKhmvW2-$r;WHm(Iy!Fa+ zx))v@Lv@Q>PgKEhg62meFpLASR%U?w{IOS?wQ=8|lX92WNa+?H-sg}kbxOa2=RTlE zs=He`Pv)TS%MN&{1679JsLaeG{beg`4YjJnPUVF%P!>hwT@kyYk2vb2=9id{6Jllb z1X?}T1<3b@^&fO@tSL!Xn7m$l1IvvnianLCW4mE_qt!K6x|`uun{mR841{<&fcD!( zym!j#!Szmb2_1MYa^*0`oN~`S$DDJMD|b0^&26q+=bUTKImne;4msv9*URz2E(sj5 z>g67Z=3yzfTVB|tSMj+#^aUr(!`kqLw=7AVG$(Gg$dX6Hub)v=>~b?xEB-=r)G4~M zxfB$>ym{&hxM)5q#a?WVdc}X`&}TJ%aKXiHI#*oG1hn9-2kxg=tvlAbk}S?GrrM{C ze+gJ~Yan~rTq*@0T<0lud65EzFPj-Bb_cHCSu@l|Jpv^K7dE@g;Q4a1sVkxb3sot5 zQ3Z-6za2w(j$LXw6C?ZhuBK^^ZQ;ZRkd3hqz9NW57dom`2^+z3co;}^+J=6}bg2I0 zu2SJ=NGX(Ez!Pn^qs4jWOHf{L+F)hV>UOqtaapAP9Ck{mAZ8ZB6d?|ZFlo1GDB2eA zJ+HrI;XS*D3$Amwb2jM5u?J49%RZAUw&nWMc@{9skc$1Rw6 zo(7DP6NcKD$-vQ>QB-L#$UL+jnwJNGCnt0f9CZB>;-!T~hREt|>~8T~cEyz-`B_tP zLW&^%9$)K_7NW|UeWZsx=PmO}Cm}(owR-D*Nc9k;T>~L0%_@c5Y*_Wuf}jG@!)!?a zFMH7yt!&v?8WtE0sB)Fw7pgF7aZAKdIADc+Yh<&Sp|Q`#mL6#Z<)ha=@C=D~$*Ip% zQV^U_XlXf!T1d!6{*Ybb_LwTV$1|vs^tk(>LgE(7bH)(4`2uj>SWGuBq?!q@x!^Jf ze@hC73j?Q}zv|owimMi2FRbiSC4=n`Koc7~+nfRb%TDzP?-{4*3XS8241aER<^Q@)re(wvp0^Tn?L6Sul z3e({e@w}K8Ev@La^hW+fhO``6e4S$(ceB8bY?Q(P$zEh|UhOwM4*9DAQ$?$dU(&8e zn8lpT{9C*n=rLCj%^|K0Qa>F?SzN5oG5Nwi;O7@R=`p=sg2jowQsVK%eh?CP(d|pS z3j|1zwyR%<)?=FfYf1V@z!nX|E7wsAxg+ps$%0WKqh!E+Bczq5_kvo~kyN!CI@>@c>Y zW%MMdPiW`x>Ns`tq~YPOq=rri9hiwuzC&R@6tqLl$I{OEw>jrd}z|oKf)9XSO9Dph4LFPfRY4yy;bIFDyvZ(q8;mripS&JIJPPL8dI7DLqi5xK=<@9q+pVBJEXOjTo- z;)Ww22j!hI<;@LU@&ye5iPWsGJ09;duQ@NO-1^qCL$-!lP&|ltyJiUln-z8ZRKcpl zZ2RG=Rg7WhSYQV~=wG@yw!=WODdc$Eq>Jj4;7s#fF5q`$qd|qsJNIyGjGJGfG zLfo6V6=JuB+%aqTBY+4X zoKNQjfSUq11Ey3Z5dQ^S++u%Wq)Y`-t?M;a9`DWSO#$?aos9}4#XVudPMMwqTvJ-h z8a|-Rcq>G{DW8_N73~Rpv?(Z;*m7~3LTZcdwGfJV=C9$Sn~~c`)%-H%9Tw4)FmN3Q z32)iSaOmFFQ9quO@qD#Tpzx8pTjOucJEYm$$8tU(fK5!-dX|iStY9{vE{qtXKxzWs85} zQ}2j=3~2Y^xy|BQbmIT5O`NBG_j{>?Mxc8mJ1Xw9PCH`ysdcUP<#qmw>xU4u`IGL5 zH#xmxVwX`7${Pkq+y9GH*DUhNjK)%_yUF)W`Qn)d(}N&PUNZ@~G5qG5+V7JW5K|7! z7%Q)#rT3hp0VLVPlT*f=jYlRHC>co0)xVs*tH?op%NYR8^r)AUXno0^mjYyUsfXH< zfnFD#rh5Sb-=;5-@#R_qN@C7Rkd|sCY|%ouqqRfEh(Nx;txpSu5jcx4bx^}Y1wV2J zn3kL|uH%4}L^JxSXRIeVwV-o1#WUw5$>ag1pNh(T!h6H#sCOIMm8msnVHZIFS z0vqya1#G1_MpS5R*3Df1x{PS7`}O?XQ^(VaFIIT90nuXF%tFu6EVJt07kL8vIVf969AF-9PLIq~ zsX6=E&4^Ke>!U*vI8|^FCUWF`BiW3esEGRv*zPN=F6i1CEjO==&O|cnZu+AkX4mr4 zZr{(;q7+;Gd4piRr_ObNW!^o*ZcM`F;KVGg2gJgVEpo&m#=(r6YjPf>RMGKIfi3Rt!44rZ>2i1!O4Nc^W9>aCP9KFp#)PmGf+XoczuzZzzcbMa3Ql|Nyzfqpxfh#BHX5N>5WK_>$(RKvu~IN zbI-*H*r@CVE%#hk5fU1XO_Eghg9i?gZR~oEr7}4`wU(9c!wM>}GLL=kp~^FY8XcpF z*URdLmD(zn>18LCeSobg9eq+7InWl!$iA^Isw1BAvyQ{8yqW(pjkH;&c1W!7q-`JH zqcL#_?as6w)s^>K>si%ewybF+OejSSK=r4p_Tw_hu_wy%w){U3c1*<&V5?FcxVY&g z$O2#rdDUIf(C%wbm$cp$tM}x;ny~`cts#$ewcs=hm6BGwL8C{P{~HvhgVImSup@?W zfS+BUSW$f8dh3mc_L>&Bv{~4rAH%hwHmT#Gsqn;OOH8VRYQme4xd_ImgFj~Xn9dE@ zmO)MSm(Xx?1gcqP4V_arJ~xv}yL$SSZ_tQT^O>8DKF;n1Eh{!d_Np4B+ym(P*mgt| zxNhcO;b|;WK-hA2Cdje;P&*BnR{tc1{Mp5H7V-;C2ixd0K^BnfgLeUq%AhhAIg#};#*~xB6hU{Vr#)BFQQq=7CF;$`B_YQW zVvKCXUN-%#39-|;EZpt)Xz9I_H%U=K?fJ?xDcav8)gJ5B&}LT}&VXw1AB0eN>l-wT zL-$l<0fEuYP;`$<$Gw|fVU;;SrEj?Q=-n^~Pk1N;GZm|pW#Stq|>KBe@M&{ng&LIb+r{x{R zgbS(k%(zI*i zGfHwEB^$7ZsSV^V#eijpvyAs;2CT-}o~dEk7oUiH^V`RVA?l}3m9jC__l3gC#fsx| zRBu_)EygK9;F96@Vi&xUa2Kcz^7}|JyDocuvm=i9A9U(5bzh!(B*vNvGbZUNGFD15 zg2M*h1i=|N(H1hGHdWae^n@@3Iv#20HHEeWnIb8fcO0alMO7S7L)fHo=AQWa(eE%*o9T>@+53BpBAr$%}hq>2}w|J?jx)Mx`f>n)o zmG<}{5Z;j~4v66j!&RUUSeJ_;`p^^Y?Z#XQ%k|{Y+0#y(>D9Zgj%i|(oMg?J&T{%k za{LbsOAxysz>|@?kU%<^!uJ)|zItr#Eih8^+q2aYsO*BoCW^tk_?&*J@V9%O^>8sgs-Kh^F zIhcw-v>2Wvhe$#1lN<3$L9+wV37UlnE$evMWuCV0&L|AgB?p{bmkH>(s8QpfiLh4r6SO8 zRr30axcWuT0q$#{bk2ikeb(dU1PmjYhzxvAX{97lL0|_4vhToPLTxyitiqwK$GLqk1_ifElMOBd?;}^E zbTkt*-pK(tK+1|jsDf6>Yu@`L?>#m*5PlfimoyO9Fl>7d`gXBjx2qx-F+i6y)^fD7knC$zx&k$lh0dqnv>E0b5ue2tzh6=g0N((o|>1<8& zw7+{$&o#yN;##m3TUAiU!hmTB)kCv0{Lh<(f8=lw+Ll`s4Dl_hrCW5^Q%H_(Thyb_ zOL~+Jfz+qn4jn*hgCa$8aS1Z%jzkxa@{fuXK6QJd04nw241(CZJJ*NEwvJZyEO@pC z>MY1}iy+HvLDR5T;Y6N?F>N3;&-UDZJRsxJAjGWau{T1O*It>7^R`NY1Vo^Ny+=@o z`eL#>1H|~iD6FCIw3q_Su64@;_Tr+CsO2j4Up^D*rpSz2Q&1h-v3itcpd~^e;8-IZ z#cB`9Ca(U>zSAdGu+-F*VGe1{O7vm#h!P&XQ6q5Lw^1!&#WBV{IWC8AWPKYo>#)a* z=Q(k&-CTWqwO`|XZQ7#JFm<-r^45q>@AlwbRr7V!fVlK2G)DryQF%l!rb`iC-T@T8 zIP?#UQ96##G@%F7x)Z_sXXKDGMzi^oL9{ar49|J>LBwCfA@~ zt*RlBwFNO50t(S}11KAN5=M4BnN+TsZ;yES#dL21SIT-3nCmY&*K~gos=~$P%OvS>*r# z!mTqV7reUU<~=hT7V>x3$K#Ms&M(0-9cj3VZbq_kEIkWaL3Kzb0-ubVcQgM;lH5-5 z`z+PVC+w_94ktz}kP{U@xVN>csL?70D!*#TFki`jpPCS8y6~R)3L8r3D%@!J;9`-m z70SRuZfqgMZ-ZN%gp@WL(w|SjjP{NSLN&G>>tMKOwFv1d^e@1e=UXJ2H`fv-ge&~V zGbv>L_awS#_=q+c$Cr8ovfdJ?;DUT(O!>oa`zP-Q^(Yc+ccAOz_$0fll%QHX9j?6| zT-4uw>|?Zi&@RLp=V`R;_H!q+^I5V&&LF2bSPyl!aH}(oc;u3js1B?k-v82mu?;ui z0L)eL?SQC!lJ?1P5XQO0qm#xZ-<`=zPbmQ4c{?ms7SWM@V<;0WhLeSp4opk38qijI z3irSUTF?PrH;W|=q|>nPOcMo|`(ctaNoN2@tBQop4IsRXuqiM~Tzm@~q5oqfTDm1a zJAEsP03>>>z`x;S$9%B%i;4KE9raDMGX+H7l4t>L0F;v|!3+;n<90l7(_7Q!HeD52 zVp~ZpTA>&@MFZC3StiH_B03=78KDRC;K~lC+b8M-Gznm9d^*JO0SUOF9YEikj|B|IA5%6eN|a>7nuP*I zxB}Yd=mt(9OT(Rnp_PGV(1J@@rE#u{-0hY@KhE6_sh#xrOWm zG|V2-IMwl6xy2nD?m}hU{s20lDEmm=q9R6S-C)p8pE)7@=5gRG0uwo_%0?bP?ALbw z$q-Fo7bZ*>suAW{ky!-dSs5n!%D;&nz?$%^c?4p`HamVd+hS$fgr%%Ra?al6DDMVz zdY~>Y3h5{8OeGggi@xKjy#PRh^#iKmKCJ4#{2M>Y4FYV_-V$zW4b%Fb+bbs~{xrc1 zWwLIVQFiNNQd4tIStfLx`nrHkb8G!uBL?VYet(_4Xdg8Nxc|d!a zEmcGim{7I<4CT;@Hl#hWu>KLbGB7d#(Tjb7^Qe5)W*$rIl89JrNV2tG=BshND(SdThrN;T-a`!9L|V+Y;AA}r1Z#PKm-zvwreHyjBB-U!gWSXNP`EUS49r z=K%~|aZWbXkwe!LHKO%4tof-aI=E1Ps{b1O4ZBR+jpP}y%9JY2e2 zg_#u%^Jq}0<-rMiciuM(Nd}IjDirSuCBxk>qckHuinTzf9~GI8S{>Oh>o}yevILnK zDj^ADl?3)0)M<#Z7a>R}0#iz>SJT*NNvTQAqTGw_jL|yI>$8iR_H?rai+FOY92wMm z@kW0=YBI?5!d|i!ypmMj986fe=BV{Nb#j~R#i&(cg=%0A!=n*xOO84ND@_OvM59!z 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%0D2#XP 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