From 26c668f18bfb9662a292741403808b0690191665 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Sun, 20 Oct 2024 23:03:37 +0100 Subject: [PATCH 01/58] trying to backport the old benches --- .github/scripts/cmd/cmd.py | 144 ++++++++++++++++++++++++- .github/workflows/runtimes-matrix.json | 33 +++++- 2 files changed, 174 insertions(+), 3 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 6a624bf4237b..31184b08a5c5 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -116,7 +116,15 @@ def main(): print(f'-- listing pallets for benchmark for {runtime["name"]}') wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" output = os.popen( - f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file} {runtime['bench_flags']}").read() + f"frame-omni-bencher v1 benchmark pallet " \ + f"--no-csv-header " \ + f"--no-storage-info " \ + f"--no-min-squares " \ + f"--no-median-slopes " \ + f"--all " \ + f"--list " \ + f"--runtime={wasm_file} " \ + f"{runtime['bench_flags']}").read() raw_pallets = output.strip().split('\n') all_pallets = set() @@ -221,6 +229,140 @@ def main(): print_and_log('✅ Successful benchmarks of runtimes/pallets:') for runtime, pallets in successful_benchmarks.items(): print_and_log(f'-- {runtime}: {pallets}') + + if args.command == 'bench-old': + runtime_pallets_map = {} + failed_benchmarks = {} + successful_benchmarks = {} + + profile = "release" + + print(f'Provided runtimes: {args.runtime}') + # convert to mapped dict + runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) + runtimesMatrix = {x['name']: x for x in runtimesMatrix} + print(f'Filtered out runtimes: {runtimesMatrix}') + + # loop over remaining runtimes to collect available pallets + for runtime in runtimesMatrix.values(): + os.system(f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']}") + print(f'-- listing pallets for benchmark for {runtime["name"]}') + output = os.popen( + f"target/{profile}/{runtime['old_bin']} " \ + f"benchmark pallet " \ + f"--no-csv-header " \ + f"--no-storage-info " \ + f"--no-min-squares " \ + f"--no-median-slopes " \ + f"--all " \ + f"--list " \ + f"--runtime={runtime['name']} " \ + f"{runtime['bench_flags']}" + ).read() + raw_pallets = output.strip().split('\n') + + all_pallets = set() + for pallet in raw_pallets: + if pallet: + all_pallets.add(pallet.split(',')[0].strip()) + + pallets = list(all_pallets) + print(f'Pallets in {runtime["name"]}: {pallets}') + runtime_pallets_map[runtime['name']] = pallets + + print(f'\n') + + # filter out only the specified pallets from collected runtimes/pallets + if args.pallet: + print(f'Pallets: {args.pallet}') + new_pallets_map = {} + # keep only specified pallets if they exist in the runtime + for runtime in runtime_pallets_map: + if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): + new_pallets_map[runtime] = args.pallet + + runtime_pallets_map = new_pallets_map + + print(f'Filtered out runtimes & pallets: {runtime_pallets_map}\n') + + if not runtime_pallets_map: + if args.pallet and not args.runtime: + print(f"No pallets {args.pallet} found in any runtime") + elif args.runtime and not args.pallet: + print(f"{args.runtime} runtime does not have any pallets") + elif args.runtime and args.pallet: + print(f"No pallets {args.pallet} found in {args.runtime}") + else: + print('No runtimes found') + sys.exit(1) + + for runtime in runtime_pallets_map: + for pallet in runtime_pallets_map[runtime]: + config = runtimesMatrix[runtime] + header_path = os.path.abspath(config['header']) + template = None + + print(f'-- config: {config}') + if runtime == 'dev': + # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) + search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" + print(f'-- running: {search_manifest_path}') + manifest_path = os.popen(search_manifest_path).read() + if not manifest_path: + print(f'-- pallet {pallet} not found in dev runtime') + if args.fail_fast: + print_and_log(f'Error: {pallet} not found in dev runtime') + sys.exit(1) + package_dir = os.path.dirname(manifest_path) + print(f'-- package_dir: {package_dir}') + print(f'-- manifest_path: {manifest_path}') + output_path = os.path.join(package_dir, "src", "weights.rs") + template = config['template'] + else: + default_path = f"./{config['path']}/src/weights" + xcm_path = f"./{config['path']}/src/weights/xcm" + output_path = default_path + if pallet.startswith("pallet_xcm_benchmarks"): + template = config['template'] + output_path = xcm_path + + print(f'-- benchmarking {pallet} in {runtime} into {output_path}') + cmd = f"target/{profile}/{config['old_bin']} benchmark pallet " \ + f"--extrinsic=* " \ + f"--runtime={runtime['name']} " \ + f"--pallet={pallet} " \ + f"--header={header_path} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 " \ + f"--repeat=20 " \ + f"--heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes " \ + f"{config['bench_flags']}" + print(f'-- Running: {cmd} \n') + status = os.system(cmd) + + if status != 0 and args.fail_fast: + print_and_log(f'❌ Failed to benchmark {pallet} in {runtime}') + sys.exit(1) + + # Otherwise collect failed benchmarks and print them at the end + # push failed pallets to failed_benchmarks + if status != 0: + failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] + else: + successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] + + if failed_benchmarks: + print_and_log('❌ Failed benchmarks of runtimes/pallets:') + for runtime, pallets in failed_benchmarks.items(): + print_and_log(f'-- {runtime}: {pallets}') + + if successful_benchmarks: + print_and_log('✅ Successful benchmarks of runtimes/pallets:') + for runtime, pallets in successful_benchmarks.items(): + print_and_log(f'-- {runtime}: {pallets}') elif args.command == 'fmt': command = f"cargo +nightly fmt" diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json index e4e3a2dbe6d1..0560af937311 100644 --- a/.github/workflows/runtimes-matrix.json +++ b/.github/workflows/runtimes-matrix.json @@ -8,6 +8,8 @@ "bench_features": "runtime-benchmarks,riscv", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic,pallet_nomination_pools,pallet_remark,pallet_transaction_storage", "uri": null, + "old_package": "staging-node-cli", + "old_bin": "substrate-node", "is_relay": false }, { @@ -19,6 +21,8 @@ "bench_flags": "", "bench_features": "runtime-benchmarks", "uri": "wss://try-runtime-westend.polkadot.io:443", + "old_package": "polkadot", + "old_bin": "polkadot", "is_relay": true }, { @@ -27,9 +31,11 @@ "path": "polkadot/runtime/rococo", "header": "polkadot/file_header.txt", "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", - "uri": "wss://try-runtime-rococo.polkadot.io:443", "bench_features": "runtime-benchmarks", "bench_flags": "", + "uri": "wss://try-runtime-rococo.polkadot.io:443", + "old_package": "polkadot", + "old_bin": "polkadot", "is_relay": true }, { @@ -41,6 +47,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "", "uri": "wss://westend-asset-hub-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -52,6 +60,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "", "uri": "wss://rococo-asset-hub-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -63,6 +73,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "", "uri": "wss://rococo-bridge-hub-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -74,6 +86,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "", "uri": "wss://westend-bridge-hub-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -84,7 +98,10 @@ "template": "cumulus/templates/xcm-bench-template.hbs", "bench_features": "runtime-benchmarks", "bench_flags": "", - "uri": "wss://westend-collectives-rpc.polkadot.io:443" + "uri": "wss://westend-collectives-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", + "is_relay": false }, { "name": "contracts-rococo", @@ -95,6 +112,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm", "uri": "wss://rococo-contracts-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -106,6 +125,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic", "uri": "wss://rococo-coretime-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -117,6 +138,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic", "uri": "wss://westend-coretime-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -128,6 +151,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none", "uri": null, + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -139,6 +164,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic", "uri": "wss://rococo-people-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false }, { @@ -150,6 +177,8 @@ "bench_features": "runtime-benchmarks", "bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic", "uri": "wss://westend-people-rpc.polkadot.io:443", + "old_package": "polkadot-parachain-bin", + "old_bin": "polkadot-parachain", "is_relay": false } ] From a0265445874d2fe24ebdf4211b863288d3c4eb17 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 08:46:23 +0100 Subject: [PATCH 02/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 31184b08a5c5..17866f7f80bc 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -66,6 +66,35 @@ def setup_logging(): parser_bench.add_argument('--pallet', help='Pallet(s) space separated', nargs='*', default=[]) parser_bench.add_argument('--fail-fast', help='Fail fast on first failed benchmark', action='store_true') + +""" +BENCH OLD +""" + +bench_example = '''**Examples**: + Runs all benchmarks + %(prog)s + + Runs benchmarks for pallet_balances and pallet_multisig for all runtimes which have these pallets. **--quiet** makes it to output nothing to PR but reactions + %(prog)s --pallet pallet_balances pallet_xcm_benchmarks::generic --quiet + + Runs bench for all pallets for westend runtime and fails fast on first failed benchmark + %(prog)s --runtime westend --fail-fast + + Does not output anything and cleans up the previous bot's & author command triggering comments in PR + %(prog)s --runtime westend rococo --pallet pallet_balances pallet_multisig --quiet --clean +''' + +parser_bench_old = subparsers.add_parser('bench-old', help='Runs benchmarks (Old CLI)', epilog=bench_example, formatter_class=argparse.RawDescriptionHelpFormatter) + +for arg, config in common_args.items(): + parser_bench.add_argument(arg, **config) + +parser_bench.add_argument('--runtime', help='Runtime(s) space separated', choices=runtimeNames, nargs='*', default=runtimeNames) +parser_bench.add_argument('--pallet', help='Pallet(s) space separated', nargs='*', default=[]) +parser_bench.add_argument('--fail-fast', help='Fail fast on first failed benchmark', action='store_true') + + """ FMT """ From a465d8b5e2d6b6246b3fc26c29464e3a53f47992 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 09:34:49 +0100 Subject: [PATCH 03/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 17866f7f80bc..ea0a6558d215 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -88,11 +88,11 @@ def setup_logging(): parser_bench_old = subparsers.add_parser('bench-old', help='Runs benchmarks (Old CLI)', epilog=bench_example, formatter_class=argparse.RawDescriptionHelpFormatter) for arg, config in common_args.items(): - parser_bench.add_argument(arg, **config) + parser_bench_old.add_argument(arg, **config) -parser_bench.add_argument('--runtime', help='Runtime(s) space separated', choices=runtimeNames, nargs='*', default=runtimeNames) -parser_bench.add_argument('--pallet', help='Pallet(s) space separated', nargs='*', default=[]) -parser_bench.add_argument('--fail-fast', help='Fail fast on first failed benchmark', action='store_true') +parser_bench_old.add_argument('--runtime', help='Runtime(s) space separated', choices=runtimeNames, nargs='*', default=runtimeNames) +parser_bench_old.add_argument('--pallet', help='Pallet(s) space separated', nargs='*', default=[]) +parser_bench_old.add_argument('--fail-fast', help='Fail fast on first failed benchmark', action='store_true') """ From 58dab0fec61322dbb8959f6a60ab857c07e9607c Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 12:37:11 +0100 Subject: [PATCH 04/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index ea0a6558d215..5eb489f3340f 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -285,7 +285,7 @@ def main(): f"--no-median-slopes " \ f"--all " \ f"--list " \ - f"--runtime={runtime['name']} " \ + f"--runtime=target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm " \ f"{runtime['bench_flags']}" ).read() raw_pallets = output.strip().split('\n') @@ -358,7 +358,7 @@ def main(): print(f'-- benchmarking {pallet} in {runtime} into {output_path}') cmd = f"target/{profile}/{config['old_bin']} benchmark pallet " \ f"--extrinsic=* " \ - f"--runtime={runtime['name']} " \ + f"--runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm " \ f"--pallet={pallet} " \ f"--header={header_path} " \ f"--output={output_path} " \ From 7cc592f987ebf4d30b387c672d1f33bd4889aa99 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 13:41:42 +0100 Subject: [PATCH 05/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 5eb489f3340f..805ff42adcae 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -285,8 +285,7 @@ def main(): f"--no-median-slopes " \ f"--all " \ f"--list " \ - f"--runtime=target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm " \ - f"{runtime['bench_flags']}" + f"--chain={runtime['name']}" ).read() raw_pallets = output.strip().split('\n') @@ -358,7 +357,7 @@ def main(): print(f'-- benchmarking {pallet} in {runtime} into {output_path}') cmd = f"target/{profile}/{config['old_bin']} benchmark pallet " \ f"--extrinsic=* " \ - f"--runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm " \ + f"--chain={config['name']} " \ f"--pallet={pallet} " \ f"--header={header_path} " \ f"--output={output_path} " \ @@ -367,8 +366,7 @@ def main(): f"--repeat=20 " \ f"--heap-pages=4096 " \ f"{f'--template={template} ' if template else ''}" \ - f"--no-storage-info --no-min-squares --no-median-slopes " \ - f"{config['bench_flags']}" + f"--no-storage-info --no-min-squares --no-median-slopes " print(f'-- Running: {cmd} \n') status = os.system(cmd) From a29d714a7e2e867536ddadbdf6b253e467f194bd Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 14:33:12 +0100 Subject: [PATCH 06/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 805ff42adcae..b1a82d9c0f7c 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -144,8 +144,7 @@ def main(): os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}") print(f'-- listing pallets for benchmark for {runtime["name"]}') wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" - output = os.popen( - f"frame-omni-bencher v1 benchmark pallet " \ + list_command = f"frame-omni-bencher v1 benchmark pallet " \ f"--no-csv-header " \ f"--no-storage-info " \ f"--no-min-squares " \ @@ -153,7 +152,9 @@ def main(): f"--all " \ f"--list " \ f"--runtime={wasm_file} " \ - f"{runtime['bench_flags']}").read() + f"{runtime['bench_flags']}" + print(f'-- running: {list_command}') + output = os.popen(list_command).read() raw_pallets = output.strip().split('\n') all_pallets = set() @@ -276,8 +277,7 @@ def main(): for runtime in runtimesMatrix.values(): os.system(f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']}") print(f'-- listing pallets for benchmark for {runtime["name"]}') - output = os.popen( - f"target/{profile}/{runtime['old_bin']} " \ + list_command = f"target/{profile}/{runtime['old_bin']} " \ f"benchmark pallet " \ f"--no-csv-header " \ f"--no-storage-info " \ @@ -286,7 +286,8 @@ def main(): f"--all " \ f"--list " \ f"--chain={runtime['name']}" - ).read() + print(f'-- running: {list_command}') + output = os.popen(list_command).read() raw_pallets = output.strip().split('\n') all_pallets = set() From 97e62a09b6e513aca72385ba8336bbf9f155e03c Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 16:11:41 +0100 Subject: [PATCH 07/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index b1a82d9c0f7c..a7a87a6495e9 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -331,6 +331,8 @@ def main(): header_path = os.path.abspath(config['header']) template = None + chain = runtime['name'] if runtime == 'dev' else f"{config['name']}-dev" + print(f'-- config: {config}') if runtime == 'dev': # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) @@ -358,7 +360,7 @@ def main(): print(f'-- benchmarking {pallet} in {runtime} into {output_path}') cmd = f"target/{profile}/{config['old_bin']} benchmark pallet " \ f"--extrinsic=* " \ - f"--chain={config['name']} " \ + f"--chain={chain} " \ f"--pallet={pallet} " \ f"--header={header_path} " \ f"--output={output_path} " \ From f5ae1843899ecbc3e78fe61fe948cb2b266d3bee Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 17:06:40 +0100 Subject: [PATCH 08/58] [skip ci] add build log --- .github/scripts/cmd/cmd.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index a7a87a6495e9..3937fd37b2ae 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -141,7 +141,9 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}") + build_command = f"forklift cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}" + print(f'-- building "{runtime["name"]}" with `{build_command}`') + os.system(build_command) print(f'-- listing pallets for benchmark for {runtime["name"]}') wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" list_command = f"frame-omni-bencher v1 benchmark pallet " \ @@ -275,7 +277,9 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - os.system(f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']}") + build_command = f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']}" + print(f'-- building {runtime["name"]} with `{build_command}`') + os.system(build_command) print(f'-- listing pallets for benchmark for {runtime["name"]}') list_command = f"target/{profile}/{runtime['old_bin']} " \ f"benchmark pallet " \ From cc5de908d182c109caf248363e7fd54aca0677e2 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 21 Oct 2024 18:26:07 +0100 Subject: [PATCH 09/58] [skip ci] disable forklift add --locked --- .github/scripts/cmd/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 3937fd37b2ae..9534793282bd 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -277,7 +277,7 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - build_command = f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']}" + build_command = f"cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" print(f'-- building {runtime["name"]} with `{build_command}`') os.system(build_command) print(f'-- listing pallets for benchmark for {runtime["name"]}') From 73e18fc194a12dc0fdae584df16ad739d6e08b0d Mon Sep 17 00:00:00 2001 From: Maksym H Date: Tue, 22 Oct 2024 13:08:31 +0100 Subject: [PATCH 10/58] Update cmd.py --- .github/scripts/cmd/cmd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 9534793282bd..7f4c8b1bb4e3 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -280,7 +280,8 @@ def main(): build_command = f"cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" print(f'-- building {runtime["name"]} with `{build_command}`') os.system(build_command) - print(f'-- listing pallets for benchmark for {runtime["name"]}') + chain = runtime['name'] if runtime == 'dev' else f"{runtime['name']}-dev" + print(f'-- listing pallets for benchmark for {chain}') list_command = f"target/{profile}/{runtime['old_bin']} " \ f"benchmark pallet " \ f"--no-csv-header " \ @@ -289,7 +290,7 @@ def main(): f"--no-median-slopes " \ f"--all " \ f"--list " \ - f"--chain={runtime['name']}" + f"--chain={chain}" print(f'-- running: {list_command}') output = os.popen(list_command).read() raw_pallets = output.strip().split('\n') From ed66cff59f16cc7b0bdd52f4fd46b67ae9d4307a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 22 Oct 2024 12:43:23 +0000 Subject: [PATCH 11/58] Update from mordamax running command 'bench-old --runtime westend --pallet pallet_balances --clean' --- .../westend/src/weights/pallet_balances.rs | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 5e91f31920ca..3896e4bc7b88 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -17,25 +17,27 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `8f791b600ec7`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: -// target/production/polkadot +// target/release/polkadot // benchmark // pallet -// --steps=50 -// --repeat=20 // --extrinsic=* +// --chain=westend-dev +// --pallet=pallet_balances +// --header=/__w/polkadot-sdk/polkadot-sdk/polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights // --wasm-execution=compiled +// --steps=50 +// --repeat=20 // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=westend-dev -// --header=./polkadot/file_header.txt -// --output=./polkadot/runtime/westend/src/weights/ +// --no-storage-info +// --no-min-squares +// --no-median-slopes #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,8 +56,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_248_000 picoseconds. - Weight::from_parts(43_872_000, 0) + // Minimum execution time: 70_887_000 picoseconds. + Weight::from_parts(71_857_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +68,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_990_000 picoseconds. - Weight::from_parts(34_693_000, 0) + // Minimum execution time: 54_814_000 picoseconds. + Weight::from_parts(56_549_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +80,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 12_681_000 picoseconds. - Weight::from_parts(13_183_000, 0) + // Minimum execution time: 21_535_000 picoseconds. + Weight::from_parts(22_139_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +92,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_474_000 picoseconds. - Weight::from_parts(18_063_000, 0) + // Minimum execution time: 31_758_000 picoseconds. + Weight::from_parts(32_585_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +104,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 45_699_000 picoseconds. - Weight::from_parts(46_099_000, 0) + // Minimum execution time: 72_436_000 picoseconds. + Weight::from_parts(74_233_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +116,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_453_000 picoseconds. - Weight::from_parts(43_133_000, 0) + // Minimum execution time: 68_854_000 picoseconds. + Weight::from_parts(70_016_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +128,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 15_066_000 picoseconds. - Weight::from_parts(15_605_000, 0) + // Minimum execution time: 25_849_000 picoseconds. + Weight::from_parts(26_506_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +141,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_180_000 picoseconds. - Weight::from_parts(14_598_000, 0) + // Minimum execution time: 24_271_000 picoseconds. + Weight::from_parts(24_694_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 13_221 - .saturating_add(Weight::from_parts(13_422_901, 0).saturating_mul(u.into())) + // Standard Error: 17_741 + .saturating_add(Weight::from_parts(20_863_760, 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, 2603).saturating_mul(u.into())) @@ -152,24 +154,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_130_000 picoseconds. - Weight::from_parts(5_257_000, 0) + // Minimum execution time: 9_409_000 picoseconds. + Weight::from_parts(9_771_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 27_328_000 picoseconds. - Weight::from_parts(27_785_000, 0) + // Minimum execution time: 44_105_000 picoseconds. + Weight::from_parts(44_697_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 17_797_000 picoseconds. - Weight::from_parts(18_103_000, 0) + // Minimum execution time: 30_221_000 picoseconds. + Weight::from_parts(30_697_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } From d500d8eb6fb080c928bb5ba0f40e5b36eef10425 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 22 Oct 2024 13:21:46 +0000 Subject: [PATCH 12/58] Update from mordamax running command 'bench-old --runtime westend --pallet pallet_balances' --- .../westend/src/weights/pallet_balances.rs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 3896e4bc7b88..7a247f32159f 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -19,7 +19,7 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! DATE: 2024-10-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `8f791b600ec7`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `c0e816d87c45`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -56,8 +56,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 70_887_000 picoseconds. - Weight::from_parts(71_857_000, 0) + // Minimum execution time: 69_550_000 picoseconds. + Weight::from_parts(71_124_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +68,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 54_814_000 picoseconds. - Weight::from_parts(56_549_000, 0) + // Minimum execution time: 54_828_000 picoseconds. + Weight::from_parts(55_394_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +80,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 21_535_000 picoseconds. - Weight::from_parts(22_139_000, 0) + // Minimum execution time: 20_873_000 picoseconds. + Weight::from_parts(21_657_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +92,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 31_758_000 picoseconds. - Weight::from_parts(32_585_000, 0) + // Minimum execution time: 32_120_000 picoseconds. + Weight::from_parts(32_896_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +104,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 72_436_000 picoseconds. - Weight::from_parts(74_233_000, 0) + // Minimum execution time: 72_211_000 picoseconds. + Weight::from_parts(73_372_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +116,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 68_854_000 picoseconds. - Weight::from_parts(70_016_000, 0) + // Minimum execution time: 68_754_000 picoseconds. + Weight::from_parts(69_479_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +128,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 25_849_000 picoseconds. - Weight::from_parts(26_506_000, 0) + // Minimum execution time: 25_414_000 picoseconds. + Weight::from_parts(25_994_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,11 +141,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 24_271_000 picoseconds. - Weight::from_parts(24_694_000, 0) + // Minimum execution time: 23_857_000 picoseconds. + Weight::from_parts(24_210_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 17_741 - .saturating_add(Weight::from_parts(20_863_760, 0).saturating_mul(u.into())) + // Standard Error: 11_370 + .saturating_add(Weight::from_parts(19_547_869, 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, 2603).saturating_mul(u.into())) @@ -154,24 +154,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_409_000 picoseconds. - Weight::from_parts(9_771_000, 0) + // Minimum execution time: 9_666_000 picoseconds. + Weight::from_parts(9_918_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 44_105_000 picoseconds. - Weight::from_parts(44_697_000, 0) + // Minimum execution time: 43_778_000 picoseconds. + Weight::from_parts(44_615_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_221_000 picoseconds. - Weight::from_parts(30_697_000, 0) + // Minimum execution time: 29_759_000 picoseconds. + Weight::from_parts(30_312_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } From c616ea19a642fe58f12c996b70ed4a076acd9c7b Mon Sep 17 00:00:00 2001 From: Maksym H Date: Tue, 22 Oct 2024 14:55:10 +0100 Subject: [PATCH 13/58] add machine test to old --- .github/scripts/cmd/cmd.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 7f4c8b1bb4e3..90feac8689df 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -277,10 +277,16 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - build_command = f"cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" + build_command = f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" print(f'-- building {runtime["name"]} with `{build_command}`') os.system(build_command) + chain = runtime['name'] if runtime == 'dev' else f"{runtime['name']}-dev" + + machine_test = f"target/{profile}/{runtime['old_bin']} benchmark machine --chain={chain}" + print(f"Running machine test for `{machine_test}`") + os.system(machine_test) + print(f'-- listing pallets for benchmark for {chain}') list_command = f"target/{profile}/{runtime['old_bin']} " \ f"benchmark pallet " \ From 27ad9771dc438098df33f29eff52b2707ebb2927 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 22 Oct 2024 14:14:23 +0000 Subject: [PATCH 14/58] Update from mordamax running command 'bench --runtime westend --pallet pallet_balances' --- .../westend/src/weights/pallet_balances.rs | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 7a247f32159f..d21155d73000 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -19,15 +19,16 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! DATE: 2024-10-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `c0e816d87c45`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 +//! HOSTNAME: `7615ed9a6a08`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 // Executed Command: -// target/release/polkadot +// frame-omni-bencher +// v1 // benchmark // pallet // --extrinsic=* -// --chain=westend-dev +// --runtime=target/release/wbuild/westend-runtime/westend_runtime.wasm // --pallet=pallet_balances // --header=/__w/polkadot-sdk/polkadot-sdk/polkadot/file_header.txt // --output=./polkadot/runtime/westend/src/weights @@ -56,8 +57,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 69_550_000 picoseconds. - Weight::from_parts(71_124_000, 0) + // Minimum execution time: 74_833_000 picoseconds. + Weight::from_parts(76_179_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +69,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 54_828_000 picoseconds. - Weight::from_parts(55_394_000, 0) + // Minimum execution time: 58_960_000 picoseconds. + Weight::from_parts(60_216_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +81,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_873_000 picoseconds. - Weight::from_parts(21_657_000, 0) + // Minimum execution time: 22_872_000 picoseconds. + Weight::from_parts(23_854_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +93,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 32_120_000 picoseconds. - Weight::from_parts(32_896_000, 0) + // Minimum execution time: 35_382_000 picoseconds. + Weight::from_parts(36_789_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +105,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 72_211_000 picoseconds. - Weight::from_parts(73_372_000, 0) + // Minimum execution time: 77_767_000 picoseconds. + Weight::from_parts(79_697_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +117,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 68_754_000 picoseconds. - Weight::from_parts(69_479_000, 0) + // Minimum execution time: 72_756_000 picoseconds. + Weight::from_parts(74_319_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +129,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 25_414_000 picoseconds. - Weight::from_parts(25_994_000, 0) + // Minimum execution time: 27_787_000 picoseconds. + Weight::from_parts(28_607_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,11 +142,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 23_857_000 picoseconds. - Weight::from_parts(24_210_000, 0) + // Minimum execution time: 25_918_000 picoseconds. + Weight::from_parts(26_526_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_370 - .saturating_add(Weight::from_parts(19_547_869, 0).saturating_mul(u.into())) + // Standard Error: 20_843 + .saturating_add(Weight::from_parts(20_711_759, 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, 2603).saturating_mul(u.into())) @@ -154,24 +155,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_666_000 picoseconds. - Weight::from_parts(9_918_000, 0) + // Minimum execution time: 10_899_000 picoseconds. + Weight::from_parts(11_174_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_778_000 picoseconds. - Weight::from_parts(44_615_000, 0) + // Minimum execution time: 47_408_000 picoseconds. + Weight::from_parts(48_893_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 29_759_000 picoseconds. - Weight::from_parts(30_312_000, 0) + // Minimum execution time: 31_798_000 picoseconds. + Weight::from_parts(32_988_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } From c9d4dc82a6b99783335d78646d513e2e531c85dd Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Tue, 22 Oct 2024 16:00:22 +0000 Subject: [PATCH 15/58] ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=westend --target_dir=polkadot --pallet=pallet_balances --- .../westend/src/weights/pallet_balances.rs | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index d21155d73000..26d567050458 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -19,26 +19,23 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 //! DATE: 2024-10-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `7615ed9a6a08`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: -// frame-omni-bencher -// v1 +// target/production/polkadot // benchmark // pallet -// --extrinsic=* -// --runtime=target/release/wbuild/westend-runtime/westend_runtime.wasm -// --pallet=pallet_balances -// --header=/__w/polkadot-sdk/polkadot-sdk/polkadot/file_header.txt -// --output=./polkadot/runtime/westend/src/weights -// --wasm-execution=compiled // --steps=50 // --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled // --heap-pages=4096 -// --no-storage-info -// --no-min-squares -// --no-median-slopes +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -57,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 74_833_000 picoseconds. - Weight::from_parts(76_179_000, 0) + // Minimum execution time: 49_645_000 picoseconds. + Weight::from_parts(51_277_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -69,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 58_960_000 picoseconds. - Weight::from_parts(60_216_000, 0) + // Minimum execution time: 39_313_000 picoseconds. + Weight::from_parts(40_558_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -81,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 22_872_000 picoseconds. - Weight::from_parts(23_854_000, 0) + // Minimum execution time: 15_177_000 picoseconds. + Weight::from_parts(15_882_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -93,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 35_382_000 picoseconds. - Weight::from_parts(36_789_000, 0) + // Minimum execution time: 22_477_000 picoseconds. + Weight::from_parts(23_225_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -105,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 77_767_000 picoseconds. - Weight::from_parts(79_697_000, 0) + // Minimum execution time: 52_384_000 picoseconds. + Weight::from_parts(53_061_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -117,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 72_756_000 picoseconds. - Weight::from_parts(74_319_000, 0) + // Minimum execution time: 48_330_000 picoseconds. + Weight::from_parts(50_035_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -129,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 27_787_000 picoseconds. - Weight::from_parts(28_607_000, 0) + // Minimum execution time: 17_921_000 picoseconds. + Weight::from_parts(18_545_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -142,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 25_918_000 picoseconds. - Weight::from_parts(26_526_000, 0) + // Minimum execution time: 16_954_000 picoseconds. + Weight::from_parts(17_314_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 20_843 - .saturating_add(Weight::from_parts(20_711_759, 0).saturating_mul(u.into())) + // Standard Error: 11_798 + .saturating_add(Weight::from_parts(15_263_818, 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, 2603).saturating_mul(u.into())) @@ -155,24 +152,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_899_000 picoseconds. - Weight::from_parts(11_174_000, 0) + // Minimum execution time: 6_506_000 picoseconds. + Weight::from_parts(6_701_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_408_000 picoseconds. - Weight::from_parts(48_893_000, 0) + // Minimum execution time: 30_712_000 picoseconds. + Weight::from_parts(31_980_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 31_798_000 picoseconds. - Weight::from_parts(32_988_000, 0) + // Minimum execution time: 20_958_000 picoseconds. + Weight::from_parts(22_074_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } From 237128be2f4f76241f8940feb49cdfbdcd792a5e Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Tue, 22 Oct 2024 16:44:22 +0000 Subject: [PATCH 16/58] ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=westend --target_dir=polkadot --pallet=pallet_balances --machine=y --- .../westend/src/weights/pallet_balances.rs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 26d567050458..458b1e0decb1 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 49_645_000 picoseconds. - Weight::from_parts(51_277_000, 0) + // Minimum execution time: 49_120_000 picoseconds. + Weight::from_parts(50_674_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 39_313_000 picoseconds. - Weight::from_parts(40_558_000, 0) + // Minimum execution time: 39_062_000 picoseconds. + Weight::from_parts(41_071_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 15_177_000 picoseconds. - Weight::from_parts(15_882_000, 0) + // Minimum execution time: 15_009_000 picoseconds. + Weight::from_parts(15_776_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 22_477_000 picoseconds. - Weight::from_parts(23_225_000, 0) + // Minimum execution time: 22_568_000 picoseconds. + Weight::from_parts(23_393_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 52_384_000 picoseconds. - Weight::from_parts(53_061_000, 0) + // Minimum execution time: 52_067_000 picoseconds. + Weight::from_parts(54_267_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 48_330_000 picoseconds. - Weight::from_parts(50_035_000, 0) + // Minimum execution time: 48_358_000 picoseconds. + Weight::from_parts(49_769_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_921_000 picoseconds. - Weight::from_parts(18_545_000, 0) + // Minimum execution time: 18_147_000 picoseconds. + Weight::from_parts(18_594_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_954_000 picoseconds. - Weight::from_parts(17_314_000, 0) + // Minimum execution time: 17_010_000 picoseconds. + Weight::from_parts(17_499_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_798 - .saturating_add(Weight::from_parts(15_263_818, 0).saturating_mul(u.into())) + // Standard Error: 11_915 + .saturating_add(Weight::from_parts(15_286_797, 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, 2603).saturating_mul(u.into())) @@ -152,24 +152,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_506_000 picoseconds. - Weight::from_parts(6_701_000, 0) + // Minimum execution time: 6_581_000 picoseconds. + Weight::from_parts(6_823_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_712_000 picoseconds. - Weight::from_parts(31_980_000, 0) + // Minimum execution time: 30_903_000 picoseconds. + Weight::from_parts(32_022_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 20_958_000 picoseconds. - Weight::from_parts(22_074_000, 0) + // Minimum execution time: 20_604_000 picoseconds. + Weight::from_parts(21_606_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } From 7ba35b4f60fe1b248f59afb7747422a4696a8448 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Fri, 25 Oct 2024 09:43:38 +0100 Subject: [PATCH 17/58] disable docker in docker --- .github/env | 1 + .github/workflows/cmd.yml | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/env b/.github/env index bb61e1f4cd99..9528cfd8ff10 100644 --- a/.github/env +++ b/.github/env @@ -1 +1,2 @@ IMAGE="docker.io/paritytech/ci-unified:bullseye-1.81.0-2024-09-11-v202409111034" +RUST_NIGHTLY_VERSION="2024-09-11" diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 525ab0c0fc23..34c209670da2 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -284,9 +284,24 @@ jobs: JOB_NAME: "cmd" runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 4320 # 72 hours -> 3 days; as it could take a long time to run all the runtimes/pallets - container: - image: ${{ needs.set-image.outputs.IMAGE }} + # container: ## Disabling DockerInDocker for now + # image: ${{ needs.set-image.outputs.IMAGE }} steps: + - name: Install updates and protobuf-compiler + run: | + sudo apt update && sudo apt install --assume-yes \ + openssl pkg-config g++ make cmake protobuf-compiler curl libssl-dev libclang-dev libudev-dev git jq + + - name: Set rust version via common env file + run: cat .github/env >> $GITHUB_ENV + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + targets: "wasm32-unknown-unknown,x86_64-unknown-linux-musl" + components: "rust-src rustfmt clippy" + toolchain: "nightly-${{env.RUST_NIGHTLY_VERSION}}" + - name: Get command uses: actions-ecosystem/action-regex-match@v2 id: get-pr-comment From 2443f9218a645279af9ac8c3103cb076e378339c Mon Sep 17 00:00:00 2001 From: Maksym H Date: Fri, 25 Oct 2024 12:10:40 +0100 Subject: [PATCH 18/58] move checkout earlier --- .github/workflows/cmd.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 34c209670da2..3c95c10fc10e 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -284,16 +284,21 @@ jobs: JOB_NAME: "cmd" runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 4320 # 72 hours -> 3 days; as it could take a long time to run all the runtimes/pallets - # container: ## Disabling DockerInDocker for now - # image: ${{ needs.set-image.outputs.IMAGE }} steps: - name: Install updates and protobuf-compiler run: | sudo apt update && sudo apt install --assume-yes \ openssl pkg-config g++ make cmake protobuf-compiler curl libssl-dev libclang-dev libudev-dev git jq + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ needs.get-pr-branch.outputs.repo }} + ref: ${{ needs.get-pr-branch.outputs.pr-branch }} + - name: Set rust version via common env file - run: cat .github/env >> $GITHUB_ENV + run: | + run: cat .github/env >> $GITHUB_ENV - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -356,12 +361,6 @@ jobs: body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has started 🚀 [See logs here](${job_url})` }) - - name: Checkout - uses: actions/checkout@v4 - with: - repository: ${{ needs.get-pr-branch.outputs.repo }} - ref: ${{ needs.get-pr-branch.outputs.pr-branch }} - - name: Install dependencies for bench if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') run: | @@ -516,4 +515,4 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, content: '+1' - }) + }) \ No newline at end of file From 10ea752d41589a51a10f367a96e46995ad457ff2 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Fri, 25 Oct 2024 12:15:06 +0100 Subject: [PATCH 19/58] Update cmd.yml --- .github/workflows/cmd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 3c95c10fc10e..d1d68adce612 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -298,7 +298,7 @@ jobs: - name: Set rust version via common env file run: | - run: cat .github/env >> $GITHUB_ENV + cat .github/env >> $GITHUB_ENV - name: Install Rust uses: dtolnay/rust-toolchain@master From 695b8ccca0b06d9c79b857639aa65784e7d5c618 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Fri, 25 Oct 2024 16:08:43 +0100 Subject: [PATCH 20/58] Update cmd.yml --- .github/workflows/cmd.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index d1d68adce612..f6017628028c 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -288,7 +288,7 @@ jobs: - name: Install updates and protobuf-compiler run: | sudo apt update && sudo apt install --assume-yes \ - openssl pkg-config g++ make cmake protobuf-compiler curl libssl-dev libclang-dev libudev-dev git jq + openssl pkg-config g++ make cmake protobuf-compiler curl libssl-dev libclang-dev libudev-dev git jq python3 python3-pip - name: Checkout uses: actions/checkout@v4 @@ -378,6 +378,7 @@ jobs: # Fixes "detected dubious ownership" error in the ci git config --global --add safe.directory '*' git remote -v + cat /proc/cpuinfo python3 -m pip install -r .github/scripts/generate-prdoc.requirements.txt python3 .github/scripts/cmd/cmd.py $CMD $PR_ARG git status From 3ccada6169cc44a4f5ec09f19a1a8c74d23f7f8d Mon Sep 17 00:00:00 2001 From: Maksym H Date: Fri, 25 Oct 2024 19:32:40 +0100 Subject: [PATCH 21/58] add cache and remove forklift --- .github/scripts/cmd/cmd.py | 4 ++-- .github/workflows/cmd.yml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 90feac8689df..a027d1edf54b 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -141,7 +141,7 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - build_command = f"forklift cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}" + build_command = f"cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}" print(f'-- building "{runtime["name"]}" with `{build_command}`') os.system(build_command) print(f'-- listing pallets for benchmark for {runtime["name"]}') @@ -277,7 +277,7 @@ def main(): # loop over remaining runtimes to collect available pallets for runtime in runtimesMatrix.values(): - build_command = f"forklift cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" + build_command = f"cargo build -p {runtime['old_package']} --profile {profile} --features={runtime['bench_features']} --locked" print(f'-- building {runtime["name"]} with `{build_command}`') os.system(build_command) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index f6017628028c..205ed3ea478f 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -360,6 +360,12 @@ jobs: repo: context.repo.repo, body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has started 🚀 [See logs here](${job_url})` }) + + - name: Cache Rust dependencies + if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') + uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + with: + cache-on-failure: true - name: Install dependencies for bench if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') From f704741a1ace69afafb3ac5aeadf1338ce63a6da Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:04:17 +0000 Subject: [PATCH 22/58] oldlinux --- .github/workflows/cmd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 205ed3ea478f..ea526ebcd8fe 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -231,7 +231,7 @@ jobs: fi if [[ $BODY == "/cmd bench"* ]]; then - echo "RUNNER=parity-weights" >> $GITHUB_OUTPUT + echo "RUNNER=parity-oldlinux" >> $GITHUB_OUTPUT elif [[ $BODY == "/cmd update-ui"* ]]; then echo "RUNNER=parity-large" >> $GITHUB_OUTPUT else @@ -522,4 +522,4 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, content: '+1' - }) \ No newline at end of file + }) From 6094201a9ad5ce875a948a744bcb7db976960126 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Tue, 29 Oct 2024 17:21:13 +0000 Subject: [PATCH 23/58] cmd-bot-vm --- .github/workflows/cmd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index ea526ebcd8fe..37c9cbe3139e 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -231,7 +231,7 @@ jobs: fi if [[ $BODY == "/cmd bench"* ]]; then - echo "RUNNER=parity-oldlinux" >> $GITHUB_OUTPUT + echo "RUNNER=cmd-bot-vm" >> $GITHUB_OUTPUT elif [[ $BODY == "/cmd update-ui"* ]]; then echo "RUNNER=parity-large" >> $GITHUB_OUTPUT else From 872a0209bde780c2906d0815cbcdae4aa62b1b51 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Tue, 29 Oct 2024 17:23:01 +0000 Subject: [PATCH 24/58] cmd-bot-vm - production --- .github/scripts/cmd/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index a027d1edf54b..5715a7fbfeda 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -131,7 +131,7 @@ def main(): failed_benchmarks = {} successful_benchmarks = {} - profile = "release" + profile = "production" print(f'Provided runtimes: {args.runtime}') # convert to mapped dict From 4639b82428367cc27f5c448997dd1137880b925b Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 22 Oct 2024 18:02:01 +0200 Subject: [PATCH 25/58] [pallet-revive] fix fixture build path (#6174) Co-authored-by: GitHub Action Co-authored-by: Cyrill Leutwiler --- prdoc/pr_6174.prdoc | 9 ++++++++ substrate/frame/revive/fixtures/build.rs | 25 ++++++++++++++-------- substrate/frame/revive/fixtures/src/lib.rs | 23 ++++++++++---------- substrate/frame/revive/src/evm/runtime.rs | 1 + 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 prdoc/pr_6174.prdoc diff --git a/prdoc/pr_6174.prdoc b/prdoc/pr_6174.prdoc new file mode 100644 index 000000000000..8aa1c25012b1 --- /dev/null +++ b/prdoc/pr_6174.prdoc @@ -0,0 +1,9 @@ +title: '[pallet-revive] fix fixture build path' +doc: +- audience: Runtime Dev + description: "Fix fixture build path" +crates: +- name: pallet-revive-fixtures + bump: patch +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index cb4b76408141..ee7db4203cc9 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -182,18 +182,14 @@ mod build { pub fn run() -> Result<()> { let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); let contracts_dir = fixtures_dir.join("contracts"); - let uapi_dir = fixtures_dir.parent().expect("uapi dir exits; qed").join("uapi"); - let ws_dir: PathBuf = env::var("CARGO_WORKSPACE_ROOT_DIR")?.into(); - let out_dir: PathBuf = ws_dir.join("target").join("pallet-revive-fixtures"); - - // create out_dir if it does not exist - if !out_dir.exists() { - fs::create_dir_all(&out_dir)?; - } + let out_dir: PathBuf = env::var("OUT_DIR")?.into(); // the fixtures have a dependency on the uapi crate println!("cargo::rerun-if-changed={}", fixtures_dir.display()); - println!("cargo::rerun-if-changed={}", uapi_dir.display()); + let uapi_dir = fixtures_dir.parent().expect("parent dir exits; qed").join("uapi"); + if uapi_dir.exists() { + println!("cargo::rerun-if-changed={}", uapi_dir.display()); + } let entries = collect_entries(&contracts_dir); if entries.is_empty() { @@ -207,6 +203,17 @@ mod build { invoke_build(tmp_dir_path)?; write_output(tmp_dir_path, &out_dir, entries)?; + + #[cfg(unix)] + if let Ok(symlink_dir) = env::var("CARGO_WORKSPACE_ROOT_DIR") { + let symlink_dir: PathBuf = symlink_dir.into(); + let symlink_dir: PathBuf = symlink_dir.join("target").join("pallet-revive-fixtures"); + if symlink_dir.is_symlink() { + fs::remove_file(&symlink_dir)? + } + std::os::unix::fs::symlink(&out_dir, &symlink_dir)?; + } + Ok(()) } } diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index eacd63b97e57..5548dca66d07 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -22,11 +22,8 @@ extern crate alloc; /// Load a given wasm module and returns a wasm binary contents along with it's hash. #[cfg(feature = "std")] pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { - let ws_dir: std::path::PathBuf = env!("CARGO_WORKSPACE_ROOT_DIR").into(); - let fixture_path = ws_dir - .join("target") - .join("pallet-revive-fixtures") - .join(format!("{fixture_name}.polkavm")); + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); log::debug!("Loading fixture from {fixture_path:?}"); let binary = std::fs::read(fixture_path)?; let code_hash = sp_io::hashing::keccak_256(&binary); @@ -43,12 +40,7 @@ pub mod bench { #[cfg(feature = "riscv")] macro_rules! fixture { ($name: literal) => { - include_bytes!(concat!( - env!("CARGO_WORKSPACE_ROOT_DIR"), - "/target/pallet-revive-fixtures/", - $name, - ".polkavm" - )) + include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm")) }; } #[cfg(not(feature = "riscv"))] @@ -71,3 +63,12 @@ pub mod bench { dummy } } + +#[cfg(test)] +mod test { + #[test] + fn out_dir_should_have_compiled_mocks() { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + assert!(out_dir.join("dummy.polkavm").exists()); + } +} diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 58110bcf186f..e4340b27a187 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -387,6 +387,7 @@ pub trait EthExtra { } } +#[cfg(feature = "riscv")] #[cfg(test)] mod test { use super::*; From adf4b5e4b9ab064022efe31d0b7afcedd6296577 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:35:15 +0200 Subject: [PATCH 26/58] `fatxpool`: `LocalTransactionPool` implemented (#6104) [`LocalTransactionPool` trait](https://github.com/paritytech/polkadot-sdk/blob/d5b96e9e7f24adc1799f8e426c5cb69b4f2dbf8a/substrate/client/transaction-pool/api/src/lib.rs#L408-L426) is now implemented for `ForkAwareTransactionPool`. Closes #5493 --- prdoc/pr_6104.prdoc | 10 ++++ .../client/transaction-pool/benches/basics.rs | 9 ++++ .../client/transaction-pool/src/common/api.rs | 37 +++++-------- .../transaction-pool/src/common/tests.rs | 9 ++++ .../fork_aware_txpool/fork_aware_txpool.rs | 20 +++---- .../src/fork_aware_txpool/tx_mem_pool.rs | 18 +++---- .../src/fork_aware_txpool/view.rs | 53 +++++++++++++++++-- .../src/fork_aware_txpool/view_store.rs | 40 +++++++++++--- .../client/transaction-pool/src/graph/pool.rs | 13 ++++- .../runtime/transaction-pool/src/lib.rs | 9 ++++ 10 files changed, 162 insertions(+), 56 deletions(-) create mode 100644 prdoc/pr_6104.prdoc diff --git a/prdoc/pr_6104.prdoc b/prdoc/pr_6104.prdoc new file mode 100644 index 000000000000..2b62a68c9f0e --- /dev/null +++ b/prdoc/pr_6104.prdoc @@ -0,0 +1,10 @@ +title: "LocalTransactionPool implemented for fork aware transaction pool" + +doc: + - audience: Node Dev + description: | + LocalTransactionPool trait is implemented for fork aware transaction pool. + +crates: + - name: sc-transaction-pool + bump: minor diff --git a/substrate/client/transaction-pool/benches/basics.rs b/substrate/client/transaction-pool/benches/basics.rs index 2db34bc3f32f..0d8c1cbba9b4 100644 --- a/substrate/client/transaction-pool/benches/basics.rs +++ b/substrate/client/transaction-pool/benches/basics.rs @@ -91,6 +91,15 @@ impl ChainApi for TestApi { }))) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> sc_transaction_pool_api::error::Result { + unimplemented!(); + } + fn block_id_to_number( &self, at: &BlockId, diff --git a/substrate/client/transaction-pool/src/common/api.rs b/substrate/client/transaction-pool/src/common/api.rs index a5185ba606ef..e16c0f2efa51 100644 --- a/substrate/client/transaction-pool/src/common/api.rs +++ b/substrate/client/transaction-pool/src/common/api.rs @@ -162,6 +162,18 @@ where .boxed() } + /// Validates a transaction by calling into the runtime. + /// + /// Same as `validate_transaction` but blocks the current thread when performing validation. + fn validate_transaction_blocking( + &self, + at: Block::Hash, + source: TransactionSource, + uxt: graph::ExtrinsicFor, + ) -> error::Result { + validate_transaction_blocking(&*self.client, at, source, uxt) + } + fn block_id_to_number( &self, at: &BlockId, @@ -272,28 +284,3 @@ where result } - -impl FullChainApi -where - Block: BlockT, - Client: ProvideRuntimeApi - + BlockBackend - + BlockIdTo - + HeaderBackend - + HeaderMetadata, - Client: Send + Sync + 'static, - Client::Api: TaggedTransactionQueue, -{ - /// Validates a transaction by calling into the runtime, same as - /// `validate_transaction` but blocks the current thread when performing - /// validation. Only implemented for `FullChainApi` since we can call into - /// the runtime locally. - pub fn validate_transaction_blocking( - &self, - at: Block::Hash, - source: TransactionSource, - uxt: graph::ExtrinsicFor, - ) -> error::Result { - validate_transaction_blocking(&*self.client, at, source, uxt) - } -} diff --git a/substrate/client/transaction-pool/src/common/tests.rs b/substrate/client/transaction-pool/src/common/tests.rs index 1cbabf8b5fde..b00cf5fbfede 100644 --- a/substrate/client/transaction-pool/src/common/tests.rs +++ b/substrate/client/transaction-pool/src/common/tests.rs @@ -156,6 +156,15 @@ impl ChainApi for TestApi { futures::future::ready(Ok(res)) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> error::Result { + unimplemented!(); + } + /// Returns a block number given the block id. fn block_id_to_number( &self, diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs index 11e30bef7ea2..7e72b44adf38 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs @@ -599,7 +599,7 @@ where log::debug!(target: LOG_TARGET, "fatp::submit_at count:{} views:{}", xts.len(), self.active_views_count()); log_xt_trace!(target: LOG_TARGET, xts.iter().map(|xt| self.tx_hash(xt)), "[{:?}] fatp::submit_at"); let xts = xts.into_iter().map(Arc::from).collect::>(); - let mempool_result = self.mempool.extend_unwatched(source, xts.clone()); + let mempool_result = self.mempool.extend_unwatched(source, &xts); if view_store.is_empty() { return future::ready(Ok(mempool_result)).boxed() @@ -838,16 +838,16 @@ where fn submit_local( &self, _at: Block::Hash, - _xt: sc_transaction_pool_api::LocalTransactionFor, + xt: sc_transaction_pool_api::LocalTransactionFor, ) -> Result { - //todo [#5493] - //looks like view_store / view needs non async submit_local method ?. - let e = Err(sc_transaction_pool_api::error::Error::Unactionable.into()); - log::warn!( - target: LOG_TARGET, - "LocalTransactionPool::submit_local is not implemented for ForkAwareTxPool, returning error: {e:?}", - ); - e + log::debug!(target: LOG_TARGET, "fatp::submit_local views:{}", self.active_views_count()); + let xt = Arc::from(xt); + let result = self + .mempool + .extend_unwatched(TransactionSource::Local, &[xt.clone()]) + .remove(0)?; + + self.view_store.submit_local(xt).or_else(|_| Ok(result)) } } diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs index 86ea27dcf451..989c7e8ef356 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs @@ -237,11 +237,11 @@ where pub(super) fn extend_unwatched( &self, source: TransactionSource, - xts: Vec>, + xts: &[ExtrinsicFor], ) -> Vec, ChainApi::Error>> { let mut transactions = self.transactions.write(); let result = xts - .into_iter() + .iter() .map(|xt| { let hash = self.api.hash_and_length(&xt).0; self.try_insert( @@ -437,7 +437,7 @@ mod tx_mem_pool_tests { let xts = (0..max + 1).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().take(max).all(Result::is_ok)); assert!(matches!( results.into_iter().last().unwrap().unwrap_err(), @@ -455,7 +455,7 @@ mod tx_mem_pool_tests { let mut xts = (0..max - 1).map(|x| Arc::from(uxt(x as _))).collect::>(); xts.push(xts.iter().last().unwrap().clone()); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().take(max - 1).all(Result::is_ok)); assert!(matches!( results.into_iter().last().unwrap().unwrap_err(), @@ -471,7 +471,7 @@ mod tx_mem_pool_tests { let xts = (0..max).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().all(Result::is_ok)); let xt = Arc::from(uxt(98)); @@ -481,7 +481,7 @@ mod tx_mem_pool_tests { sc_transaction_pool_api::error::Error::ImmediatelyDropped )); let xt = Arc::from(uxt(99)); - let mut result = mempool.extend_unwatched(TransactionSource::External, vec![xt]); + let mut result = mempool.extend_unwatched(TransactionSource::External, &[xt]); assert!(matches!( result.pop().unwrap().unwrap_err(), sc_transaction_pool_api::error::Error::ImmediatelyDropped @@ -498,7 +498,7 @@ mod tx_mem_pool_tests { let xt0 = xts.iter().last().unwrap().clone(); let xt1 = xts.iter().next().unwrap().clone(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().all(Result::is_ok)); let result = mempool.push_watched(TransactionSource::External, xt0); @@ -506,7 +506,7 @@ mod tx_mem_pool_tests { result.unwrap_err(), sc_transaction_pool_api::error::Error::AlreadyImported(_) )); - let mut result = mempool.extend_unwatched(TransactionSource::External, vec![xt1]); + let mut result = mempool.extend_unwatched(TransactionSource::External, &[xt1]); assert!(matches!( result.pop().unwrap().unwrap_err(), sc_transaction_pool_api::error::Error::AlreadyImported(_) @@ -521,7 +521,7 @@ mod tx_mem_pool_tests { let xts0 = (0..10).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts0); + let results = mempool.extend_unwatched(TransactionSource::External, &xts0); assert!(results.iter().all(Result::is_ok)); let xts1 = (0..5).map(|x| Arc::from(uxt(2 * x))).collect::>(); diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs index fd5bfa8312c0..99095d88cb0a 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs @@ -33,10 +33,11 @@ use crate::{ LOG_TARGET, }; use parking_lot::Mutex; -use sc_transaction_pool_api::{PoolStatus, TransactionSource}; +use sc_transaction_pool_api::{error::Error as TxPoolError, PoolStatus, TransactionSource}; use sp_blockchain::HashAndNumber; use sp_runtime::{ - traits::Block as BlockT, transaction_validity::TransactionValidityError, SaturatedConversion, + generic::BlockId, traits::Block as BlockT, transaction_validity::TransactionValidityError, + SaturatedConversion, }; use std::{collections::HashMap, sync::Arc, time::Instant}; @@ -178,6 +179,50 @@ where self.pool.submit_and_watch(&self.at, source, xt).await } + /// Synchronously imports single unvalidated extrinsics into the view. + pub(super) fn submit_local( + &self, + xt: ExtrinsicFor, + ) -> Result, ChainApi::Error> { + let (hash, length) = self.pool.validated_pool().api().hash_and_length(&xt); + log::trace!(target: LOG_TARGET, "[{:?}] view::submit_local at:{}", hash, self.at.hash); + + let validity = self + .pool + .validated_pool() + .api() + .validate_transaction_blocking( + self.at.hash, + TransactionSource::Local, + Arc::from(xt.clone()), + )? + .map_err(|e| { + match e { + TransactionValidityError::Invalid(i) => TxPoolError::InvalidTransaction(i), + TransactionValidityError::Unknown(u) => TxPoolError::UnknownTransaction(u), + } + .into() + })?; + + let block_number = self + .pool + .validated_pool() + .api() + .block_id_to_number(&BlockId::hash(self.at.hash))? + .ok_or_else(|| TxPoolError::InvalidBlockId(format!("{:?}", self.at.hash)))?; + + let validated = ValidatedTransaction::valid_at( + block_number.saturated_into::(), + hash, + TransactionSource::Local, + Arc::from(xt), + length, + validity, + ); + + self.pool.validated_pool().submit(vec![validated]).remove(0) + } + /// Status of the pool associated with the view. pub(super) fn status(&self) -> PoolStatus { self.pool.validated_pool().status() @@ -243,9 +288,7 @@ where let validation_result = (api.validate_transaction(self.at.hash, tx.source, tx.data.clone()).await, tx.hash, tx); validation_results.push(validation_result); } else { - { - self.revalidation_worker_channels.lock().as_mut().map(|ch| ch.remove_sender()); - } + self.revalidation_worker_channels.lock().as_mut().map(|ch| ch.remove_sender()); should_break = true; } } => {} diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs index 953d6d860338..413fca223242 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs @@ -29,6 +29,7 @@ use crate::{ ReadyIteratorFor, LOG_TARGET, }; use futures::prelude::*; +use itertools::Itertools; use parking_lot::RwLock; use sc_transaction_pool_api::{error::Error as PoolError, PoolStatus, TransactionSource}; use sp_blockchain::TreeRoute; @@ -110,6 +111,37 @@ where HashMap::<_, _>::from_iter(results.into_iter()) } + /// Synchronously imports single unverified extrinsics into every active view. + pub(super) fn submit_local( + &self, + xt: ExtrinsicFor, + ) -> Result, ChainApi::Error> { + let active_views = self + .active_views + .read() + .iter() + .map(|(_, view)| view.clone()) + .collect::>(); + + let tx_hash = self.api.hash_and_length(&xt).0; + + let result = active_views + .iter() + .map(|view| { + self.dropped_stream_controller + .add_initial_views(std::iter::once(tx_hash), view.at.hash); + view.submit_local(xt.clone()) + }) + .find_or_first(Result::is_ok); + + if let Some(Err(err)) = result { + log::trace!(target: LOG_TARGET, "[{:?}] submit_local: err: {}", tx_hash, err); + return Err(err) + }; + + Ok(tx_hash) + } + /// Import a single extrinsic and starts to watch its progress in the pool. /// /// The extrinsic is imported to every view, and the individual streams providing the progress @@ -155,12 +187,8 @@ where let maybe_error = futures::future::join_all(submit_and_watch_futures) .await .into_iter() - .reduce(|mut r, v| { - if r.is_err() && v.is_ok() { - r = v; - } - r - }); + .find_or_first(Result::is_ok); + if let Some(Err(err)) = maybe_error { log::trace!(target: LOG_TARGET, "[{:?}] submit_and_watch: err: {}", tx_hash, err); return Err((err, Some(external_watcher))); diff --git a/substrate/client/transaction-pool/src/graph/pool.rs b/substrate/client/transaction-pool/src/graph/pool.rs index 6d08a0f0b93c..2dd8de352c6b 100644 --- a/substrate/client/transaction-pool/src/graph/pool.rs +++ b/substrate/client/transaction-pool/src/graph/pool.rs @@ -73,7 +73,7 @@ pub trait ChainApi: Send + Sync { + Send + 'static; - /// Verify extrinsic at given block. + /// Asynchronously verify extrinsic at given block. fn validate_transaction( &self, at: ::Hash, @@ -81,6 +81,17 @@ pub trait ChainApi: Send + Sync { uxt: ExtrinsicFor, ) -> Self::ValidationFuture; + /// Synchronously verify given extrinsic at given block. + /// + /// Validates a transaction by calling into the runtime. Same as `validate_transaction` but + /// blocks the current thread when performing validation. + fn validate_transaction_blocking( + &self, + at: ::Hash, + source: TransactionSource, + uxt: ExtrinsicFor, + ) -> Result; + /// Returns a block number given the block id. fn block_id_to_number( &self, diff --git a/substrate/test-utils/runtime/transaction-pool/src/lib.rs b/substrate/test-utils/runtime/transaction-pool/src/lib.rs index 2d19dbfb6d49..6a4f38f63e82 100644 --- a/substrate/test-utils/runtime/transaction-pool/src/lib.rs +++ b/substrate/test-utils/runtime/transaction-pool/src/lib.rs @@ -450,6 +450,15 @@ impl ChainApi for TestApi { ready(Ok(Ok(validity))) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> Result { + unimplemented!(); + } + fn block_id_to_number( &self, at: &BlockId, From 1360dba0afa42627d688a73d1f7e52c9e04912e5 Mon Sep 17 00:00:00 2001 From: tmpolaczyk <44604217+tmpolaczyk@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:23:29 +0200 Subject: [PATCH 27/58] Use bool::then instead of then_some with function calls (#6156) I noticed that hardware benchmarks are being run even though we pass the --no-hardware-benchmarks cli flag. After some debugging, the cause is an incorrect usage of the `then_some` method. From [std docs](https://doc.rust-lang.org/std/primitive.bool.html#method.then_some): > Arguments passed to then_some are eagerly evaluated; if you are passing the result of a function call, it is recommended to use [then](https://doc.rust-lang.org/std/primitive.bool.html#method.then), which is lazily evaluated. ```rust let mut a = 0; let mut function_with_side_effects = || { a += 1; }; true.then_some(function_with_side_effects()); false.then_some(function_with_side_effects()); // `a` is incremented twice because the value passed to `then_some` is // evaluated eagerly. assert_eq!(a, 2); ``` This PR fixes all the similar usages of the `then_some` method across the codebase. polkadot address: 138eUqXvUYT3o4GdbnWQfGRzM8yDWh5Q2eFrFULL7RAXzdWD --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Shawn Tabrizi Co-authored-by: Oliver Tale-Yazdi --- cumulus/polkadot-omni-node/lib/src/command.rs | 16 +++++++------ polkadot/cli/src/command.rs | 10 ++++---- .../src/collator_side/validators_buffer.rs | 2 +- prdoc/pr_6156.prdoc | 23 +++++++++++++++++++ substrate/bin/node/cli/src/service.rs | 10 ++++---- substrate/client/network/src/discovery.rs | 2 +- .../client/network/sync/src/strategy/state.rs | 2 +- .../client/network/sync/src/strategy/warp.rs | 2 +- .../frame/contracts/proc-macro/src/lib.rs | 2 +- .../src/pallet/expand/pallet_struct.rs | 2 +- substrate/frame/support/src/migrations.rs | 4 ++-- templates/parachain/node/src/command.rs | 16 +++++++------ 12 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 prdoc/pr_6156.prdoc diff --git a/cumulus/polkadot-omni-node/lib/src/command.rs b/cumulus/polkadot-omni-node/lib/src/command.rs index 350dcfee1cdb..fe935a03cc95 100644 --- a/cumulus/polkadot-omni-node/lib/src/command.rs +++ b/cumulus/polkadot-omni-node/lib/src/command.rs @@ -266,13 +266,15 @@ pub fn run(cmd_config: RunConfig) -> Result<() } let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench( - Some(database_path), - &SUBSTRATE_REFERENCE_HARDWARE, - ) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) + }) + }) .flatten(); let parachain_account = diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index d124c8fb7eb7..7c904e6658e7 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -203,10 +203,12 @@ where runner.run_node_until_exit(move |config| async move { let hwbench = (!cli.run.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(&database_path); + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) + }) + }) .flatten(); let database_source = config.database.clone(); diff --git a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs index fbb3ff4328a5..35202fc96299 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs @@ -110,7 +110,7 @@ impl ValidatorGroupsBuffer { .validators .iter() .enumerate() - .filter_map(|(idx, authority_id)| bits[idx].then_some(authority_id.clone())) + .filter_map(|(idx, authority_id)| bits[idx].then(|| authority_id.clone())) .collect(); if let Some(last_group) = self.group_infos.iter().last() { diff --git a/prdoc/pr_6156.prdoc b/prdoc/pr_6156.prdoc new file mode 100644 index 000000000000..d20324a83a2f --- /dev/null +++ b/prdoc/pr_6156.prdoc @@ -0,0 +1,23 @@ +title: "Use bool::then instead of then_some with function calls" +doc: +- audience: Node Dev + description: |- + Fix misusage of `bool::then_some`. + +crates: +- name: polkadot-omni-node-lib + bump: patch +- name: polkadot-cli + bump: patch +- name: polkadot-collator-protocol + bump: patch +- name: sc-network + bump: patch +- name: sc-network-sync + bump: patch +- name: pallet-contracts-proc-macro + bump: patch +- name: frame-support-procedural + bump: patch +- name: frame-support + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index d71f1304caf5..7b166f94bcc8 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -420,10 +420,12 @@ pub fn new_full_base::Hash>>( let enable_offchain_worker = config.offchain_worker.enabled; let hwbench = (!disable_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(&database_path); + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) + }) + }) .flatten(); let sc_service::PartialComponents { diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index 86c66c22701c..49e0797c126c 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -648,7 +648,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { let mut list: LinkedHashSet<_> = self .permanent_addresses .iter() - .filter_map(|(p, a)| (*p == peer_id).then_some(a.clone())) + .filter_map(|(p, a)| (*p == peer_id).then(|| a.clone())) .collect(); if let Some(ephemeral_addresses) = self.ephemeral_addresses.get(&peer_id) { diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index a04ab8be4fea..d69ab3e2d535 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -173,7 +173,7 @@ impl StateStrategy { peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)> { - is_best.then_some({ + is_best.then(|| { let best_number = *announce.header.number(); let best_hash = announce.header.hash(); if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index cce6a93caf43..0c71dd3c6aee 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -301,7 +301,7 @@ where peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)> { - is_best.then_some({ + is_best.then(|| { let best_number = *announce.header.number(); let best_hash = announce.header.hash(); if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index 84ea7de00a2f..4aba1d24dbd5 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -522,7 +522,7 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { /// `expand_impls()`). fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); - let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); + let docs = docs.then(|| expand_docs(def)).unwrap_or(TokenStream2::new()); let stable_api_count = def.host_funcs.iter().filter(|f| f.is_stable).count(); quote! { diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs index c6166ff45b19..79bf33a828e2 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -171,7 +171,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { let whitelisted_storage_idents: Vec = def .storages .iter() - .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .filter_map(|s| s.whitelisted.then(|| s.ident.clone())) .collect(); let whitelisted_storage_keys_impl = quote::quote![ diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index 905d6143e4f1..3fdf8d6edc95 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -825,14 +825,14 @@ impl SteppedMigrations for T { fn nth_id(n: u32) -> Option> { n.is_zero() - .then_some(T::id().encode()) + .then(|| T::id().encode()) .defensive_proof("nth_id should only be called with n==0") } fn nth_max_steps(n: u32) -> Option> { // It should be generally fine to call with n>0, but the code should not attempt to. n.is_zero() - .then_some(T::max_steps()) + .then(|| T::max_steps()) .defensive_proof("nth_max_steps should only be called with n==0") } diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index 938bda837e0d..5d9308aed154 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -220,13 +220,15 @@ pub fn run() -> Result<()> { runner.run_node_until_exit(|config| async move { let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench( - Some(database_path), - &SUBSTRATE_REFERENCE_HARDWARE, - ) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) + }) + }) .flatten(); let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) From 34f471d6fb8c5b40955642d383becb9bcc53cbe3 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 22 Oct 2024 18:38:38 -0300 Subject: [PATCH 28/58] Assets in pool with native can be used in `query_weight_to_asset_fee` (#6080) A follow-up to https://github.com/paritytech/polkadot-sdk/pull/5599. Assets in a pool with the native one are returned from `query_acceptable_payment_assets`. Now those assets can be used in `query_weight_to_asset_fee` to get the correct amount that needs to be paid. --------- Co-authored-by: command-bot <> --- .../emulated/common/src/macros.rs | 68 +++++++++++++++++++ .../tests/assets/asset-hub-rococo/src/lib.rs | 2 +- .../assets/asset-hub-rococo/src/tests/swap.rs | 5 ++ .../tests/assets/asset-hub-westend/src/lib.rs | 2 +- .../asset-hub-westend/src/tests/swap.rs | 5 ++ .../assets/asset-hub-rococo/src/lib.rs | 44 +++++++----- .../assets/asset-hub-westend/src/lib.rs | 47 ++++++++----- .../runtimes/assets/common/src/lib.rs | 33 +++++++++ prdoc/pr_6080.prdoc | 22 ++++++ 9 files changed, 191 insertions(+), 37 deletions(-) create mode 100644 prdoc/pr_6080.prdoc diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 68926b04bfe6..3ff5ed388a39 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -451,3 +451,71 @@ macro_rules! test_dry_run_transfer_across_pk_bridge { } }; } + +#[macro_export] +macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { + ( $asset_hub:ty ) => { + $crate::macros::paste::paste! { + use emulated_integration_tests_common::USDT_ID; + use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1}; + + $asset_hub::execute_with(|| { + // Setup a pool between USDT and WND. + type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin; + type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets; + type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion; + let wnd = Location::new(1, []); + let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]); + let sender = [<$asset_hub Sender>]::get(); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(sender.clone()), + Box::new(wnd.clone()), + Box::new(usdt.clone()), + )); + + type Runtime = <$asset_hub as Chain>::Runtime; + let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap(); + assert_eq!(acceptable_payment_assets, vec![ + VersionedAssetId::from(AssetId(wnd.clone())), + VersionedAssetId::from(AssetId(usdt.clone())), + ]); + + let program = Xcm::<()>::builder() + .withdraw_asset((Parent, 100u128)) + .buy_execution((Parent, 10u128), Unlimited) + .deposit_asset(All, [0u8; 32]) + .build(); + let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap(); + let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap(); + // Assets not in a pool don't work. + assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err()); + let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone()))); + // Weight to asset fee fails because there's not enough asset in the pool. + // We just created it, there's none. + assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound)); + // We add some. + assert_ok!(Assets::mint( + RuntimeOrigin::signed(sender.clone()), + USDT_ID.into(), + sender.clone().into(), + 5_000_000_000_000 + )); + // We make 1 WND = 4 USDT. + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(sender.clone()), + Box::new(wnd), + Box::new(usdt.clone()), + 1_000_000_000_000, + 4_000_000_000_000, + 0, + 0, + sender.into() + )); + // Now it works. + let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt))); + assert_ok!(fee_in_usdt); + assert!(fee_in_usdt.unwrap() > fee_in_wnd); + }); + } + }; +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 12f440fdefee..1184b5fd7c4a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -37,7 +37,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index ac0c90ba198d..d9b32eaa357e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -386,3 +386,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ); }); } + +#[test] +fn xcm_fee_querying_apis_work() { + test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubRococo); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 906768b19b79..179a44e14aa1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -34,7 +34,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index 1a2821452155..4535fd431990 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -389,3 +389,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ); }); } + +#[test] +fn xcm_fee_querying_apis_work() { + test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubWestend); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index ae5d2102ff66..f768f803aeaf 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -1412,31 +1412,41 @@ impl_runtime_apis! { // We accept the native token to pay fees. let mut acceptable_assets = vec![AssetId(native_token.clone())]; // We also accept all assets in a pool with the native token. - acceptable_assets.extend( - pallet_asset_conversion::Pools::::iter_keys().filter_map( - |(asset_1, asset_2)| { - if asset_1 == native_token { - Some(asset_2.clone().into()) - } else if asset_2 == native_token { - Some(asset_1.clone().into()) - } else { - None - } - }, - ), - ); + let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::Location + >(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter(); + acceptable_assets.extend(assets_in_pool_with_native); PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let native_asset = xcm_config::TokenLocation::get(); + let fee_in_native = WeightToFee::weight_to_fee(&weight); match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => { + Ok(asset_id) if asset_id.0 == native_asset => { // for native token - Ok(WeightToFee::weight_to_fee(&weight)) + Ok(fee_in_native) }, Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) + let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::Location + >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; + if assets_in_pool_with_this_asset + .into_iter() + .map(|asset_id| asset_id.0) + .any(|location| location == native_asset) { + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + asset_id.clone().0, + native_asset, + fee_in_native, + true, // We include the fee. + ).ok_or(XcmPaymentApiError::AssetNotFound) + } else { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + } }, Err(_) => { log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 0da80098b28f..63234bfb6e59 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1450,31 +1450,42 @@ impl_runtime_apis! { // We accept the native token to pay fees. let mut acceptable_assets = vec![AssetId(native_token.clone())]; // We also accept all assets in a pool with the native token. - acceptable_assets.extend( - pallet_asset_conversion::Pools::::iter_keys().filter_map( - |(asset_1, asset_2)| { - if asset_1 == native_token { - Some(asset_2.clone().into()) - } else if asset_2 == native_token { - Some(asset_1.clone().into()) - } else { - None - } - }, - ), - ); + let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::Location + >(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter(); + acceptable_assets.extend(assets_in_pool_with_native); PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let native_asset = xcm_config::WestendLocation::get(); + let fee_in_native = WeightToFee::weight_to_fee(&weight); match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) + Ok(asset_id) if asset_id.0 == native_asset => { + // for native asset + Ok(fee_in_native) }, Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) + // We recognize assets in a pool with the native one. + let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::Location + >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; + if assets_in_pool_with_this_asset + .into_iter() + .map(|asset_id| asset_id.0) + .any(|location| location == native_asset) { + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + asset_id.clone().0, + native_asset, + fee_in_native, + true, // We include the fee. + ).ok_or(XcmPaymentApiError::AssetNotFound) + } else { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + } }, Err(_) => { log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index deda5fa4ab9c..26046e5974b5 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -26,6 +26,9 @@ pub mod runtime_api; extern crate alloc; use crate::matching::{LocalLocationPattern, ParentLocation}; +use alloc::vec::Vec; +use codec::{Decode, EncodeLike}; +use core::cmp::PartialEq; use frame_support::traits::{Equals, EverythingBut}; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId}; use sp_runtime::traits::TryConvertInto; @@ -134,6 +137,36 @@ pub type PoolAssetsConvertedConcreteId = TryConvertInto, >; +/// Returns an iterator of all assets in a pool with `asset`. +/// +/// Should only be used in runtime APIs since it iterates over the whole +/// `pallet_asset_conversion::Pools` map. +/// +/// It takes in any version of an XCM Location but always returns the latest one. +/// This is to allow some margin of migrating the pools when updating the XCM version. +/// +/// An error of type `()` is returned if the version conversion fails for XCM locations. +/// This error should be mapped by the caller to a more descriptive one. +pub fn get_assets_in_pool_with< + Runtime: pallet_asset_conversion::Config, + L: TryInto + Clone + Decode + EncodeLike + PartialEq, +>( + asset: &L, +) -> Result, ()> { + pallet_asset_conversion::Pools::::iter_keys() + .filter_map(|(asset_1, asset_2)| { + if asset_1 == *asset { + Some(asset_2) + } else if asset_2 == *asset { + Some(asset_1) + } else { + None + } + }) + .map(|location| location.try_into().map_err(|_| ()).map(AssetId)) + .collect::, _>>() +} + #[cfg(test)] mod tests { use super::*; diff --git a/prdoc/pr_6080.prdoc b/prdoc/pr_6080.prdoc new file mode 100644 index 000000000000..52ecd58dddde --- /dev/null +++ b/prdoc/pr_6080.prdoc @@ -0,0 +1,22 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Assets in pool with native can be used in query_weight_to_asset_fee in Asset Hubs + +doc: + - audience: Runtime User + description: | + `query_weight_to_asset_fee` now works with assets in a pool with the native asset in both + Westend and Rococo asset hubs. + This means all the information you get from `query_acceptable_payment_assets` can be used + directly in `query_weight_to_asset_fee` to get the correct fees that need to be paid. + +crates: + - name: assets-common + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: emulated-integration-tests-common + bump: minor From 6ee35388114fa3b6763f531b9f01f86e796ce570 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 23 Oct 2024 13:22:48 +0200 Subject: [PATCH 29/58] [pallet-revive] Add pallet to AH westend (#5502) Add pallet-revive to Westend runtime, and configure the runtime to accept Ethereum signed transaction --- Cargo.lock | 9 +- .../assets/asset-hub-westend/Cargo.toml | 4 + .../assets/asset-hub-westend/src/lib.rs | 183 +++++++++++++++++- prdoc/pr_5502.prdoc | 7 + 4 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 prdoc/pr_5502.prdoc diff --git a/Cargo.lock b/Cargo.lock index a42f5baa4765..ce19fb039cb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "pallet-nfts", "pallet-nfts-runtime-api", "pallet-proxy", + "pallet-revive", "pallet-session", "pallet-state-trie-migration", "pallet-timestamp", @@ -16835,8 +16836,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -16869,7 +16870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16882,7 +16883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 5fa48381b674..d5eaa43ab834 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -44,6 +44,7 @@ pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-uniques = { workspace = true } +pallet-revive = { workspace = true } pallet-utility = { workspace = true } sp-api = { workspace = true } sp-block-builder = { workspace = true } @@ -129,6 +130,7 @@ runtime-benchmarks = [ "pallet-nft-fractionalization/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-revive/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-transaction-payment/runtime-benchmarks", @@ -169,6 +171,7 @@ try-runtime = [ "pallet-nft-fractionalization/try-runtime", "pallet-nfts/try-runtime", "pallet-proxy/try-runtime", + "pallet-revive/try-runtime", "pallet-session/try-runtime", "pallet-state-trie-migration/try-runtime", "pallet-timestamp/try-runtime", @@ -221,6 +224,7 @@ std = [ "pallet-nfts-runtime-api/std", "pallet-nfts/std", "pallet-proxy/std", + "pallet-revive/std", "pallet-session/std", "pallet-state-trie-migration/std", "pallet-timestamp/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 63234bfb6e59..9a60de77a584 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -47,7 +47,7 @@ use frame_support::{ fungible, fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, - TransformOrigin, + Nothing, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -58,13 +58,14 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; +use pallet_revive::evm::runtime::EthExtra; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, @@ -934,6 +935,52 @@ impl pallet_xcm_bridge_hub_router::Config for Runtime type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; } +parameter_types! { + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); + pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); +} + +type EventRecord = frame_system::EventRecord< + ::RuntimeEvent, + ::Hash, +>; + +impl pallet_revive::Config for Runtime { + type Time = Timestamp; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = pallet_revive::weights::SubstrateWeight; + type ChainExtension = (); + type AddressMapper = pallet_revive::DefaultAddressMapper; + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type RuntimeHoldReason = RuntimeHoldReason; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = (); + type Xcm = pallet_xcm::Pallet; + type ChainId = ConstU64<420_420_421>; +} + +impl TryFrom for pallet_revive::Call { + type Error = (); + + fn try_from(value: RuntimeCall) -> Result { + match value { + RuntimeCall::Revive(call) => Ok(call), + _ => Err(()), + } + } +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -982,6 +1029,7 @@ construct_runtime!( AssetsFreezer: pallet_assets_freezer:: = 57, ForeignAssetsFreezer: pallet_assets_freezer:: = 58, PoolAssetsFreezer: pallet_assets_freezer:: = 59, + Revive: pallet_revive = 60, StateTrieMigration: pallet_state_trie_migration = 70, @@ -1012,9 +1060,34 @@ pub type TxExtension = ( cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, frame_metadata_hash_extension::CheckMetadataHash, ); + +/// Default extensions applied to Ethereum transactions. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EthExtraImpl; + +impl EthExtra for EthExtraImpl { + type Config = Runtime; + type Extension = TxExtension; + + fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::from(generic::Era::Immortal), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::::from(tip, None), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ) + } +} + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + pallet_revive::evm::runtime::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -1249,6 +1322,7 @@ mod benches { [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_xcm_bridge_hub_router, ToRococo] [pallet_asset_conversion_ops, AssetConversionMigration] + [pallet_revive, Revive] // XCM [pallet_xcm, PalletXcmExtrinsicsBenchmark::] // NOTE: Make sure you point to the individual modules below. @@ -1994,6 +2068,109 @@ impl_runtime_apis! { PolkadotXcm::is_trusted_teleporter(asset, location) } } + + impl pallet_revive::ReviveApi for Runtime + { + fn eth_transact( + from: H160, + dest: Option, + value: Balance, + input: Vec, + gas_limit: Option, + storage_deposit_limit: Option, + ) -> pallet_revive::EthContractResult + { + use pallet_revive::AddressMapper; + let blockweights = ::BlockWeights::get(); + let origin = ::AddressMapper::to_account_id(&from); + + let encoded_size = |pallet_call| { + let call = RuntimeCall::Revive(pallet_call); + let uxt: UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into(); + uxt.encoded_size() as u32 + }; + + Revive::bare_eth_transact( + origin, + dest, + value, + input, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + encoded_size, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn call( + origin: AccountId, + dest: H160, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> pallet_revive::ContractResult { + let blockweights= ::BlockWeights::get(); + Revive::bare_call( + RuntimeOrigin::signed(origin), + dest, + value, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + input_data, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: pallet_revive::Code, + data: Vec, + salt: Option<[u8; 32]>, + ) -> pallet_revive::ContractResult + { + let blockweights= ::BlockWeights::get(); + Revive::bare_instantiate( + RuntimeOrigin::signed(origin), + value, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + code, + data, + salt, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> pallet_revive::CodeUploadResult + { + Revive::bare_upload_code( + RuntimeOrigin::signed(origin), + code, + storage_deposit_limit.unwrap_or(u128::MAX), + ) + } + + fn get_storage( + address: H160, + key: [u8; 32], + ) -> pallet_revive::GetStorageResult { + Revive::get_storage( + address, + key + ) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/prdoc/pr_5502.prdoc b/prdoc/pr_5502.prdoc new file mode 100644 index 000000000000..ea9972f01870 --- /dev/null +++ b/prdoc/pr_5502.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] Add pallet to AH westend' +doc: + - audience: Runtime Dev + description: 'Add pallet-revive to Westend runtime, and configure the runtime to accept Ethereum signed transaction' +crates: +- name: asset-hub-westend-runtime + bump: major From 74bb59a6eb6217ca8e70f51787f5506eeab7e28f Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:58:58 +0100 Subject: [PATCH 30/58] Polkadot OmniNode Docs (#6094) provides low-level documentation on how the omni-node is meant to work. This is meant to act as reusable material for other teams (e.g. Papermoon and W3F) to use and integrate into the high level Polkadot documentation. Broadly speaking, for omni-node to have great rust-docs, we need to focus on the following crates, all of which got a bit of love in this PR: 1. `sp-genesis-builder` 2. `polkadot-omni-node` 3. `polkadot-omni-node-lib` 4. `frame-omni-bencher` On top of this, we have now: * `polkadot_sdk_docs::guides` contains two new steps demonstrating the most basic version of composing your pallet, putting it into a runtime, and putting that runtime into omni-node * `polkadot_sdk_docs::reference_docs::omni_node` to explain in more detail how omni-node differs from the old-school node. * `polkadot_sdk_docs::reference_docs::frame_weight_benchmarking` to finally have a minimal reference about weights and benchmarking. * It provides tests for some of the steps in https://github.com/paritytech/polkadot-sdk/issues/5568 closes https://github.com/paritytech/polkadot-sdk/issues/5568 closes https://github.com/paritytech/polkadot-sdk/issues/4781 Next steps - [x] Ensure the README of the parachain template is up-to-date. @iulianbarbu - [ ] Readme for `polkadot-omni-node` and similar is updated. For now, use `cargo-readme` and copy over the rust-docs. To build the branch locally and run this: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/meta_contributing/index.html#how-to-develop-locally --------- Co-authored-by: Iulian Barbu <14218860+iulianbarbu@users.noreply.github.com> Co-authored-by: Sebastian Kunert Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> --- .gitignore | 1 + Cargo.lock | 48 +- Cargo.toml | 4 + cumulus/polkadot-omni-node/lib/src/cli.rs | 6 + cumulus/polkadot-omni-node/lib/src/lib.rs | 13 +- .../lib/src/nodes/manual_seal.rs | 6 +- cumulus/polkadot-omni-node/src/main.rs | 7 +- docs/mermaid/IA.mmd | 1 - docs/sdk/Cargo.toml | 24 +- docs/sdk/assets/theme.css | 35 ++ .../packages/guides/first-pallet/Cargo.toml | 26 + .../packages/guides/first-pallet/src/lib.rs | 480 ++++++++++++++++++ .../packages/guides/first-runtime/Cargo.toml | 60 +++ .../packages/guides/first-runtime/build.rs | 27 + .../packages/guides/first-runtime/src/lib.rs | 301 +++++++++++ .../src/guides/enable_elastic_scaling_mvp.rs | 9 +- docs/sdk/src/guides/mod.rs | 39 +- docs/sdk/src/guides/your_first_node.rs | 313 ++++++++++++ docs/sdk/src/guides/your_first_pallet/mod.rs | 46 +- .../guides/your_first_pallet/with_event.rs | 101 ---- docs/sdk/src/guides/your_first_runtime.rs | 169 +++++- docs/sdk/src/lib.rs | 3 +- docs/sdk/src/meta_contributing.rs | 10 +- docs/sdk/src/polkadot_sdk/mod.rs | 23 + docs/sdk/src/polkadot_sdk/substrate.rs | 16 - .../src/reference_docs/chain_spec_genesis.rs | 3 + .../frame_benchmarking_weight.rs | 221 +++++++- docs/sdk/src/reference_docs/mod.rs | 4 +- docs/sdk/src/reference_docs/omni_node.rs | 185 +++++++ docs/sdk/src/reference_docs/umbrella_crate.rs | 1 + prdoc/pr_6094.prdoc | 21 + substrate/bin/node/cli/src/cli.rs | 1 + substrate/frame/Cargo.toml | 6 + substrate/frame/aura/src/lib.rs | 4 +- substrate/frame/src/lib.rs | 10 + .../primitives/genesis-builder/src/lib.rs | 72 ++- templates/minimal/runtime/src/lib.rs | 2 +- .../runtime/src/genesis_config_presets.rs | 2 + 38 files changed, 2071 insertions(+), 229 deletions(-) create mode 100644 docs/sdk/packages/guides/first-pallet/Cargo.toml create mode 100644 docs/sdk/packages/guides/first-pallet/src/lib.rs create mode 100644 docs/sdk/packages/guides/first-runtime/Cargo.toml create mode 100644 docs/sdk/packages/guides/first-runtime/build.rs create mode 100644 docs/sdk/packages/guides/first-runtime/src/lib.rs delete mode 100644 docs/sdk/src/guides/your_first_pallet/with_event.rs create mode 100644 docs/sdk/src/reference_docs/omni_node.rs create mode 100644 prdoc/pr_6094.prdoc diff --git a/.gitignore b/.gitignore index 28c28cc8ab0e..afa9ed33f4a0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ runtime/wasm/target/ substrate.code-workspace target/ *.scale +justfile diff --git a/Cargo.lock b/Cargo.lock index ce19fb039cb9..602891892a2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15633,6 +15633,7 @@ dependencies = [ name = "polkadot-sdk-docs" version = "0.0.1" dependencies = [ + "assert_cmd", "chain-spec-guide-runtime", "cumulus-client-service", "cumulus-pallet-aura-ext", @@ -15640,6 +15641,7 @@ dependencies = [ "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "docify", + "frame-benchmarking", "frame-executive", "frame-metadata-hash-extension", "frame-support", @@ -15663,6 +15665,7 @@ dependencies = [ "pallet-example-offchain-worker", "pallet-example-single-block-migrations", "pallet-examples", + "pallet-grandpa", "pallet-multisig", "pallet-nfts", "pallet-preimage", @@ -15677,8 +15680,12 @@ dependencies = [ "pallet-xcm", "parachain-template-runtime", "parity-scale-codec", + "polkadot-omni-node-lib", "polkadot-sdk", + "polkadot-sdk-docs-first-pallet", + "polkadot-sdk-docs-first-runtime", "polkadot-sdk-frame", + "rand", "sc-chain-spec", "sc-cli", "sc-client-db", @@ -15694,6 +15701,7 @@ dependencies = [ "sc-rpc-api", "sc-service", "scale-info", + "serde_json", "simple-mermaid 0.1.1", "solochain-template-runtime", "sp-api 26.0.0", @@ -15708,6 +15716,7 @@ dependencies = [ "sp-std 14.0.0", "sp-tracing 16.0.0", "sp-version 29.0.0", + "sp-weights 27.0.0", "staging-chain-spec-builder", "staging-node-cli", "staging-parachain-info", @@ -15720,6 +15729,35 @@ dependencies = [ "xcm-simulator", ] +[[package]] +name = "polkadot-sdk-docs-first-pallet" +version = "0.0.0" +dependencies = [ + "docify", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", +] + +[[package]] +name = "polkadot-sdk-docs-first-runtime" +version = "0.0.0" +dependencies = [ + "docify", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "polkadot-sdk-docs-first-pallet", + "polkadot-sdk-frame", + "scale-info", + "serde_json", + "sp-keyring", + "substrate-wasm-builder", +] + [[package]] name = "polkadot-sdk-frame" version = "0.1.0" @@ -15742,8 +15780,10 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-grandpa", "sp-core 28.0.0", + "sp-genesis-builder", "sp-inherents", "sp-io 30.0.0", + "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", "sp-session", @@ -16836,8 +16876,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.5.0", - "itertools 0.12.1", + "heck 0.4.1", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -16870,7 +16910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16883,7 +16923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", diff --git a/Cargo.toml b/Cargo.toml index 049de32b54cf..6ba91de3c09b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,8 @@ members = [ "cumulus/test/service", "cumulus/xcm/xcm-emulator", "docs/sdk", + "docs/sdk/packages/guides/first-pallet", + "docs/sdk/packages/guides/first-runtime", "docs/sdk/src/reference_docs/chain_spec_runtime", "polkadot", "polkadot/cli", @@ -806,6 +808,8 @@ hyper = { version = "1.3.1", default-features = false } hyper-rustls = { version = "0.24.2" } hyper-util = { version = "0.1.5", default-features = false } # TODO: remove hyper v0.14 https://github.com/paritytech/polkadot-sdk/issues/4896 +first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } +first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } hyperv14 = { package = "hyper", version = "0.14.29", default-features = false } impl-serde = { version = "0.5.0", default-features = false } impl-trait-for-tuples = { version = "0.2.2" } diff --git a/cumulus/polkadot-omni-node/lib/src/cli.rs b/cumulus/polkadot-omni-node/lib/src/cli.rs index 6ca328912bba..dc59c185d909 100644 --- a/cumulus/polkadot-omni-node/lib/src/cli.rs +++ b/cumulus/polkadot-omni-node/lib/src/cli.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! CLI options of the omni-node. See [`Command`]. + use crate::{ chain_spec::DiskChainSpecLoader, common::{ @@ -103,6 +105,7 @@ pub enum Subcommand { Benchmark(frame_benchmarking_cli::BenchmarkCmd), } +/// CLI Options shipped with `polkadot-omni-node`. #[derive(clap::Parser)] #[command( propagate_version = true, @@ -113,9 +116,11 @@ pub struct Cli { #[arg(skip)] pub(crate) chain_spec_loader: Option>, + /// Possible subcommands. See [`Subcommand`]. #[command(subcommand)] pub subcommand: Option, + /// The shared parameters with all cumulus-based parachain nodes. #[command(flatten)] pub run: cumulus_client_cli::RunCmd, @@ -200,6 +205,7 @@ impl SubstrateCli for Cli { } } +/// The relay chain CLI flags. These are passed in after a `--` at the end. #[derive(Debug)] pub struct RelayChainCli { /// The actual relay chain cli object. diff --git a/cumulus/polkadot-omni-node/lib/src/lib.rs b/cumulus/polkadot-omni-node/lib/src/lib.rs index a293ab225c6f..3f01f4211144 100644 --- a/cumulus/polkadot-omni-node/lib/src/lib.rs +++ b/cumulus/polkadot-omni-node/lib/src/lib.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! # Polkadot Omni Node Library +//! //! Helper library that can be used to run a parachain node. //! //! ## Overview @@ -37,11 +39,18 @@ //! //! ## Examples //! -//! For an example, see the `polkadot-parachain-bin` crate. +//! For an example, see the [`polkadot-parachain-bin`](https://crates.io/crates/polkadot-parachain-bin) crate. +//! +//! ## Binary +//! +//! It can be used to start a parachain node from a provided chain spec file. +//! It is only compatible with runtimes that use block number `u32` and `Aura` consensus. +//! +//! Example: `polkadot-omni-node --chain ` #![deny(missing_docs)] -mod cli; +pub mod cli; mod command; mod common; mod fake_runtime_api; diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs index d00d7adf27e1..e8043bd7b2aa 100644 --- a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -28,7 +28,6 @@ use sc_network::NetworkBackend; use sc_service::{build_polkadot_syncing_strategy, Configuration, PartialComponents, TaskManager}; use sc_telemetry::TelemetryHandle; use sp_runtime::traits::Header; -use sp_timestamp::Timestamp; use std::{marker::PhantomData, sync::Arc}; pub struct ManualSealNode(PhantomData); @@ -182,7 +181,10 @@ impl ManualSealNode { additional_key_values: None, }; Ok(( - sp_timestamp::InherentDataProvider::new(Timestamp::new(0)), + // This is intentional, as the runtime that we expect to run against this + // will never receive the aura-related inherents/digests, and providing + // real timestamps would cause aura <> timestamp checking to fail. + sp_timestamp::InherentDataProvider::new(sp_timestamp::Timestamp::new(0)), mocked_parachain, )) } diff --git a/cumulus/polkadot-omni-node/src/main.rs b/cumulus/polkadot-omni-node/src/main.rs index a86ec6f6fde6..5bca81e2e78b 100644 --- a/cumulus/polkadot-omni-node/src/main.rs +++ b/cumulus/polkadot-omni-node/src/main.rs @@ -14,12 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -//! Basic polkadot omni-node. +//! White labeled polkadot omni-node. //! -//! It can be used to start a parachain node from a provided chain spec file. -//! It is only compatible with runtimes that use block number `u32` and `Aura` consensus. -//! -//! Example: `polkadot-omni-node --chain [chain_spec.json]` +//! For documentation, see [`polkadot_omni_node_lib`]. #![warn(missing_docs)] #![warn(unused_extern_crates)] diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd index 0f14e200df9c..dcf9806dcb62 100644 --- a/docs/mermaid/IA.mmd +++ b/docs/mermaid/IA.mmd @@ -8,6 +8,5 @@ flowchart polkadot_sdk --> substrate polkadot_sdk --> frame - polkadot_sdk --> polkadot[polkadot node] polkadot_sdk --> xcm polkadot_sdk --> templates diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index b86ce9868208..0c39367eeed3 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -29,6 +29,7 @@ pallet-example-offchain-worker = { workspace = true, default-features = true } # How we build docs in rust-docs simple-mermaid = "0.1.1" docify = { workspace = true } +serde_json = { workspace = true } # Polkadot SDK deps, typically all should only be in scope such that we can link to their doc item. polkadot-sdk = { features = ["runtime-full"], workspace = true, default-features = true } @@ -39,6 +40,7 @@ subkey = { workspace = true, default-features = true } frame-system = { workspace = true } frame-support = { workspace = true } frame-executive = { workspace = true } +frame-benchmarking = { workspace = true } pallet-example-authorization-tx-extension = { workspace = true, default-features = true } pallet-example-single-block-migrations = { workspace = true, default-features = true } frame-metadata-hash-extension = { workspace = true, default-features = true } @@ -70,6 +72,9 @@ cumulus-primitives-proof-size-hostfunction = { workspace = true, default-feature cumulus-client-service = { workspace = true, default-features = true } cumulus-primitives-storage-weight-reclaim = { workspace = true, default-features = true } +# Omni Node +polkadot-omni-node-lib = { workspace = true, default-features = true } + # Pallets and FRAME internals pallet-aura = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } @@ -92,6 +97,7 @@ pallet-scheduler = { workspace = true, default-features = true } pallet-referenda = { workspace = true, default-features = true } pallet-broker = { workspace = true, default-features = true } pallet-babe = { workspace = true, default-features = true } +pallet-grandpa = { workspace = true, default-features = true } # Primitives sp-io = { workspace = true, default-features = true } @@ -106,6 +112,7 @@ sp-arithmetic = { workspace = true, default-features = true } sp-genesis-builder = { workspace = true, default-features = true } sp-offchain = { workspace = true, default-features = true } sp-version = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } # XCM @@ -117,9 +124,18 @@ xcm-simulator = { workspace = true } pallet-xcm = { workspace = true } # runtime guides -chain-spec-guide-runtime = { workspace = true } + +chain-spec-guide-runtime = { workspace = true, default-features = true } # Templates -minimal-template-runtime = { workspace = true } -solochain-template-runtime = { workspace = true } -parachain-template-runtime = { workspace = true } +minimal-template-runtime = { workspace = true, default-features = true } +solochain-template-runtime = { workspace = true, default-features = true } +parachain-template-runtime = { workspace = true, default-features = true } + +# local packages +first-runtime = { workspace = true, default-features = true } +first-pallet = { workspace = true, default-features = true } + +[dev-dependencies] +assert_cmd = "2.0.14" +rand = "0.8" diff --git a/docs/sdk/assets/theme.css b/docs/sdk/assets/theme.css index 1f47a8ef5b0c..f9aa4760275e 100644 --- a/docs/sdk/assets/theme.css +++ b/docs/sdk/assets/theme.css @@ -6,6 +6,27 @@ --polkadot-purple: #552BBF; } +/* Light theme */ +html[data-theme="light"] { + --quote-background: #f9f9f9; + --quote-border: #ccc; + --quote-text: #333; +} + +/* Dark theme */ +html[data-theme="dark"] { + --quote-background: #333; + --quote-border: #555; + --quote-text: #f9f9f9; +} + +/* Ayu theme */ +html[data-theme="ayu"] { + --quote-background: #272822; + --quote-border: #383830; + --quote-text: #f8f8f2; +} + body.sdk-docs { nav.sidebar>div.sidebar-crate>a>img { width: 190px; @@ -20,3 +41,17 @@ body.sdk-docs { html[data-theme="light"] .sidebar-crate > .logo-container > img { content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png"); } + +/* Custom styles for blockquotes */ +blockquote { + background-color: var(--quote-background); + border-left: 5px solid var(--quote-border); + color: var(--quote-text); + margin: 1em 0; + padding: 1em 1.5em; + /* font-style: italic; */ +} + +blockquote p { + margin: 0; +} diff --git a/docs/sdk/packages/guides/first-pallet/Cargo.toml b/docs/sdk/packages/guides/first-pallet/Cargo.toml new file mode 100644 index 000000000000..dad5b8863494 --- /dev/null +++ b/docs/sdk/packages/guides/first-pallet/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "polkadot-sdk-docs-first-pallet" +description = "A simple pallet created for the polkadot-sdk-docs guides" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } +frame = { workspace = true, features = ["experimental", "runtime"] } +docify = { workspace = true } + +[features] +default = ["std"] +std = ["codec/std", "frame/std", "scale-info/std"] diff --git a/docs/sdk/packages/guides/first-pallet/src/lib.rs b/docs/sdk/packages/guides/first-pallet/src/lib.rs new file mode 100644 index 000000000000..168b7ca44aba --- /dev/null +++ b/docs/sdk/packages/guides/first-pallet/src/lib.rs @@ -0,0 +1,480 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallets used in the `your_first_pallet` guide. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod shell_pallet { + use frame::prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); +} + +#[frame::pallet(dev_mode)] +pub mod pallet { + use frame::prelude::*; + + #[docify::export] + pub type Balance = u128; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export] + /// Single storage item, of type `Balance`. + #[pallet::storage] + pub type TotalIssuance = StorageValue<_, Balance>; + + #[docify::export] + /// A mapping from `T::AccountId` to `Balance` + #[pallet::storage] + pub type Balances = StorageMap<_, _, T::AccountId, Balance>; + + #[docify::export(impl_pallet)] + #[pallet::call] + impl Pallet { + /// An unsafe mint that can be called by anyone. Not a great idea. + pub fn mint_unsafe( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + // ensure that this is a signed account, but we don't really check `_anyone`. + let _anyone = ensure_signed(origin)?; + + // update the balances map. Notice how all `` remains as ``. + Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); + // update total issuance. + TotalIssuance::::mutate(|t| *t = Some(t.unwrap_or(0) + amount)); + + Ok(()) + } + + /// Transfer `amount` from `origin` to `dest`. + pub fn transfer( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + // ensure sender has enough balance, and if so, calculate what is left after `amount`. + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + if sender_balance < amount { + return Err("InsufficientBalance".into()) + } + let remainder = sender_balance - amount; + + // update sender and dest balances. + Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); + Balances::::insert(&sender, remainder); + + Ok(()) + } + } + + #[allow(unused)] + impl Pallet { + #[docify::export] + pub fn transfer_better( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + ensure!(sender_balance >= amount, "InsufficientBalance"); + let remainder = sender_balance - amount; + + // .. snip + Ok(()) + } + + #[docify::export] + /// Transfer `amount` from `origin` to `dest`. + pub fn transfer_better_checked( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; + + // .. snip + Ok(()) + } + } + + #[cfg(any(test, doc))] + pub(crate) mod tests { + use crate::pallet::*; + + #[docify::export(testing_prelude)] + use frame::testing_prelude::*; + + pub(crate) const ALICE: u64 = 1; + pub(crate) const BOB: u64 = 2; + pub(crate) const CHARLIE: u64 = 3; + + #[docify::export] + // This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod + // tests { .. }` + mod runtime { + use super::*; + // we need to reference our `mod pallet` as an identifier to pass to + // `construct_runtime`. + // YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE + use crate::pallet as pallet_currency; + + construct_runtime!( + pub enum Runtime { + // ---^^^^^^ This is where `enum Runtime` is defined. + System: frame_system, + Currency: pallet_currency, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + // within pallet we just said `::AccountId`, now we + // finally specified it. + type AccountId = u64; + } + + // our simple pallet has nothing to be configured. + impl pallet_currency::Config for Runtime {} + } + + pub(crate) use runtime::*; + + #[allow(unused)] + #[docify::export] + fn new_test_state_basic() -> TestState { + let mut state = TestState::new_empty(); + let accounts = vec![(ALICE, 100), (BOB, 100)]; + state.execute_with(|| { + for (who, amount) in &accounts { + Balances::::insert(who, amount); + TotalIssuance::::mutate(|b| *b = Some(b.unwrap_or(0) + amount)); + } + }); + + state + } + + #[docify::export] + pub(crate) struct StateBuilder { + balances: Vec<(::AccountId, Balance)>, + } + + #[docify::export(default_state_builder)] + impl Default for StateBuilder { + fn default() -> Self { + Self { balances: vec![(ALICE, 100), (BOB, 100)] } + } + } + + #[docify::export(impl_state_builder_add)] + impl StateBuilder { + fn add_balance( + mut self, + who: ::AccountId, + amount: Balance, + ) -> Self { + self.balances.push((who, amount)); + self + } + } + + #[docify::export(impl_state_builder_build)] + impl StateBuilder { + pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) { + let mut ext = TestState::new_empty(); + ext.execute_with(|| { + for (who, amount) in &self.balances { + Balances::::insert(who, amount); + TotalIssuance::::mutate(|b| *b = Some(b.unwrap_or(0) + amount)); + } + }); + + ext.execute_with(test); + + // assertions that must always hold + ext.execute_with(|| { + assert_eq!( + Balances::::iter().map(|(_, x)| x).sum::(), + TotalIssuance::::get().unwrap_or_default() + ); + }) + } + } + + #[docify::export] + #[test] + fn first_test() { + TestState::new_empty().execute_with(|| { + // We expect Alice's account to have no funds. + assert_eq!(Balances::::get(&ALICE), None); + assert_eq!(TotalIssuance::::get(), None); + + // mint some funds into Alice's account. + assert_ok!(Pallet::::mint_unsafe( + RuntimeOrigin::signed(ALICE), + ALICE, + 100 + )); + + // re-check the above + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(100)); + }) + } + + #[docify::export] + #[test] + fn state_builder_works() { + StateBuilder::default().build_and_execute(|| { + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + + #[docify::export] + #[test] + fn state_builder_add_balance() { + StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| { + assert_eq!(Balances::::get(&CHARLIE), Some(42)); + assert_eq!(TotalIssuance::::get(), Some(242)); + }) + } + + #[test] + #[should_panic] + fn state_builder_duplicate_genesis_fails() { + StateBuilder::default() + .add_balance(CHARLIE, 42) + .add_balance(CHARLIE, 43) + .build_and_execute(|| { + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(242)); + }) + } + + #[docify::export] + #[test] + fn mint_works() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_ok!(Pallet::::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100)); + + // then: + assert_eq!(Balances::::get(&BOB), Some(200)); + assert_eq!(TotalIssuance::::get(), Some(300)); + + // given: + assert_ok!(Pallet::::mint_unsafe( + RuntimeOrigin::signed(ALICE), + CHARLIE, + 100 + )); + + // then: + assert_eq!(Balances::::get(&CHARLIE), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(400)); + }); + } + + #[docify::export] + #[test] + fn transfer_works() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(ALICE), BOB, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(50)); + assert_eq!(Balances::::get(&BOB), Some(150)); + assert_eq!(TotalIssuance::::get(), Some(200)); + + // when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(BOB), ALICE, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + + #[docify::export] + #[test] + fn transfer_from_non_existent_fails() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_err!( + Pallet::::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10), + "NonExistentAccount" + ); + + // then nothing has changed. + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_v2 { + use super::pallet::Balance; + use frame::prelude::*; + + #[docify::export(config_v2)] + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type of the runtime. + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + TryInto>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type Balances = StorageMap<_, _, T::AccountId, Balance>; + + #[pallet::storage] + pub type TotalIssuance = StorageValue<_, Balance>; + + #[docify::export] + #[pallet::error] + pub enum Error { + /// Account does not exist. + NonExistentAccount, + /// Account does not have enough balance. + InsufficientBalance, + } + + #[docify::export] + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A transfer succeeded. + Transferred { from: T::AccountId, to: T::AccountId, amount: Balance }, + } + + #[pallet::call] + impl Pallet { + #[docify::export(transfer_v2)] + pub fn transfer( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + // ensure sender has enough balance, and if so, calculate what is left after `amount`. + let sender_balance = + Balances::::get(&sender).ok_or(Error::::NonExistentAccount)?; + let remainder = + sender_balance.checked_sub(amount).ok_or(Error::::InsufficientBalance)?; + + Balances::::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount)); + Balances::::insert(&sender, remainder); + + Self::deposit_event(Event::::Transferred { from: sender, to: dest, amount }); + + Ok(()) + } + } + + #[cfg(any(test, doc))] + pub mod tests { + use super::{super::pallet::tests::StateBuilder, *}; + use frame::testing_prelude::*; + const ALICE: u64 = 1; + const BOB: u64 = 2; + + #[docify::export] + pub mod runtime_v2 { + use super::*; + use crate::pallet_v2 as pallet_currency; + + construct_runtime!( + pub enum Runtime { + System: frame_system, + Currency: pallet_currency, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + type AccountId = u64; + } + + impl pallet_currency::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + } + } + + pub(crate) use runtime_v2::*; + + #[docify::export(transfer_works_v2)] + #[test] + fn transfer_works() { + StateBuilder::default().build_and_execute(|| { + // skip the genesis block, as events are not deposited there and we need them for + // the final assertion. + System::set_block_number(ALICE); + + // given the initial state, when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(ALICE), BOB, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(50)); + assert_eq!(Balances::::get(&BOB), Some(150)); + assert_eq!(TotalIssuance::::get(), Some(200)); + + // now we can also check that an event has been deposited: + assert_eq!( + System::read_events_for_pallet::>(), + vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }] + ); + }); + } + } +} diff --git a/docs/sdk/packages/guides/first-runtime/Cargo.toml b/docs/sdk/packages/guides/first-runtime/Cargo.toml new file mode 100644 index 000000000000..303d5c5e7f5f --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "polkadot-sdk-docs-first-runtime" +description = "A simple runtime created for the polkadot-sdk-docs guides" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } +serde_json = { workspace = true } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { workspace = true, features = ["experimental", "runtime"] } + +# pallets that we want to use +pallet-balances = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } + +# other polkadot-sdk-deps +sp-keyring = { workspace = true } + +# local pallet templates +first-pallet = { workspace = true } + +docify = { workspace = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true, optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "serde_json/std", + + "frame/std", + + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "first-pallet/std", + "sp-keyring/std", + + "substrate-wasm-builder", +] diff --git a/docs/sdk/packages/guides/first-runtime/build.rs b/docs/sdk/packages/guides/first-runtime/build.rs new file mode 100644 index 000000000000..b7676a70dfe8 --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/build.rs @@ -0,0 +1,27 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + #[cfg(feature = "std")] + { + substrate_wasm_builder::WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build(); + } +} diff --git a/docs/sdk/packages/guides/first-runtime/src/lib.rs b/docs/sdk/packages/guides/first-runtime/src/lib.rs new file mode 100644 index 000000000000..92d51962b6fe --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/src/lib.rs @@ -0,0 +1,301 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime used in `your_first_runtime`. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +use alloc::{vec, vec::Vec}; +use first_pallet::pallet_v2 as our_first_pallet; +use frame::{ + prelude::*, + runtime::{apis, prelude::*}, +}; +use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo}; + +#[docify::export] +#[runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("first-runtime"), + impl_name: create_runtime_str!("first-runtime"), + authoring_version: 1, + spec_version: 0, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + system_version: 1, +}; + +#[docify::export(cr)] +construct_runtime!( + pub struct Runtime { + // Mandatory for all runtimes + System: frame_system, + + // A number of other pallets from FRAME. + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Sudo: pallet_sudo, + TransactionPayment: pallet_transaction_payment, + + // Our local pallet + FirstPallet: our_first_pallet, + } +); + +#[docify::export_content] +mod runtime_types { + use super::*; + pub(super) type SignedExtra = ( + // `frame` already provides all the signed extensions from `frame-system`. We just add the + // one related to tx-payment here. + frame::runtime::types_common::SystemTransactionExtensionsOf, + pallet_transaction_payment::ChargeTransactionPayment, + ); + + pub(super) type Block = frame::runtime::types_common::BlockOf; + pub(super) type Header = HeaderFor; + + pub(super) type RuntimeExecutive = Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + >; +} +use runtime_types::*; + +#[docify::export_content] +mod config_impls { + use super::*; + + parameter_types! { + pub const Version: RuntimeVersion = VERSION; + } + + #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = Block; + type Version = Version; + type AccountData = + pallet_balances::AccountData<::Balance>; + } + + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] + impl pallet_balances::Config for Runtime { + type AccountStore = System; + } + + #[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)] + impl pallet_sudo::Config for Runtime {} + + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] + impl pallet_timestamp::Config for Runtime {} + + #[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] + impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + // We specify a fixed length to fee here, which essentially means all transactions charge + // exactly 1 unit of fee. + type LengthToFee = FixedFee<1, ::Balance>; + type WeightToFee = NoFee<::Balance>; + } +} + +#[docify::export(our_config_impl)] +impl our_first_pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +/// Provides getters for genesis configuration presets. +pub mod genesis_config_presets { + use super::*; + use crate::{ + interface::{Balance, MinimumBalance}, + BalancesConfig, RuntimeGenesisConfig, SudoConfig, + }; + use serde_json::Value; + + /// Returns a development genesis config preset. + #[docify::export] + pub fn development_config_genesis() -> Value { + let endowment = >::get().max(1) * 1000; + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: AccountKeyring::iter() + .map(|a| (a.to_account_id(), endowment)) + .collect::>(), + }, + sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") + } + + /// Get the set of the available genesis config presets. + #[docify::export] + pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + /// List of supported presets. + #[docify::export] + pub fn preset_names() -> Vec { + vec![PresetId::from(DEV_RUNTIME_PRESET)] + } +} + +impl_runtime_apis! { + impl apis::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + RuntimeExecutive::execute_block(block) + } + + fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { + RuntimeExecutive::initialize_block(header) + } + } + + impl apis::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 apis::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ExtrinsicFor) -> ApplyExtrinsicResult { + RuntimeExecutive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> HeaderFor { + RuntimeExecutive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl apis::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ExtrinsicFor, + block_hash: ::Hash, + ) -> TransactionValidity { + RuntimeExecutive::validate_transaction(source, tx, block_hash) + } + } + + impl apis::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &HeaderFor) { + RuntimeExecutive::offchain_worker(header) + } + } + + impl apis::SessionKeys for Runtime { + fn generate_session_keys(_seed: Option>) -> Vec { + Default::default() + } + + fn decode_session_keys( + _encoded: Vec, + ) -> Option, apis::KeyTypeId)>> { + Default::default() + } + } + + impl apis::AccountNonceApi for Runtime { + fn account_nonce(account: interface::AccountId) -> interface::Nonce { + System::account_nonce(account) + } + } + + impl apis::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> GenesisBuilderResult { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, self::genesis_config_presets::get_preset) + } + + fn preset_names() -> Vec { + crate::genesis_config_presets::preset_names() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + interface::Balance, + > for Runtime { + fn query_info(uxt: ExtrinsicFor, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ExtrinsicFor, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> interface::Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> interface::Balance { + TransactionPayment::length_to_fee(length) + } + } +} + +/// Just a handy re-definition of some types based on what is already provided to the pallet +/// configs. +pub mod interface { + use super::Runtime; + use frame::prelude::frame_system; + + pub type AccountId = ::AccountId; + pub type Nonce = ::Nonce; + pub type Hash = ::Hash; + pub type Balance = ::Balance; + pub type MinimumBalance = ::ExistentialDeposit; +} diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index 812e674d163b..2339088abed4 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -70,10 +70,11 @@ //! - Ensure enough coretime is assigned to the parachain. For maximum throughput the upper bound is //! 3 cores. //! -//!
Phase 1 is not needed if using the polkadot-parachain binary -//! built from the latest polkadot-sdk release! Simply pass the -//! --experimental-use-slot-based parameter to the command line and jump to Phase -//! 2.
+//!
Phase 1 is NOT needed if using the polkadot-parachain or +//! polkadot-omni-node binary, or polkadot-omni-node-lib built from the +//! latest polkadot-sdk release! Simply pass the --experimental-use-slot-based +//! ([`polkadot_omni_node_lib::cli::Cli::experimental_use_slot_based`]) parameter to the command +//! line and jump to Phase 2.
//! //! The following steps assume using the cumulus parachain template. //! diff --git a/docs/sdk/src/guides/mod.rs b/docs/sdk/src/guides/mod.rs index a7fd146ccdf3..747128a728d0 100644 --- a/docs/sdk/src/guides/mod.rs +++ b/docs/sdk/src/guides/mod.rs @@ -1,14 +1,22 @@ //! # Polkadot SDK Docs Guides //! -//! This crate contains a collection of guides that are foundational to the developers of Polkadot -//! SDK. They are common user-journeys that are traversed in the Polkadot ecosystem. +//! This crate contains a collection of guides that are foundational to the developers of +//! Polkadot SDK. They are common user-journeys that are traversed in the Polkadot ecosystem. //! -//! 1. [`crate::guides::your_first_pallet`] is your starting point with Polkadot SDK. It contains -//! the basics of -//! building a simple crypto currency with FRAME. -//! 2. [`crate::guides::your_first_runtime`] is the next step in your journey. It contains the -//! basics of building a runtime that contains this pallet, plus a few common pallets from FRAME. +//! The main user-journey covered by these guides is: //! +//! * [`your_first_pallet`], where you learn what a FRAME pallet is, and write your first +//! application logic. +//! * [`your_first_runtime`], where you learn how to compile your pallets into a WASM runtime. +//! * [`your_first_node`], where you learn how to run the said runtime in a node. +//! +//! > By this step, you have already launched a full Polkadot-SDK-based blockchain! +//! +//! Once done, feel free to step up into one of our templates: [`crate::polkadot_sdk::templates`]. +//! +//! [`your_first_pallet`]: crate::guides::your_first_pallet +//! [`your_first_runtime`]: crate::guides::your_first_runtime +//! [`your_first_node`]: crate::guides::your_first_node //! //! Other guides are related to other miscellaneous topics and are listed as modules below. @@ -19,19 +27,12 @@ pub mod your_first_pallet; /// compiling it to [WASM](crate::polkadot_sdk::substrate#wasm-build). pub mod your_first_runtime; -// /// Running the given runtime with a node. No specific consensus mechanism is used at this stage. -// TODO -// pub mod your_first_node; - -// /// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain -// /// and connect it to a relay-chain. -// TODO -// pub mod cumulus_enabled_parachain; +/// Running the given runtime with a node. No specific consensus mechanism is used at this stage. +pub mod your_first_node; -// /// How to make a given runtime XCM-enabled, capable of sending messages (`Transact`) between -// /// itself and the relay chain to which it is connected. -// TODO -// pub mod xcm_enabled_parachain; +/// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain +/// and connect it to a relay-chain. +// pub mod your_first_parachain; /// How to enable storage weight reclaiming in a parachain node and runtime. pub mod enable_pov_reclaim; diff --git a/docs/sdk/src/guides/your_first_node.rs b/docs/sdk/src/guides/your_first_node.rs index d12349c99063..da37c11c206f 100644 --- a/docs/sdk/src/guides/your_first_node.rs +++ b/docs/sdk/src/guides/your_first_node.rs @@ -1 +1,314 @@ //! # Your first Node +//! +//! In this guide, you will learn how to run a runtime, such as the one created in +//! [`your_first_runtime`], in a node. Within the context of this guide, we will focus on running +//! the runtime with an [`omni-node`]. Please first read this page to learn about the OmniNode, and +//! other options when it comes to running a node. +//! +//! [`your_first_runtime`] is a runtime with no consensus related code, and therefore can only be +//! executed with a node that also expects no consensus ([`sc_consensus_manual_seal`]). +//! `polkadot-omni-node`'s [`--dev-block-time`] precisely does this. +//! +//! > All of the following steps are coded as unit tests of this module. Please see `Source` of the +//! > page for more information. +//! +//! ## Running The Omni Node +//! +//! ### Installs +//! +//! The `polkadot-omni-node` can either be downloaded from the latest [Release](https://github.com/paritytech/polkadot-sdk/releases/) of `polkadot-sdk`, +//! or installed using `cargo`: +//! +//! ```text +//! cargo install polkadot-omni-node +//! ``` +//! +//! Next, we need to install the [`chain-spec-builder`]. This is the tool that allows us to build +//! chain-specifications, through interacting with the genesis related APIs of the runtime, as +//! described in [`crate::guides::your_first_runtime#genesis-configuration`]. +//! +//! ```text +//! cargo install staging-chain-spec-builder +//! ``` +//! +//! > The name of the crate is prefixed with `staging` as the crate name `chain-spec-builder` on +//! > crates.io is already taken and is not controlled by `polkadot-sdk` developers. +//! +//! ### Building Runtime +//! +//! Next, we need to build the corresponding runtime that we wish to interact with. +//! +//! ```text +//! cargo build --release -p path-to-runtime +//! ``` +//! Equivalent code in tests: +#![doc = docify::embed!("./src/guides/your_first_node.rs", build_runtime)] +//! +//! This creates the wasm file under `./target/{release}/wbuild/release` directory. +//! +//! ### Building Chain Spec +//! +//! Next, we can generate the corresponding chain-spec file. For this example, we will use the +//! `development` (`sp_genesis_config::DEVELOPMENT`) preset. +//! +//! Note that we intend to run this chain-spec with `polkadot-omni-node`, which is tailored for +//! running parachains. This requires the chain-spec to always contain the `para_id` and a +//! `relay_chain` fields, which are provided below as CLI arguments. +//! +//! ```text +//! chain-spec-builder \ +//! -c \ +//! create \ +//! --para-id 42 \ +//! --relay-chain dontcare \ +//! --runtime polkadot_sdk_docs_first_runtime.wasm \ +//! named-preset development +//! ``` +//! +//! Equivalent code in tests: +#![doc = docify::embed!("./src/guides/your_first_node.rs", csb)] +//! +//! +//! ### Running `polkadot-omni-node` +//! +//! Finally, we can run the node with the generated chain-spec file. We can also specify the block +//! time using the `--dev-block-time` flag. +//! +//! ```text +//! polkadot-omni-node \ +//! --tmp \ +//! --dev-block-time 1000 \ +//! --chain .json +//! ``` +//! +//! > Note that we always prefer to use `--tmp` for testing, as it will save the chain state to a +//! > temporary folder, allowing the chain-to be easily restarted without `purge-chain`. See +//! > [`sc_cli::commands::PurgeChainCmd`] and [`sc_cli::commands::RunCmd::tmp`] for more info. +//! +//! This will start the node and import the blocks. Note while using `--dev-block-time`, the node +//! will use the testing-specific manual-seal consensus. This is an efficient way to test the +//! application logic of your runtime, without needing to yet care about consensus, block +//! production, relay-chain and so on. +//! +//! ### Next Steps +//! +//! * See the rest of the steps in [`crate::reference_docs::omni_node#user-journey`]. +//! +//! [`runtime`]: crate::reference_docs::glossary#runtime +//! [`node`]: crate::reference_docs::glossary#node +//! [`build_config`]: first_runtime::Runtime#method.build_config +//! [`omni-node`]: crate::reference_docs::omni_node +//! [`--dev-block-time`]: (polkadot_omni_node_lib::cli::Cli::dev_block_time) + +#[cfg(test)] +mod tests { + use assert_cmd::Command; + use rand::Rng; + use sc_chain_spec::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET}; + use sp_genesis_builder::PresetId; + use std::path::PathBuf; + + const PARA_RUNTIME: &'static str = "parachain-template-runtime"; + const FIRST_RUNTIME: &'static str = "polkadot-sdk-docs-first-runtime"; + const MINIMAL_RUNTIME: &'static str = "minimal-template-runtime"; + + const CHAIN_SPEC_BUILDER: &'static str = "chain-spec-builder"; + const OMNI_NODE: &'static str = "polkadot-omni-node"; + + fn cargo() -> Command { + Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())) + } + + fn get_target_directory() -> Option { + let output = cargo().arg("metadata").arg("--format-version=1").output().ok()?; + + if !output.status.success() { + return None; + } + + let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?; + let target_directory = metadata["target_directory"].as_str()?; + + Some(PathBuf::from(target_directory)) + } + + fn find_release_binary(name: &str) -> Option { + let target_dir = get_target_directory()?; + let release_path = target_dir.join("release").join(name); + + if release_path.exists() { + Some(release_path) + } else { + None + } + } + + fn find_wasm(runtime_name: &str) -> Option { + let target_dir = get_target_directory()?; + let wasm_path = target_dir + .join("release") + .join("wbuild") + .join(runtime_name) + .join(format!("{}.wasm", runtime_name.replace('-', "_"))); + + if wasm_path.exists() { + Some(wasm_path) + } else { + None + } + } + + fn maybe_build_runtimes() { + if find_wasm(&PARA_RUNTIME).is_none() { + println!("Building parachain-template-runtime..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg(PARA_RUNTIME) + .assert() + .success(); + } + if find_wasm(&FIRST_RUNTIME).is_none() { + println!("Building polkadot-sdk-docs-first-runtime..."); + #[docify::export_content] + fn build_runtime() { + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg(FIRST_RUNTIME) + .assert() + .success(); + } + build_runtime() + } + + assert!(find_wasm(PARA_RUNTIME).is_some()); + assert!(find_wasm(FIRST_RUNTIME).is_some()); + } + + fn maybe_build_chain_spec_builder() { + if find_release_binary(CHAIN_SPEC_BUILDER).is_none() { + println!("Building chain-spec-builder..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg("staging-chain-spec-builder") + .assert() + .success(); + } + assert!(find_release_binary(CHAIN_SPEC_BUILDER).is_some()); + } + + fn maybe_build_omni_node() { + if find_release_binary(OMNI_NODE).is_none() { + println!("Building polkadot-omni-node..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg("polkadot-omni-node") + .assert() + .success(); + } + } + + fn test_runtime_preset(runtime: &'static str, block_time: u64, maybe_preset: Option) { + sp_tracing::try_init_simple(); + maybe_build_runtimes(); + maybe_build_chain_spec_builder(); + maybe_build_omni_node(); + + let chain_spec_builder = + find_release_binary(&CHAIN_SPEC_BUILDER).expect("we built it above; qed"); + let omni_node = find_release_binary(OMNI_NODE).expect("we built it above; qed"); + let runtime_path = find_wasm(runtime).expect("we built it above; qed"); + + let random_seed: u32 = rand::thread_rng().gen(); + let chain_spec_file = std::env::current_dir() + .unwrap() + .join(format!("{}_{}_{}.json", runtime, block_time, random_seed)); + + Command::new(chain_spec_builder) + .args(["-c", chain_spec_file.to_str().unwrap()]) + .arg("create") + .args(["--para-id", "1000", "--relay-chain", "dontcare"]) + .args(["-r", runtime_path.to_str().unwrap()]) + .args(match maybe_preset { + Some(preset) => vec!["named-preset".to_string(), preset.to_string()], + None => vec!["default".to_string()], + }) + .assert() + .success(); + + let output = Command::new(omni_node) + .arg("--tmp") + .args(["--chain", chain_spec_file.to_str().unwrap()]) + .args(["--dev-block-time", block_time.to_string().as_str()]) + .timeout(std::time::Duration::from_secs(10)) + .output() + .unwrap(); + + std::fs::remove_file(chain_spec_file).unwrap(); + + // uncomment for debugging. + // println!("output: {:?}", output); + + let expected_blocks = (10_000 / block_time).saturating_div(2); + assert!(expected_blocks > 0, "test configuration is bad, should give it more time"); + assert!(String::from_utf8(output.stderr) + .unwrap() + .contains(format!("Imported #{}", expected_blocks).to_string().as_str())); + } + + #[test] + fn works_with_different_block_times() { + test_runtime_preset(PARA_RUNTIME, 100, Some(DEV_RUNTIME_PRESET.into())); + test_runtime_preset(PARA_RUNTIME, 3000, Some(DEV_RUNTIME_PRESET.into())); + + // we need this snippet just for docs + #[docify::export_content(csb)] + fn build_para_chain_spec_works() { + let chain_spec_builder = find_release_binary(&CHAIN_SPEC_BUILDER).unwrap(); + let runtime_path = find_wasm(PARA_RUNTIME).unwrap(); + let output = "/tmp/demo-chain-spec.json"; + Command::new(chain_spec_builder) + .args(["-c", output]) + .arg("create") + .args(["--para-id", "1000", "--relay-chain", "dontcare"]) + .args(["-r", runtime_path.to_str().unwrap()]) + .args(["named-preset", "development"]) + .assert() + .success(); + std::fs::remove_file(output).unwrap(); + } + build_para_chain_spec_works(); + } + + #[test] + fn parachain_runtime_works() { + // TODO: None doesn't work. But maybe it should? it would be misleading as many users might + // use it. + [Some(DEV_RUNTIME_PRESET.into()), Some(LOCAL_TESTNET_RUNTIME_PRESET.into())] + .into_iter() + .for_each(|preset| { + test_runtime_preset(PARA_RUNTIME, 1000, preset); + }); + } + + #[test] + fn minimal_runtime_works() { + [None, Some(DEV_RUNTIME_PRESET.into())].into_iter().for_each(|preset| { + test_runtime_preset(MINIMAL_RUNTIME, 1000, preset); + }); + } + + #[test] + fn guide_first_runtime_works() { + [Some(DEV_RUNTIME_PRESET.into())].into_iter().for_each(|preset| { + test_runtime_preset(FIRST_RUNTIME, 1000, preset); + }); + } +} diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index fcfaab00e552..aef8981b4d4a 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -47,7 +47,7 @@ //! //! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any //! pallet. Refer to the documentation of each to get an overview of what they do. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)] //! //! All of the code that follows in this guide should live inside of the `mod pallet`. //! @@ -61,17 +61,17 @@ //! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of //! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a //! > generic bounded type in the `Config` trait, and then specify it in the implementation. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)] //! //! The definition of these two storage items, based on [`pallet::storage`] details, is as follows: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", TotalIssuance)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balances)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)] //! //! ### Dispatchables //! //! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined //! as normal `fn`s attached to `struct Pallet`. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_pallet)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)] //! //! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related //! details: @@ -108,14 +108,14 @@ //! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be //! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better. //! This macro will call `.into()` under the hood. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)] //! //! Moreover, you will learn in the [Defensive Programming //! section](crate::reference_docs::defensive_programming) that it is always recommended to use //! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not //! only take a step in that direction, but also improve the error handing and make it slightly more //! ergonomic. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better_checked)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)] //! //! This is more or less all the logic that there is in this basic currency pallet! //! @@ -145,7 +145,7 @@ //! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include //! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system` //! and the one we just wrote. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)] //! //! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have //! > defaults for associated types. @@ -182,7 +182,7 @@ //! to learn is that all of your pallet testing code should be wrapped in //! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state //! to be used in our tests. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", first_test)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)] //! //! In the first test, we simply assert that there is no total issuance, and no balance associated //! with Alice's account. Then, we mint some balance into Alice's, and re-check. @@ -206,16 +206,16 @@ //! //! Let's see how we can implement a better test setup using this pattern. First, we define a //! `struct StateBuilder`. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", StateBuilder)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)] //! //! This struct is meant to contain the same list of accounts and balances that we want to have at //! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];` //! so far. Then, if desired, we attach a default value for this struct. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", default_state_builder)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)] //! //! Like any other builder pattern, we attach functions to the type to mutate its internal //! properties. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_state_builder_add)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)] //! //! Finally --the useful part-- we write our own custom `build_and_execute` function on //! this type. This function will do multiple things: @@ -227,23 +227,23 @@ //! after each test. For example, in this test, we do some additional checking about the //! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the //! assertion should always hold, and how it is checked. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_state_builder_build)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)] //! //! We can write tests that specifically check the initial state, and making sure our `StateBuilder` //! is working exactly as intended. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", state_builder_works)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", state_builder_add_balance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)] //! //! ### More Tests //! //! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and //! mint would look like. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_works)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", mint_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)] //! //! It is always a good idea to build a mental model where you write *at least* one test for each //! "success path" of a dispatchable, and one test for each "failure path", such as: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_from_non_existent_fails)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)] //! //! We leave it up to you to write a test that triggers the `InsufficientBalance` error. //! @@ -272,8 +272,8 @@ //! With the explanation out of the way, let's see how these components can be added. Both follow a //! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`] //! attributes attached. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)] //! //! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into //! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do @@ -288,17 +288,17 @@ //! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME //! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is //! doing. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)] //! //! > These `Runtime*` types are better explained in //! > [`crate::reference_docs::frame_runtime_types`]. //! //! Then, we can rewrite the `transfer` dispatchable as such: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)] //! //! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime //! setup. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)] //! //! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent = //! RuntimeEvent`) is generated by diff --git a/docs/sdk/src/guides/your_first_pallet/with_event.rs b/docs/sdk/src/guides/your_first_pallet/with_event.rs deleted file mode 100644 index a5af29c9c319..000000000000 --- a/docs/sdk/src/guides/your_first_pallet/with_event.rs +++ /dev/null @@ -1,101 +0,0 @@ -#[frame::pallet(dev_mode)] -pub mod pallet { - use frame::prelude::*; - - #[docify::export] - pub type Balance = u128; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[docify::export] - /// Single storage item, of type `Balance`. - #[pallet::storage] - pub type TotalIssuance = StorageValue<_, Balance>; - - #[docify::export] - /// A mapping from `T::AccountId` to `Balance` - #[pallet::storage] - pub type Balances = StorageMap<_, _, T::AccountId, Balance>; - - #[docify::export(impl_pallet)] - #[pallet::call] - impl Pallet { - /// An unsafe mint that can be called by anyone. Not a great idea. - pub fn mint_unsafe( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - // ensure that this is a signed account, but we don't really check `_anyone`. - let _anyone = ensure_signed(origin)?; - - // update the balances map. Notice how all `` remains as ``. - Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - // update total issuance. - TotalIssuance::::mutate(|t| *t = Some(t.unwrap_or(0) + amount)); - - Ok(()) - } - - /// Transfer `amount` from `origin` to `dest`. - pub fn transfer( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - // ensure sender has enough balance, and if so, calculate what is left after `amount`. - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - if sender_balance < amount { - return Err("NotEnoughBalance".into()) - } - let remainder = sender_balance - amount; - - // update sender and dest balances. - Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, remainder); - - Ok(()) - } - } - - #[allow(unused)] - impl Pallet { - #[docify::export] - pub fn transfer_better( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - ensure!(sender_balance >= amount, "NotEnoughBalance"); - let remainder = sender_balance - amount; - - // .. snip - Ok(()) - } - - #[docify::export] - /// Transfer `amount` from `origin` to `dest`. - pub fn transfer_better_checked( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let remainder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; - - // .. snip - Ok(()) - } - } -} diff --git a/docs/sdk/src/guides/your_first_runtime.rs b/docs/sdk/src/guides/your_first_runtime.rs index c58abc1120c1..79f01e66979a 100644 --- a/docs/sdk/src/guides/your_first_runtime.rs +++ b/docs/sdk/src/guides/your_first_runtime.rs @@ -1,3 +1,170 @@ //! # Your first Runtime //! -//! 🚧 +//! This guide will walk you through the steps to add your pallet to a runtime. +//! +//! The good news is, in [`crate::guides::your_first_pallet`], we have already created a _test_ +//! runtime that was used for testing, and a real runtime is not that much different! +//! +//! ## Setup +//! +//! A runtime shares a few similar setup requirements as with a pallet: +//! +//! * importing [`frame`], [`codec`], and [`scale_info`] crates. +//! * following the [`std` feature-gating](crate::polkadot_sdk::substrate#wasm-build) pattern. +//! +//! But, more specifically, it also contains: +//! +//! * a `build.rs` that uses [`substrate_wasm_builder`]. This entails declaring +//! `[build-dependencies]` in the Cargo manifest file: +//! +//! ```ignore +//! [build-dependencies] +//! substrate-wasm-builder = { ... } +//! ``` +//! +//! >Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes +//! per rust crate. +//! +//! You can find the full code of this guide in [`first_runtime`]. +//! +//! ## Your First Runtime +//! +//! The first new property of a real runtime that it must define its +//! [`frame::runtime::prelude::RuntimeVersion`]: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", VERSION)] +//! +//! The version contains a number of very important fields, such as `spec_version` and `spec_name` +//! that play an important role in identifying your runtime and its version, more importantly in +//! runtime upgrades. More about runtime upgrades in +//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`]. +//! +//! Then, a real runtime also contains the `impl` of all individual pallets' `trait Config` for +//! `struct Runtime`, and a [`frame::runtime::prelude::construct_runtime`] macro that amalgamates +//! them all. +//! +//! In the case of our example: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", our_config_impl)] +//! +//! In this example, we bring in a number of other pallets from [`frame`] into the runtime, each of +//! their `Config` need to be implemented for `struct Runtime`: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", config_impls)] +//! +//! Notice how we use [`frame::pallet_macros::derive_impl`] to provide "default" configuration items +//! for each pallet. Feel free to dive into the definition of each default prelude (eg. +//! [`frame::prelude::frame_system::pallet::config_preludes`]) to learn more which types are exactly +//! used. +//! +//! Recall that in test runtime in [`crate::guides::your_first_pallet`], we provided `type AccountId +//! = u64` to `frame_system`, while in this case we rely on whatever is provided by +//! [`SolochainDefaultConfig`], which is indeed a "real" 32 byte account id. +//! +//! Then, a familiar instance of `construct_runtime` amalgamates all of the pallets: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", cr)] +//! +//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that every (real) runtime needs to +//! implement a set of runtime APIs that will then let the node to communicate with it. The final +//! steps of crafting a runtime are related to achieving exactly this. +//! +//! First, we define a number of types that eventually lead to the creation of an instance of +//! [`frame::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through +//! amalgamating all pallets and further types, implements some of the very very core pieces of the +//! runtime logic, such as how blocks are executed and other runtime-api implementations. +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", runtime_types)] +//! +//! Finally, we use [`frame::runtime::prelude::impl_runtime_apis`] to implement all of the runtime +//! APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API +//! implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual +//! logic. Given that the implementation block is somewhat large, we won't repeat it here. You can +//! look for `impl_runtime_apis!` in [`first_runtime`]. +//! +//! ```ignore +//! impl_runtime_apis! { +//! impl apis::Core for Runtime { +//! fn version() -> RuntimeVersion { +//! VERSION +//! } +//! +//! fn execute_block(block: Block) { +//! RuntimeExecutive::execute_block(block) +//! } +//! +//! fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { +//! RuntimeExecutive::initialize_block(header) +//! } +//! } +//! +//! // many more trait impls... +//! } +//! ``` +//! +//! And that more or less covers the details of how you would write a real runtime! +//! +//! Once you compile a crate that contains a runtime as above, simply running `cargo build` will +//! generate the wasm blobs and place them under `./target/release/wbuild`, as explained +//! [here](crate::polkadot_sdk::substrate#wasm-build). +//! +//! ## Genesis Configuration +//! +//! Every runtime specifies a number of runtime APIs that help the outer world (most notably, a +//! `node`) know what is the genesis state of this runtime. These APIs are then used to generate +//! what is known as a **Chain Specification, or chain spec for short**. A chain spec is the +//! primary way to run a new chain. +//! +//! These APIs are defined in [`sp_genesis_builder`], and are re-exposed as a part of +//! [`frame::runtime::apis`]. Therefore, the implementation blocks can be found inside of +//! `impl_runtime_apis!` similar to: +//! +//! ```ignore +//! impl_runtime_apis! { +//! impl apis::GenesisBuilder for Runtime { +//! fn build_state(config: Vec) -> GenesisBuilderResult { +//! build_state::(config) +//! } +//! +//! fn get_preset(id: &Option) -> Option> { +//! get_preset::(id, self::genesis_config_presets::get_preset) +//! } +//! +//! fn preset_names() -> Vec { +//! crate::genesis_config_presets::preset_names() +//! } +//! } +//! +//! } +//! ``` +//! +//! The implementation of these function can naturally vary from one runtime to the other, but the +//! overall pattern is common. For the case of this runtime, we do the following: +//! +//! 1. Expose one non-default preset, namely [`sp_genesis_builder::DEV_RUNTIME_PRESET`]. This means +//! our runtime has two "presets" of genesis state in total: `DEV_RUNTIME_PRESET` and `None`. +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", preset_names)] +//! +//! For `build_state` and `get_preset`, we use the helper functions provide by frame: +//! +//! * [`frame::runtime::prelude::build_state`] and [`frame::runtime::prelude::get_preset`]. +//! +//! Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", development_config_genesis)] +//! +//! For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and +//! `chain-spec-builder`, see [`crate::reference_docs::chain_spec_genesis`]. +//! +//! ## Next Step +//! +//! See [`crate::guides::your_first_node`]. +//! +//! ## Further Reading +//! +//! 1. To learn more about signed extensions, see [`crate::reference_docs::signed_extensions`]. +//! 2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in +//! [`crate::reference_docs::frame_runtime_types`]. +//! 3. `Executive` supports more generics, most notably allowing the runtime to configure more +//! runtime migrations, as explained in +//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`]. +//! 4. Learn more about adding and implementing runtime apis in +//! [`crate::reference_docs::custom_runtime_api_rpc`]. +//! 5. To see a complete example of a runtime+pallet that is similar to this guide, please see +//! [`crate::polkadot_sdk::templates`]. +//! +//! [`SolochainDefaultConfig`]: struct@frame_system::pallet::config_preludes::SolochainDefaultConfig diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 86ca677d7cef..e2c5fc93479c 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -12,7 +12,8 @@ //! - Start by learning about the the [`polkadot_sdk`], its structure and context. //! - Then, head over to the [`guides`]. This modules contains in-depth guides about the most //! important user-journeys of the Polkadot SDK. -//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. +//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. +//! - [`external_resources`] for a list of 3rd party guides and tutorials. //! - Finally, is the parent website of this crate that contains the //! list of further tools related to the Polkadot SDK. //! diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs index e1297151b231..d68d9bca18b1 100644 --- a/docs/sdk/src/meta_contributing.rs +++ b/docs/sdk/src/meta_contributing.rs @@ -69,7 +69,8 @@ //! > what topics are already covered in this crate, and how you can build on top of the information //! > that they already pose, rather than repeating yourself**. //! -//! For more details see the [latest documenting guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md). +//! For more details see the [latest documenting +//! guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md). //! //! #### Example: Explaining `#[pallet::call]` //! @@ -132,6 +133,13 @@ //! compromise, but in the long term, we should work towards finding a way to maintain different //! revisions of this crate. //! +//! ## Versioning +//! +//! So long as not deployed in `crates.io`, please notice that all of the information in this crate, +//! namely in [`crate::guides`] and such are compatible with the master branch of `polkadot-sdk`. A +//! few solutions have been proposed to improve this, please see +//! [here](https://github.com/paritytech/polkadot-sdk/issues/6191). +//! //! ## How to Develop Locally //! //! To view the docs specific [`crate`] locally for development, including the correct HTML headers diff --git a/docs/sdk/src/polkadot_sdk/mod.rs b/docs/sdk/src/polkadot_sdk/mod.rs index c089b6729ce5..bf7346b871a1 100644 --- a/docs/sdk/src/polkadot_sdk/mod.rs +++ b/docs/sdk/src/polkadot_sdk/mod.rs @@ -75,6 +75,26 @@ //! runtimes are located under the //! [`polkadot-fellows/runtimes`](https://github.com/polkadot-fellows/runtimes) repository. //! +//! ### Binaries +//! +//! The main binaries that are part of the Polkadot SDK are: + +//! * [`polkadot`]: The Polkadot relay chain node binary, as noted above. +//! * [`polkadot-omni-node`]: A white-labeled parachain collator node. See more in +//! [`crate::reference_docs::omni_node`]. +//! * [`polkadot-parachain-bin`]: The collator node used to run collators for all Polkadot system +//! parachains. +//! * [`frame-omni-bencher`]: a benchmarking tool for FRAME-based runtimes. Nodes typically contain +//! a +//! `benchmark` subcommand that does the same. +//! * [`chain_spec_builder`]: Utility to build chain-specs Nodes typically contain a `build-spec` +//! subcommand that does the same. +//! * [`subkey`]: Substrate's key management utility. +//! * [`substrate-node`](node_cli) is an extensive substrate node that contains the superset of all +//! runtime and node side features. The corresponding runtime, called [`kitchensink_runtime`] +//! contains all of the modules that are provided with `FRAME`. This node and runtime is only used +//! for testing and demonstration. +//! //! ### Summary //! //! The following diagram summarizes how some of the components of Polkadot SDK work together: @@ -116,6 +136,9 @@ //! [`cumulus`]: crate::polkadot_sdk::cumulus //! [`polkadot`]: crate::polkadot_sdk::polkadot //! [`xcm`]: crate::polkadot_sdk::xcm +//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher +//! [`polkadot-parachain-bin`]: https://crates.io/crates/polkadot-parachain-bin +//! [`polkadot-omni-node`]: https://crates.io/crates/polkadot-omni-node /// Learn about Cumulus, the framework that transforms [`substrate`]-based chains into /// [`polkadot`]-enabled parachains. diff --git a/docs/sdk/src/polkadot_sdk/substrate.rs b/docs/sdk/src/polkadot_sdk/substrate.rs index 56b89f8c9c2a..ed654c2842c4 100644 --- a/docs/sdk/src/polkadot_sdk/substrate.rs +++ b/docs/sdk/src/polkadot_sdk/substrate.rs @@ -90,22 +90,6 @@ //! //! In order to ensure that the WASM build is **deterministic**, the [Substrate Runtime Toolbox (srtool)](https://github.com/paritytech/srtool) can be used. //! -//! ### Binaries -//! -//! Multiple binaries are shipped with substrate, the most important of which are located in the -//! [`./bin`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin) folder. -//! -//! * [`node_cli`] is an extensive substrate node that contains the superset of all runtime and node -//! side features. The corresponding runtime, called [`kitchensink_runtime`] contains all of the -//! modules that are provided with `FRAME`. This node and runtime is only used for testing and -//! demonstration. -//! * [`chain_spec_builder`]: Utility to build more detailed chain-specs for the aforementioned -//! node. Other projects typically contain a `build-spec` subcommand that does the same. -//! * [`node_template`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin/node): -//! a template node that contains a minimal set of features and can act as a starting point of a -//! project. -//! * [`subkey`]: Substrate's key management utility. -//! //! ### Anatomy of a Binary Crate //! //! From the above, [`node_cli`]/[`kitchensink_runtime`] and `node-template` are essentially diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index a2e22d1ed1eb..3326f433f288 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -152,10 +152,13 @@ //! presets and build the chain specification file. It is possible to use the tool with the //! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build the required packages, just run //! the following command: +//! //! ```ignore //! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release //! ``` +//! //! The `chain-spec-builder` util can also be installed with `cargo install`: +//! //! ```ignore //! cargo install staging-chain-spec-builder //! cargo build -p chain-spec-guide-runtime --release diff --git a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs index cf9e58791492..68d7d31f67f3 100644 --- a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs +++ b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs @@ -1,23 +1,212 @@ //! # FRAME Benchmarking and Weights. //! -//! Notes: +//! This reference doc explores the concept of weights within Polkadot-SDK runtimes, and more +//! specifically how FRAME-based runtimes handle it. //! -//! On Weight as a concept. +//! ## Metering //! -//! - Why we need it. Super important. People hate this. We need to argue why it is worth it. -//! - Axis of weight: PoV + Time. -//! - pre dispatch weight vs. metering and post dispatch correction. -//! - mention that we will do this for PoV -//! - you can manually refund using `DispatchResultWithPostInfo`. -//! - Technically you can have weights with any benchmarking framework. You just need one number to -//! be computed pre-dispatch. But FRAME gives you a framework for this. -//! - improve documentation of `#[weight = ..]` and `#[pallet::weight(..)]`. All syntax variation -//! should be covered. +//! The existence of "weight" as a concept in Polkadot-SDK is a direct consequence of the usage of +//! WASM as a virtual machine. Unlike a metered virtual machine like EVM, where every instruction +//! can have a (fairly) deterministic "cost" (also known as "gas price") associated with it, WASM is +//! a stack machine with more complex instruction set, and more unpredictable execution times. This +//! means that unlike EVM, it is not possible to implement a "metering" system in WASM. A metering +//! system is one in which instructions are executed one by one, and the cost/gas is stored in an +//! accumulator. The execution may then halt once a gas limit is reached. //! -//! On FRAME benchmarking machinery: +//! In Polkadot-SDK, the WASM runtime is not assumed to be metered. //! -//! - Component analysis, why everything must be linear. -//! - How to write benchmarks, how you must think of worst case. -//! - How to run benchmarks. +//! ## Trusted Code //! -//! - +//! Another important difference is that EVM is mostly used to express smart contracts, which are +//! foreign and untrusted codes from the perspective of the blockchain executing them. In such +//! cases, metering is crucial, in order to ensure a malicious code cannot consume more gas than +//! expected. +//! +//! This assumption does not hold about the runtime of Polkadot-SDK-based blockchains. The runtime +//! is trusted code, and it is assumed to be written by the same team/developers who are running the +//! blockchain itself. Therefore, this assumption of "untrusted foreign code" does not hold. +//! +//! This is why the runtime can opt for a more performant, more flexible virtual machine like WASM, +//! and get away without having metering. +//! +//! ## Benchmarking +//! +//! With the matter of untrusted code execution out of the way, the need for strict metering goes +//! out of the way. Yet, it would still be very beneficial for block producers to be able to know an +//! upper bound on how much resources a operation is going to consume before actually executing that +//! operation. This is why FRAME has a toolkit for benchmarking pallets: So that this upper bound +//! can be empirically determined. +//! +//! > Note: Benchmarking is a static analysis: It is all about knowing the upper bound of how much +//! > resources an operation takes statically, without actually executing it. In the context of +//! > FRAME extrinsics, this static-ness is expressed by the keyword "pre-dispatch". +//! +//! To understand why this upper bound is needed, consider the following: A block producer knows +//! they have 20ms left to finish producing their block, and wishes to include more transactions in +//! the block. Yet, in a metered environment, it would not know which transaction is likely to fit +//! the 20ms. In a benchmarked environment, it can examine the transactions for their upper bound, +//! and include the ones that are known to fit based on the worst case. +//! +//! The benchmarking code can be written as a part of FRAME pallet, using the macros provided in +//! [`frame_benchmarking`]. See any of the existing pallets in `polkadot-sdk`, or the pallets in our +//! [`crate::polkadot_sdk::templates`] for examples. +//! +//! ## Weight +//! +//! Finally, [`sp_weights::Weight`] is the output of the benchmarking process. It is a +//! two-dimensional data structure that demonstrates the resources consumed by a given block of +//! code (for example, a transaction). The two dimensions are: +//! +//! * reference time: The time consumed in pico-seconds, on a reference hardware. +//! * proof size: The amount of storage proof necessary to re-execute the block of code. This is +//! mainly needed for parachain <> relay-chain verification. +//! +//! ## How To Write Benchmarks: Worst Case +//! +//! The most important detail about writing benchmarking code is that it must be written such that +//! it captures the worst case execution of any block of code. +//! +//! Consider: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer)] +//! +//! If this block of code is to be benchmarked, then the benchmarking code must be written such that +//! it captures the worst case. +//! +//! ## Gluing Pallet Benchmarking with Runtime +//! +//! FRAME pallets are mandated to provide their own benchmarking code. Runtimes contain the +//! boilerplate needed to run these benchmarking (see [Running Benchmarks +//! below](#running-benchmarks)). The outcome of running these benchmarks are meant to be fed back +//! into the pallet via a conventional `trait WeightInfo` on `Config`: +#![doc = docify::embed!("src/reference_docs/frame_benchmarking_weight.rs", WeightInfo)] +//! +//! Then, individual functions of this trait are the final values that we assigned to the +//! [`frame::pallet_macros::weight`] attribute: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_2)] +//! +//! ## Manual Refund +//! +//! Back to the assumption of writing benchmarks for worst case: Sometimes, the pre-dispatch weight +//! significantly differ from the post-dispatch actual weight consumed. This can be expressed with +//! the following FRAME syntax: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_3)] +//! +//! ## Running Benchmarks +//! +//! Two ways exist to run the benchmarks of a runtime. +//! +//! 1. The old school way: Most Polkadot-SDK based nodes (such as the ones integrated in +//! [`templates`]) have an a `benchmark` subcommand integrated into themselves. +//! 2. The more [`crate::reference_docs::omni_node`] compatible way of running the benchmarks would +//! be using [`frame-omni-bencher`] CLI, which only relies on a runtime. +//! +//! Note that by convention, the runtime and pallets always have their benchmarking code feature +//! gated as behind `runtime-benchmarks`. So, the runtime should be compiled with `--features +//! runtime-benchmarks`. +//! +//! ## Automatic Refund of `proof_size`. +//! +//! A new feature in FRAME allows the runtime to be configured for "automatic refund" of the proof +//! size weight. This is very useful for maximizing the throughput of parachains. Please see: +//! [`crate::guides::enable_pov_reclaim`]. +//! +//! ## Summary +//! +//! Polkadot-SDK runtimes use a more performant VM, namely WASM, which does not have metering. In +//! return they have to be benchmarked to provide an upper bound on the resources they consume. This +//! upper bound is represented as [`sp_weights::Weight`]. +//! +//! ## Future: PolkaVM +//! +//! With the transition of Polkadot relay chain to [JAM], a set of new features are being +//! introduced, one of which being a new virtual machine named [PolkaVM] that is as flexible as +//! WASM, but also capable of metering. This might alter the future of benchmarking in FRAME and +//! Polkadot-SDK, rendering them not needed anymore once PolkaVM is fully integrated into +//! Polkadot-sdk. For a basic explanation of JAM and PolkaVM, see [here](https://blog.kianenigma.com/posts/tech/demystifying-jam/#pvm). +//! +//! +//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher +//! [`templates`]: crate::polkadot_sdk::templates +//! [PolkaVM]: https://github.com/koute/polkavm +//! [JAM]: https://graypaper.com + +#[frame::pallet(dev_mode)] +#[allow(unused_variables, unreachable_code, unused, clippy::diverging_sub_expression)] +pub mod pallet { + use frame::prelude::*; + + #[docify::export] + pub trait WeightInfo { + fn simple_transfer() -> Weight; + } + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[docify::export] + #[pallet::weight(10_000)] + pub fn simple_transfer( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResult { + let destination_exists = todo!(); + if destination_exists { + // simpler code path + } else { + // more complex code path + } + Ok(()) + } + + #[docify::export] + #[pallet::weight(T::WeightInfo::simple_transfer())] + pub fn simple_transfer_2( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResult { + let destination_exists = todo!(); + if destination_exists { + // simpler code path + } else { + // more complex code path + } + Ok(()) + } + + #[docify::export] + // This is the worst-case, pre-dispatch weight. + #[pallet::weight(T::WeightInfo::simple_transfer())] + pub fn simple_transfer_3( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResultWithPostInfo { + // ^^ Notice the new return type + let destination_exists = todo!(); + if destination_exists { + // simpler code path + // Note that need for .into(), to convert `()` to `PostDispatchInfo` + // See: https://paritytech.github.io/polkadot-sdk/master/frame_support/dispatch/struct.PostDispatchInfo.html#impl-From%3C()%3E-for-PostDispatchInfo + Ok(().into()) + } else { + // more complex code path + let actual_weight = + todo!("this can likely come from another benchmark that is NOT the worst case"); + let pays_fee = todo!("You can set this to `Pays::Yes` or `Pays::No` to change if this transaction should pay fees"); + Ok(frame::deps::frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(actual_weight), + pays_fee, + }) + } + } + } +} diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index 9cf5605a88ba..e47eece784c4 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -78,7 +78,6 @@ pub mod frame_system_accounts; pub mod development_environment_advice; /// Learn about benchmarking and weight. -// TODO: @shawntabrizi @ggwpez https://github.com/paritytech/polkadot-sdk-docs/issues/50 pub mod frame_benchmarking_weight; /// Learn about the token-related logic in FRAME and how to apply it to your use case. @@ -109,3 +108,6 @@ pub mod umbrella_crate; /// Learn about how to create custom RPC endpoints and runtime APIs. pub mod custom_runtime_api_rpc; + +/// The [`polkadot-omni-node`](https://crates.io/crates/polkadot-omni-node) and its related binaries. +pub mod omni_node; diff --git a/docs/sdk/src/reference_docs/omni_node.rs b/docs/sdk/src/reference_docs/omni_node.rs new file mode 100644 index 000000000000..44d63704a458 --- /dev/null +++ b/docs/sdk/src/reference_docs/omni_node.rs @@ -0,0 +1,185 @@ +//! # (Omni) Node +//! +//! This reference doc elaborates on what a Polkadot-SDK/Substrate node software is, and what +//! various ways exist to run one. +//! +//! The node software, as denoted in [`crate::reference_docs::wasm_meta_protocol`], is everything in +//! a blockchain other than the WASM runtime. It contains common components such as the database, +//! networking, RPC server and consensus. Substrate-based nodes are native binaries that are +//! compiled down from the Rust source code. The `node` folder in any of the [`templates`] are +//! examples of this source. +//! +//! > Note: A typical node also contains a lot of other tools (exposed as subcommands) that are +//! > useful for operating a blockchain, such as the ones noted in +//! > [`polkadot_omni_node_lib::cli::Cli::subcommand`]. +//! +//! ## Node <> Runtime Interdependence +//! +//! While in principle the node can be mostly independent of the runtime, for various reasons, such +//! as the [native runtime](crate::reference_docs::wasm_meta_protocol#native-runtime), the node and +//! runtime were historically tightly linked together. Another reason is that the node and the +//! runtime need to be in agreement about which consensus algorithm they use, as described +//! [below](#consensus-engine). +//! +//! Specifically, the node relied on the existence of a linked runtime, and *could only reliably run +//! that runtime*. This is why if you look at any of the [`templates`], they are all composed of a +//! node, and a runtime. +//! +//! Moreover, the code and API of each of these nodes was historically very advanced, and tailored +//! towards those who wish to customize many of the node components at depth. +//! +//! > The notorious `service.rs` in any node template is a good example of this. +//! +//! A [trend](https://github.com/paritytech/polkadot-sdk/issues/62) has already been undergoing in +//! order to de-couple the node and the runtime for a long time. The north star of this effort is +//! twofold : +//! +//! 1. develop what can be described as an "omni-node": A node that can run most runtimes. +//! 2. provide a cleaner abstraction for creating a custom node. +//! +//! While a single omni-node running *all possible runtimes* is not feasible, the +//! [`polkadot-omni-node`] is an attempt at creating the former, and the [`polkadot_omni_node_lib`] +//! is the latter. +//! +//! > Note: The OmniNodes are mainly focused on the development needs of **Polkadot +//! > parachains ONLY**, not (Substrate) solo-chains. For the time being, solo-chains are not +//! > supported by the OmniNodes. This might change in the future. +//! +//! ## Types of Nodes +//! +//! With the emergence of the OmniNodes, let's look at the various Node options available to a +//! builder. +//! +//! ### [`polkadot-omni-node`] +//! +//! [`polkadot-omni-node`] is a white-labeled binary, released as a part of Polkadot SDK that is +//! capable of meeting the needs of most Polkadot parachains. +//! +//! It can act as the collator of a parachain in production, with all the related auxillary +//! functionalities that a normal collator node has: RPC server, archiving state, etc. Moreover, it +//! can also run the wasm blob of the parachain locally for testing and development. +//! +//! ### [`polkadot_omni_node_lib`] +//! +//! [`polkadot_omni_node_lib`] is the library version of the above, which can be used to create a +//! fresh parachain node, with a some limited configuration options using a lean API. +//! +//! ### Old School Nodes +//! +//! The existing node architecture, as seen in the [`templates`], is still available for those who +//! want to have full control over the node software. +//! +//! ### Summary +//! +//! We can summarize the choices for the node software of any given user of Polkadot-SDK, wishing to +//! deploy a parachain into 3 categories: +//! +//! 1. **Use the [`polkadot-omni-node`]**: This is the easiest way to get started, and is the most +//! likely to be the best choice for most users. +//! * can run almost any runtime with [`--dev-block-time`] +//! 2. **Use the [`polkadot_omni_node_lib`]**: This is the best choice for those who want to have +//! slightly more control over the node software, such as embedding a custom chain-spec. +//! 3. **Use the old school nodes**: This is the best choice for those who want to have full control +//! over the node software, such as changing the consensus engine, altering the transaction pool, +//! and so on. +//! +//! ## _OmniTools_: User Journey +//! +//! All in all, the user journey of a team/builder, in the OmniNode world is as follows: +//! +//! * The [`templates`], most notably the [`parachain-template`] is the canonical starting point. +//! That being said, the node code of the templates (which may be eventually +//! removed/feature-gated) is no longer of relevance. The only focus is in the runtime, and +//! obtaining a `.wasm` file. References: +//! * [`crate::guides::your_first_pallet`] +//! * [`crate::guides::your_first_runtime`] +//! * If need be, the weights of the runtime need to be updated using `frame-omni-bencher`. +//! References: +//! * [`crate::reference_docs::frame_benchmarking_weight`] +//! * Next, [`chain-spec-builder`] is used to generate a `chain_spec.json`, either for development, +//! or for production. References: +//! * [`crate::reference_docs::chain_spec_genesis`] +//! * For local development, the following options are available: +//! * `polkadot-omni-node` (notably, with [`--dev-block-time`]). References: +//! * [`crate::guides::your_first_node`] +//! * External tools such as `chopsticks`, `zombienet`. +//! * See the `README.md` file of the `polkadot-sdk-parachain-template`. +//! * For production `polkadot-omni-node` can be used out of the box. +//! * For further customization [`polkadot_omni_node_lib`] can be used. +//! +//! ## Appendix +//! +//! This section describes how the interdependence between the node and the runtime is related to +//! the consensus engine. This information is useful for those who want to understand the +//! historical context of the node and the runtime. +//! +//! ### Consensus Engine +//! +//! In any given substrate-based chain, both the node and the runtime will have their own +//! opinion/information about what consensus engine is going to be used. +//! +//! In practice, the majority of the implementation of any consensus engine is in the node side, but +//! the runtime also typically needs to expose a custom runtime-api to enable the particular +//! consensus engine to work, and that particular runtime-api is implemented by a pallet +//! corresponding to that consensus engine. +//! +//! For example, taking a snippet from [`solochain_template_runtime`], the runtime has to provide +//! this additional runtime-api (compared to [`minimal_template_runtime`]), if the node software is +//! configured to use the Aura consensus engine: +//! +//! ```text +//! impl sp_consensus_aura::AuraApi for Runtime { +//! fn slot_duration() -> sp_consensus_aura::SlotDuration { +//! ... +//! } +//! fn authorities() -> Vec { +//! ... +//! } +//! } +//! ``` +//! +//! For simplicity, we can break down "consensus" into two main parts: +//! +//! * Block Authoring: Deciding who gets to produce the next block. +//! * Finality: Deciding when a block is considered final. +//! +//! For block authoring, there are a number of options: +//! +//! * [`sc_consensus_manual_seal`]: Useful for testing, where any node can produce a block at any +//! time. This is often combined with a fixed interval at which a block is produced. +//! * [`sc_consensus_aura`]/[`pallet_aura`]: A simple round-robin block authoring mechanism. +//! * [`sc_consensus_babe`]/[`pallet_babe`]: A more advanced block authoring mechanism, capable of +//! anonymizing the next block author. +//! * [`sc_consensus_pow`]: Proof of Work block authoring. +//! +//! For finality, there is one main option shipped with polkadot-sdk: +//! +//! * [`sc_consensus_grandpa`]/[`pallet_grandpa`]: A finality gadget that uses a voting mechanism to +//! decide when a block +//! +//! **The most important lesson here is that the node and the runtime must have matching consensus +//! components.** +//! +//! ### Consequences for OmniNode +//! +//! +//! The consequence of the above is that anyone using the OmniNode must also be aware of the +//! consensus system used in the runtime, and be aware if it is matching that of the OmniNode or +//! not. For the time being, [`polkadot-omni-node`] only supports: +//! +//! * Parachain-based Aura consensus, with 6s async-backing block-time, and before full elastic +//! scaling). [`polkadot_omni_node_lib::cli::Cli::experimental_use_slot_based`] for fixed factor +//! scaling (a step +//! * Ability to run any runtime with [`--dev-block-time`] flag. This uses +//! [`sc_consensus_manual_seal`] under the hood, and has no restrictions on the runtime's +//! consensus. +//! +//! [This](https://github.com/paritytech/polkadot-sdk/issues/5565) future improvement to OmniNode +//! aims to make such checks automatic. +//! +//! +//! [`templates`]: crate::polkadot_sdk::templates +//! [`parachain-template`]: https://github.com/paritytech/polkadot-sdk-parachain-template +//! [`--dev-block-time`]: polkadot_omni_node_lib::cli::Cli::dev_block_time +//! [`polkadot-omni-node`]: https://crates.io/crates/polkadot-omni-node +//! [`chain-spec-builder`]: https://crates.io/crates/staging-chain-spec-builder diff --git a/docs/sdk/src/reference_docs/umbrella_crate.rs b/docs/sdk/src/reference_docs/umbrella_crate.rs index 1074cde37693..8d9bcdfc2089 100644 --- a/docs/sdk/src/reference_docs/umbrella_crate.rs +++ b/docs/sdk/src/reference_docs/umbrella_crate.rs @@ -5,6 +5,7 @@ //! crate. This helps with selecting the right combination of crate versions, since otherwise 3rd //! party tools are needed to select a compatible set of versions. //! +//! //! ## Features //! //! The umbrella crate supports no-std builds and can therefore be used in the runtime and node. diff --git a/prdoc/pr_6094.prdoc b/prdoc/pr_6094.prdoc new file mode 100644 index 000000000000..23391c889155 --- /dev/null +++ b/prdoc/pr_6094.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Polkadot OmniNode Docs + +doc: + - audience: ... + description: | + Adds documentation in https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html and rust-docs for polkadot-omni-node project. + +crates: + - name: sp-genesis-builder + bump: patch + - name: pallet-aura + bump: patch + - name: polkadot-omni-node-lib + bump: patch + - name: polkadot-sdk-frame # since the crate is "experimental, we don't need to bump yet." + bump: major + - name: polkadot-omni-node + bump: patch diff --git a/substrate/bin/node/cli/src/cli.rs b/substrate/bin/node/cli/src/cli.rs index c0dcacb2e4b4..1d7001a5dccf 100644 --- a/substrate/bin/node/cli/src/cli.rs +++ b/substrate/bin/node/cli/src/cli.rs @@ -59,6 +59,7 @@ pub enum Subcommand { Inspect(node_inspect::cli::InspectCmd), /// Sub-commands concerned with benchmarking. + /// /// The pallet benchmarking moved to the `pallet` sub-command. #[command(subcommand)] Benchmark(frame_benchmarking_cli::BenchmarkCmd), diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 595fb5a19b04..2d0daf82997d 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -44,8 +44,10 @@ sp-offchain = { optional = true, workspace = true } sp-session = { optional = true, workspace = true } sp-consensus-aura = { optional = true, workspace = true } sp-consensus-grandpa = { optional = true, workspace = true } +sp-genesis-builder = { optional = true, workspace = true } sp-inherents = { optional = true, workspace = true } sp-storage = { optional = true, workspace = true } +sp-keyring = { optional = true, workspace = true } frame-executive = { optional = true, workspace = true } frame-system-rpc-runtime-api = { optional = true, workspace = true } @@ -73,7 +75,9 @@ runtime = [ "sp-block-builder", "sp-consensus-aura", "sp-consensus-grandpa", + "sp-genesis-builder", "sp-inherents", + "sp-keyring", "sp-offchain", "sp-session", "sp-storage", @@ -97,8 +101,10 @@ std = [ "sp-consensus-aura?/std", "sp-consensus-grandpa?/std", "sp-core/std", + "sp-genesis-builder?/std", "sp-inherents?/std", "sp-io/std", + "sp-keyring?/std", "sp-offchain?/std", "sp-runtime/std", "sp-session?/std", diff --git a/substrate/frame/aura/src/lib.rs b/substrate/frame/aura/src/lib.rs index f829578fb285..c74e864ea0d9 100644 --- a/substrate/frame/aura/src/lib.rs +++ b/substrate/frame/aura/src/lib.rs @@ -400,7 +400,9 @@ impl OnTimestampSet for Pallet { assert_eq!( CurrentSlot::::get(), timestamp_slot, - "Timestamp slot must match `CurrentSlot`" + "Timestamp slot must match `CurrentSlot`. This likely means that the configured block \ + time in the node and/or rest of the runtime is not compatible with Aura's \ + `SlotDuration`", ); } } diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index fcd96b40c3c4..ade1095cc504 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -222,7 +222,12 @@ pub mod runtime { // Types often used in the runtime APIs. pub use sp_core::OpaqueMetadata; + pub use sp_genesis_builder::{ + PresetId, Result as GenesisBuilderResult, DEV_RUNTIME_PRESET, + LOCAL_TESTNET_RUNTIME_PRESET, + }; pub use sp_inherents::{CheckInherentsResult, InherentData}; + pub use sp_keyring::AccountKeyring; pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; } @@ -246,6 +251,7 @@ pub mod runtime { pub use sp_block_builder::*; pub use sp_consensus_aura::*; pub use sp_consensus_grandpa::*; + pub use sp_genesis_builder::*; pub use sp_offchain::*; pub use sp_session::runtime_api::*; pub use sp_transaction_pool::runtime_api::*; @@ -396,8 +402,12 @@ pub mod deps { #[cfg(feature = "runtime")] pub use sp_consensus_grandpa; #[cfg(feature = "runtime")] + pub use sp_genesis_builder; + #[cfg(feature = "runtime")] pub use sp_inherents; #[cfg(feature = "runtime")] + pub use sp_keyring; + #[cfg(feature = "runtime")] pub use sp_offchain; #[cfg(feature = "runtime")] pub use sp_storage; diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 07317bc4cb52..4763aa06341e 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -17,17 +17,33 @@ #![cfg_attr(not(feature = "std"), no_std)] -//! Substrate genesis config builder +//! # Substrate genesis config builder. //! -//! For FRAME based runtimes, this runtime interface provides means to interact with -//! `RuntimeGenesisConfig`. Runtime provides a default `RuntimeGenesisConfig` structure in a form of -//! the JSON blob. +//! This crate contains [`GenesisBuilder`], a runtime-api to be implemented by runtimes, in order to +//! express their genesis state. //! -//! For non-FRAME runtimes this interface is intended to build genesis state of the runtime basing -//! on some input arbitrary bytes array. This documentation uses term `RuntimeGenesisConfig`, which -//! for non-FRAME runtimes may be understood as the runtime-side entity representing initial runtime -//! configuration. The representation of the preset is an arbitrary `Vec` and does not -//! necessarily have to represent a JSON blob. +//! The overall flow of the methods in [`GenesisBuilder`] is as follows: +//! +//! 1. [`GenesisBuilder::preset_names`]: A runtime exposes a number of different +//! `RuntimeGenesisConfig` variations, each of which is called a `preset`, and is identified by a +//! [`PresetId`]. All runtimes are encouraged to expose at least [`DEV_RUNTIME_PRESET`] and +//! [`LOCAL_TESTNET_RUNTIME_PRESET`] presets for consistency. +//! 2. [`GenesisBuilder::get_preset`]: Given a `PresetId`, this the runtime returns the JSON blob +//! representation of the `RuntimeGenesisConfig` for that preset. This JSON blob is often mixed +//! into the broader `chain_spec`. If `None` is given, [`GenesisBuilder::get_preset`] provides a +//! JSON represention of the default `RuntimeGenesisConfig` (by simply serializing the +//! `RuntimeGenesisConfig::default()` value into JSON format). This is used as a base for +//! applying patches / presets. + +//! 3. [`GenesisBuilder::build_state`]: Given a JSON blob, this method should deserialize it and +//! enact it (using `frame_support::traits::BuildGenesisConfig` for Frame-based runtime), +//! essentially writing it to the state. +//! +//! The first two flows are often done in between a runtime, and the `chain_spec_builder` binary. +//! The latter is used when a new blockchain is launched to enact and store the genesis state. See +//! the documentation of `chain_spec_builder` for more info. +//! +//! ## Patching //! //! The runtime may provide a number of partial predefined `RuntimeGenesisConfig` configurations in //! the form of patches which shall be applied on top of the default `RuntimeGenesisConfig`. The @@ -35,19 +51,22 @@ //! customized in the default runtime genesis config. These predefined configurations are referred //! to as presets. //! -//! This allows the runtime to provide a number of predefined configs (e.g. for different -//! testnets or development) without neccessity to leak the runtime types outside the itself (e.g. -//! node or chain-spec related tools). +//! This allows the runtime to provide a number of predefined configs (e.g. for different testnets +//! or development) without necessarily to leak the runtime types outside itself (e.g. node or +//! chain-spec related tools). +//! +//! ## FRAME vs. non-FRAME +//! +//! For FRAME based runtimes [`GenesisBuilder`] provides means to interact with +//! `RuntimeGenesisConfig`. +//! +//! For non-FRAME runtimes this interface is intended to build genesis state of the runtime basing +//! on some input arbitrary bytes array. This documentation uses term `RuntimeGenesisConfig`, which +//! for non-FRAME runtimes may be understood as the "runtime-side entity representing initial +//! runtime genesis configuration". The representation of the preset is an arbitrary `Vec` and +//! does not necessarily have to represent a JSON blob. //! -//! This Runtime API allows to interact with `RuntimeGenesisConfig`, in particular: -//! - provide the list of available preset names, -//! - provide a number of named presets of `RuntimeGenesisConfig`, -//! - provide a JSON represention of the default `RuntimeGenesisConfig` (by simply serializing the -//! default `RuntimeGenesisConfig` struct into JSON format), -//! - deserialize the full `RuntimeGenesisConfig` from given JSON blob and put the resulting -//! `RuntimeGenesisConfig` structure into the state storage creating the initial runtime's state. -//! Allows to build customized genesis. This operation internally calls `GenesisBuild::build` -//! function for all runtime pallets. +//! ## Genesis Block State //! //! Providing externalities with an empty storage and putting `RuntimeGenesisConfig` into storage //! (by calling `build_state`) allows to construct the raw storage of `RuntimeGenesisConfig` @@ -75,14 +94,15 @@ pub const DEV_RUNTIME_PRESET: &'static str = "development"; pub const LOCAL_TESTNET_RUNTIME_PRESET: &'static str = "local_testnet"; sp_api::decl_runtime_apis! { - /// API to interact with RuntimeGenesisConfig for the runtime + /// API to interact with `RuntimeGenesisConfig` for the runtime pub trait GenesisBuilder { /// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the /// storage. /// - /// In the case of a FRAME-based runtime, this function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and - /// puts it into the storage. If the provided JSON blob is incorrect or incomplete or the - /// deserialization fails, an error is returned. + /// In the case of a FRAME-based runtime, this function deserializes the full + /// `RuntimeGenesisConfig` from the given JSON blob and puts it into the storage. If the + /// provided JSON blob is incorrect or incomplete or the deserialization fails, an error + /// is returned. /// /// Please note that provided JSON blob must contain all `RuntimeGenesisConfig` fields, no /// defaults will be used. @@ -91,7 +111,7 @@ sp_api::decl_runtime_apis! { /// Returns a JSON blob representation of the built-in `RuntimeGenesisConfig` identified by /// `id`. /// - /// If `id` is `None` the function returns JSON blob representation of the default + /// If `id` is `None` the function should return JSON blob representation of the default /// `RuntimeGenesisConfig` struct of the runtime. Implementation must provide default /// `RuntimeGenesisConfig`. /// diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 464cad4e3da0..4f914a823bf6 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -324,7 +324,7 @@ impl_runtime_apis! { } fn preset_names() -> Vec { - self::genesis_config_presets::preset_names() + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs index 322c91f4f136..394bde0be770 100644 --- a/templates/parachain/runtime/src/genesis_config_presets.rs +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -76,6 +76,8 @@ fn local_testnet_genesis() -> Value { ], Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(), Sr25519Keyring::Alice.to_account_id(), + // TODO: this is super opaque, how should one know they should configure this? add to + // README! 1000.into(), ) } From 88f4306acac9b33695482ad2075d7790b234d94a Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 24 Oct 2024 12:47:44 +0300 Subject: [PATCH 31/58] Fix `zombienet-bridges-0001-asset-transfer-works` (#6175) Closes https://github.com/paritytech/polkadot-sdk/issues/6161 Westend BridgeHub freezes for a while at block 3 and if we try to init the bridge and fund the accounts during that time, it fails. So we wait untill all the parachains produced at least 10 blocks, in order to make sure that they work reliably. --- .gitlab/pipeline/zombienet/bridges.yml | 4 +--- .../rococo-westend/{rococo.zndsl => rococo-bridge.zndsl} | 0 .../environments/rococo-westend/rococo-start.zndsl | 8 ++++++++ bridges/testing/environments/rococo-westend/spawn.sh | 5 +++-- .../testing/environments/rococo-westend/start_relayer.sh | 4 ++-- .../{westend.zndsl => westend-bridge.zndsl} | 0 .../environments/rococo-westend/westend-start.zndsl | 8 ++++++++ 7 files changed, 22 insertions(+), 7 deletions(-) rename bridges/testing/environments/rococo-westend/{rococo.zndsl => rococo-bridge.zndsl} (100%) create mode 100644 bridges/testing/environments/rococo-westend/rococo-start.zndsl rename bridges/testing/environments/rococo-westend/{westend.zndsl => westend-bridge.zndsl} (100%) create mode 100644 bridges/testing/environments/rococo-westend/westend-start.zndsl diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 0472601a6a2b..07711e32a9a3 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -11,7 +11,7 @@ - if: $CI_COMMIT_REF_NAME =~ /^gh-readonly-queue.*$/ variables: DOCKER_IMAGES_VERSION: ${CI_COMMIT_SHORT_SHA} - - !reference [.build-refs, rules] + - !reference [ .build-refs, rules ] before_script: - echo "Zombienet Tests Config" - echo "${ZOMBIENET_IMAGE}" @@ -47,8 +47,6 @@ - cp -r /tmp/bridges-tests-run-*/bridge_hub_rococo_local_network/*.log ./zombienet-logs/ # copy logs of westend nodes - cp -r /tmp/bridges-tests-run-*/bridge_hub_westend_local_network/*.log ./zombienet-logs/ - tags: - - zombienet-polkadot-integration-test zombienet-bridges-0001-asset-transfer-works: extends: diff --git a/bridges/testing/environments/rococo-westend/rococo.zndsl b/bridges/testing/environments/rococo-westend/rococo-bridge.zndsl similarity index 100% rename from bridges/testing/environments/rococo-westend/rococo.zndsl rename to bridges/testing/environments/rococo-westend/rococo-bridge.zndsl diff --git a/bridges/testing/environments/rococo-westend/rococo-start.zndsl b/bridges/testing/environments/rococo-westend/rococo-start.zndsl new file mode 100644 index 000000000000..8c719b010df6 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/rococo-start.zndsl @@ -0,0 +1,8 @@ +Description: Check if the Rococo parachains started producing blocks reliably +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-rococo-collator1: reports block height is at least 10 within 180 seconds +bridge-hub-rococo-collator1: reports block height is at least 10 within 180 seconds + diff --git a/bridges/testing/environments/rococo-westend/spawn.sh b/bridges/testing/environments/rococo-westend/spawn.sh index a0ab00be1444..83b3b0720bb8 100755 --- a/bridges/testing/environments/rococo-westend/spawn.sh +++ b/bridges/testing/environments/rococo-westend/spawn.sh @@ -35,9 +35,11 @@ start_zombienet $TEST_DIR $westend_def westend_dir westend_pid echo if [[ $init -eq 1 ]]; then + run_zndsl ${BASH_SOURCE%/*}/rococo-start.zndsl $rococo_dir + run_zndsl ${BASH_SOURCE%/*}/westend-start.zndsl $westend_dir + rococo_init_log=$logs_dir/rococo-init.log echo -e "Setting up the rococo side of the bridge. Logs available at: $rococo_init_log\n" - westend_init_log=$logs_dir/westend-init.log echo -e "Setting up the westend side of the bridge. Logs available at: $westend_init_log\n" @@ -47,7 +49,6 @@ if [[ $init -eq 1 ]]; then westend_init_pid=$! wait -n $rococo_init_pid $westend_init_pid - $helper_script init-bridge-hub-rococo-local >> $rococo_init_log 2>&1 & rococo_init_pid=$! $helper_script init-bridge-hub-westend-local >> $westend_init_log 2>&1 & diff --git a/bridges/testing/environments/rococo-westend/start_relayer.sh b/bridges/testing/environments/rococo-westend/start_relayer.sh index 9c57e4a6ab6e..150fce035071 100755 --- a/bridges/testing/environments/rococo-westend/start_relayer.sh +++ b/bridges/testing/environments/rococo-westend/start_relayer.sh @@ -29,8 +29,8 @@ messages_relayer_log=$logs_dir/relayer_messages.log echo -e "Starting rococo-westend messages relayer. Logs available at: $messages_relayer_log\n" start_background_process "$helper_script run-messages-relay" $messages_relayer_log messages_relayer_pid -run_zndsl ${BASH_SOURCE%/*}/rococo.zndsl $rococo_dir -run_zndsl ${BASH_SOURCE%/*}/westend.zndsl $westend_dir +run_zndsl ${BASH_SOURCE%/*}/rococo-bridge.zndsl $rococo_dir +run_zndsl ${BASH_SOURCE%/*}/westend-bridge.zndsl $westend_dir eval $__finality_relayer_pid="'$finality_relayer_pid'" eval $__parachains_relayer_pid="'$parachains_relayer_pid'" diff --git a/bridges/testing/environments/rococo-westend/westend.zndsl b/bridges/testing/environments/rococo-westend/westend-bridge.zndsl similarity index 100% rename from bridges/testing/environments/rococo-westend/westend.zndsl rename to bridges/testing/environments/rococo-westend/westend-bridge.zndsl diff --git a/bridges/testing/environments/rococo-westend/westend-start.zndsl b/bridges/testing/environments/rococo-westend/westend-start.zndsl new file mode 100644 index 000000000000..fe587322edb6 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/westend-start.zndsl @@ -0,0 +1,8 @@ +Description: Check if the Westend parachains started producing blocks reliably +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-westend-collator1: reports block height is at least 10 within 180 seconds +bridge-hub-westend-collator1: reports block height is at least 10 within 180 seconds + From 6e31925a7673d69f2bc91903deb6ed78a6b99cc1 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 24 Oct 2024 12:14:02 +0200 Subject: [PATCH 32/58] [pallet-revive] fix hardcoded gas in tests (#6192) Fix hardcoded gas limits in tests --------- Co-authored-by: GitHub Action --- prdoc/pr_6192.prdoc | 7 ++++++ substrate/frame/revive/src/evm/runtime.rs | 30 +++++++++++++++++------ substrate/frame/revive/src/lib.rs | 3 ++- 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_6192.prdoc diff --git a/prdoc/pr_6192.prdoc b/prdoc/pr_6192.prdoc new file mode 100644 index 000000000000..cd9255486706 --- /dev/null +++ b/prdoc/pr_6192.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] fix hardcoded gas in tests' +doc: +- audience: Runtime Dev + description: Fix hardcoded gas limits in tests +crates: +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index e4340b27a187..4c3fdeca720c 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -356,12 +356,7 @@ pub trait EthExtra { Default::default(), ) .into(); - - log::debug!(target: LOG_TARGET, "Checking Ethereum transaction fees: - dispatch_info: {info:?} - encoded_len: {encoded_len:?} - fees: {actual_fee:?} - "); + log::debug!(target: LOG_TARGET, "try_into_checked_extrinsic: encoded_len: {encoded_len:?} actual_fee: {actual_fee:?} eth_fee: {eth_fee:?}"); if eth_fee < actual_fee { log::debug!(target: LOG_TARGET, "fees {eth_fee:?} too low for the extrinsic {actual_fee:?}"); @@ -490,11 +485,30 @@ mod test { } } + fn estimate_gas(&mut self) { + let dry_run = crate::Pallet::::bare_eth_transact( + Account::default().account_id(), + self.tx.to, + self.tx.value.try_into().unwrap(), + self.tx.input.clone().0, + Weight::MAX, + u64::MAX, + |call| { + let call = RuntimeCall::Contracts(call); + let uxt: Ex = sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into(); + uxt.encoded_size() as u32 + }, + crate::DebugInfo::Skip, + crate::CollectEvents::Skip, + ); + self.tx.gas = ((dry_run.fee + GAS_PRICE as u64) / (GAS_PRICE as u64)).into(); + } + /// Create a new builder with a call to the given address. fn call_with(dest: H160) -> Self { let mut builder = Self::new(); builder.tx.to = Some(dest); - builder.tx.gas = U256::from(516_708u128); + builder.estimate_gas(); builder } @@ -502,7 +516,7 @@ mod test { fn instantiate_with(code: Vec, data: Vec) -> Self { let mut builder = Self::new(); builder.tx.input = Bytes(code.into_iter().chain(data.into_iter()).collect()); - builder.tx.gas = U256::from(1_035_070u128); + builder.estimate_gas(); builder } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 9b0bbb2d6bcb..11752e47cf2a 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -1212,6 +1212,7 @@ where to: Some(dest), ..Default::default() }; + let eth_dispatch_call = crate::Call::::eth_transact { payload: tx.dummy_signed_payload(), gas_limit: result.gas_required, @@ -1238,7 +1239,7 @@ where ) .into(); - log::debug!(target: LOG_TARGET, "Call dry run Result: dispatch_info: {dispatch_info:?} len: {encoded_len:?} fee: {fee:?}"); + log::debug!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}"); EthContractResult { gas_required: result.gas_required, storage_deposit: result.storage_deposit.charge_or_zero(), From 79ec5ae08a38e7e709166d14f7204c593416120a Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 24 Oct 2024 15:21:52 +0300 Subject: [PATCH 33/58] Added Trusted Query API implementation for Westend and Rococo relay chains (#6212) Added missing API methods to Rococo and Westend parachains. Preparatory work for making chopstick tests run smoothly. Follow-up of [PR#6039](https://github.com/paritytech/polkadot-sdk/pull/6039) --- polkadot/runtime/rococo/src/lib.rs | 14 ++++++++++++- polkadot/runtime/westend/src/lib.rs | 14 ++++++++++++- prdoc/pr_6212.prdoc | 32 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_6212.prdoc diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e94b6666ed07..44dd820f8c3c 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -112,7 +112,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; +use xcm::{ + latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; @@ -2622,6 +2625,15 @@ sp_api::impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_teleporter(asset, location) + } + } } #[cfg(all(test, feature = "try-runtime"))] diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 461be186ee51..a91ca399db41 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -107,7 +107,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; +use xcm::{ + latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; use xcm_runtime_apis::{ @@ -2791,4 +2794,13 @@ sp_api::impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_teleporter(asset, location) + } + } } diff --git a/prdoc/pr_6212.prdoc b/prdoc/pr_6212.prdoc new file mode 100644 index 000000000000..97f522025d1e --- /dev/null +++ b/prdoc/pr_6212.prdoc @@ -0,0 +1,32 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Added Trusted Query API calls for Westend and Rococo chains" + +doc: + - audience: Runtime Dev + description: | + Added is_trusted_reserve and is_trusted_teleporter API calls to relay chains. + Given an asset and a location, they return if the chain trusts that location as a reserve or teleporter for that asset respectively. + You can implement them on your runtime by simply calling a helper function on `pallet-xcm`. + ```rust + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } + ``` + + - audience: Runtime User + description: | + There's a new runtime API to check if a chain trust a Location as a reserve or teleporter for a given Asset. + It's implemented in all the relays and system parachains in Westend and Rococo. + +crates: + - name: westend-runtime + bump: minor + - name: rococo-runtime + bump: minor From 4f6ec7c12d17529c00aff630374833a3a5be8b82 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 24 Oct 2024 14:27:01 +0200 Subject: [PATCH 34/58] Snowbridge: PNA Audit Better Documentation and minor Refactorings (#6216) # Description Snowbridge PNA has been audited. A number of issues where raised due to not understanding the fee model for Polkadot Native Assets(PNA) implementation. This PR addresses this by adding more comments and better naming of private functions. ## Integration None, documentation and private method name changes. --- .../primitives/router/src/inbound/mod.rs | 2 ++ .../primitives/router/src/outbound/mod.rs | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index fbfc52d01c83..a9324ac42470 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -416,6 +416,8 @@ where // Final destination is a 32-byte account on AssetHub Destination::AccountId32 { id } => Ok(Location::new(0, [AccountId32 { network: None, id }])), + // Forwarding to a destination parachain is not allowed for PNA and is validated on the + // Ethereum side. https://github.com/Snowfork/snowbridge/blob/e87ddb2215b513455c844463a25323bb9c01ff36/contracts/src/Assets.sol#L216-L224 _ => Err(ConvertMessageError::InvalidDestination), }?; diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index efc1ef56f304..3b5dbdb77c89 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -207,9 +207,9 @@ where fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { let result = match self.peek() { - Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + Ok(ReserveAssetDeposited { .. }) => self.make_mint_foreign_token_command(), // Get withdraw/deposit and make native tokens create message. - Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Ok(WithdrawAsset { .. }) => self.make_unlock_native_token_command(), Err(e) => Err(e), _ => return Err(XcmConverterError::UnexpectedInstruction), }?; @@ -222,7 +222,9 @@ where Ok(result) } - fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + fn make_unlock_native_token_command( + &mut self, + ) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -271,7 +273,12 @@ where ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - // If there was a fee specified verify it. + // Fees are collected on AH, up front and directly from the user, to cover the + // complete cost of the transfer. Any additional fees provided in the XCM program are + // refunded to the beneficiary. We only validate the fee here if its provided to make sure + // the XCM program is well formed. Another way to think about this from an XCM perspective + // would be that the user offered to pay X amount in fees, but we charge 0 of that X amount + // (no fee) and refund X to the user. if let Some(fee_asset) = fee_asset { // The fee asset must be the same as the reserve asset. if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { @@ -328,7 +335,9 @@ where /// # BuyExecution /// # DepositAsset /// # SetTopic - fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + fn make_mint_foreign_token_command( + &mut self, + ) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets. @@ -377,7 +386,12 @@ where ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - // If there was a fee specified verify it. + // Fees are collected on AH, up front and directly from the user, to cover the + // complete cost of the transfer. Any additional fees provided in the XCM program are + // refunded to the beneficiary. We only validate the fee here if its provided to make sure + // the XCM program is well formed. Another way to think about this from an XCM perspective + // would be that the user offered to pay X amount in fees, but we charge 0 of that X amount + // (no fee) and refund X to the user. if let Some(fee_asset) = fee_asset { // The fee asset must be the same as the reserve asset. if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { From 83cd90eb73a9a9633856fd38be329ab664997199 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:01:12 +0300 Subject: [PATCH 35/58] Enable approval-voting-parallel by default on kusama (#6218) The approval-voting-parallel introduced with https://github.com/paritytech/polkadot-sdk/pull/4849 has been tested on `versi` and approximately 3 weeks on parity's existing kusama nodes https://github.com/paritytech/devops/issues/3583, things worked as expected, so enable it by default on all kusama nodes in the next release. The next step will be enabling by default on polkadot if no issue arrises while running on kusama. --------- Signed-off-by: Alexandru Gheorghe --- polkadot/node/service/src/lib.rs | 9 ++++----- prdoc/pr_6218.prdoc | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 prdoc/pr_6218.prdoc diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index da3ab760ed22..d6f24159e1da 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -759,13 +759,12 @@ pub fn new_full< Some(backoff) }; - // Running approval voting in parallel is enabled by default on all networks except Polkadot and - // Kusama, unless explicitly enabled by the commandline option. + // Running approval voting in parallel is enabled by default on all networks except Polkadot + // unless explicitly enabled by the commandline option. // This is meant to be temporary until we have enough confidence in the new system to enable it // by default on all networks. - let enable_approval_voting_parallel = (!config.chain_spec.is_kusama() && - !config.chain_spec.is_polkadot()) || - enable_approval_voting_parallel; + let enable_approval_voting_parallel = + !config.chain_spec.is_polkadot() || enable_approval_voting_parallel; let disable_grandpa = config.disable_grandpa; let name = config.network.node_name.clone(); diff --git a/prdoc/pr_6218.prdoc b/prdoc/pr_6218.prdoc new file mode 100644 index 000000000000..5c97c926f238 --- /dev/null +++ b/prdoc/pr_6218.prdoc @@ -0,0 +1,9 @@ +title: Enable approval-voting-parallel by default on kusama + +doc: + - audience: Node Dev + description: | + Enable approval-voting-parallel by default on kusama +crates: + - name: polkadot-service + bump: patch From 1cb818b7546b8b18ba88f0455ace2159c5395ca0 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Thu, 24 Oct 2024 16:33:59 +0200 Subject: [PATCH 36/58] pallet macro: Support instantiable pallets in tasks (#5194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/paritytech/polkadot-sdk/issues/5185 also implement handling of attr in expansion in construct-runtime --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- prdoc/pr_5194.prdoc | 11 ++ .../src/construct_runtime/expand/task.rs | 57 +++++-- .../procedural/src/pallet/expand/tasks.rs | 155 +++++++----------- .../src/pallet/expand/tt_default_parts.rs | 4 +- .../procedural/src/pallet/parse/mod.rs | 7 +- .../procedural/src/pallet/parse/tasks.rs | 34 ++-- .../test/tests/pallet_ui/pass/task_valid.rs | 26 +++ .../test/tests/pallet_ui/task_invalid_gen.rs | 39 +++++ .../tests/pallet_ui/task_invalid_gen.stderr | 5 + .../test/tests/pallet_ui/task_invalid_gen2.rs | 39 +++++ .../tests/pallet_ui/task_invalid_gen2.stderr | 13 ++ substrate/frame/support/test/tests/tasks.rs | 135 +++++++++++++++ 12 files changed, 392 insertions(+), 133 deletions(-) create mode 100644 prdoc/pr_5194.prdoc create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr create mode 100644 substrate/frame/support/test/tests/tasks.rs diff --git a/prdoc/pr_5194.prdoc b/prdoc/pr_5194.prdoc new file mode 100644 index 000000000000..afb9d57e79e3 --- /dev/null +++ b/prdoc/pr_5194.prdoc @@ -0,0 +1,11 @@ +title: "FRAME: Support instantiable pallets in tasks." + +doc: + - audience: Runtime Dev + description: | + In FRAME, tasks can now be used in instantiable pallet. Also some fix for expansion with + conditional compilation in construct runtine. + +crates: + - name: frame-support-procedural + bump: patch diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs index 6531c0e9e070..1302f86455f2 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs @@ -16,6 +16,7 @@ // limitations under the License use crate::construct_runtime::Pallet; +use core::str::FromStr; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::quote; @@ -28,7 +29,8 @@ pub fn expand_outer_task( let mut from_impls = Vec::new(); let mut task_variants = Vec::new(); let mut variant_names = Vec::new(); - let mut task_paths = Vec::new(); + let mut task_types = Vec::new(); + let mut cfg_attrs = Vec::new(); for decl in pallet_decls { if decl.find_part("Task").is_none() { continue @@ -37,18 +39,31 @@ pub fn expand_outer_task( let variant_name = &decl.name; let path = &decl.path; let index = decl.index; + let instance = decl.instance.as_ref().map(|instance| quote!(, #path::#instance)); + let task_type = quote!(#path::Task<#runtime_name #instance>); + + let attr = decl.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); from_impls.push(quote! { - impl From<#path::Task<#runtime_name>> for RuntimeTask { - fn from(hr: #path::Task<#runtime_name>) -> Self { + #attr + impl From<#task_type> for RuntimeTask { + fn from(hr: #task_type) -> Self { RuntimeTask::#variant_name(hr) } } - impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + #attr + impl TryInto<#task_type> for RuntimeTask { type Error = (); - fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + fn try_into(self) -> Result<#task_type, Self::Error> { match self { RuntimeTask::#variant_name(hr) => Ok(hr), _ => Err(()), @@ -58,13 +73,16 @@ pub fn expand_outer_task( }); task_variants.push(quote! { + #attr #[codec(index = #index)] - #variant_name(#path::Task<#runtime_name>), + #variant_name(#task_type), }); variant_names.push(quote!(#variant_name)); - task_paths.push(quote!(#path::Task)); + task_types.push(task_type); + + cfg_attrs.push(attr); } let prelude = quote!(#scrate::traits::tasks::__private); @@ -91,35 +109,50 @@ pub fn expand_outer_task( fn is_valid(&self) -> bool { match self { - #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.is_valid(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { match self { - #(RuntimeTask::#variant_names(val) => val.run(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.run(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn weight(&self) -> #scrate::pallet_prelude::Weight { match self { - #(RuntimeTask::#variant_names(val) => val.weight(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.weight(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn task_index(&self) -> u32 { match self { - #(RuntimeTask::#variant_names(val) => val.task_index(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.task_index(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn iter() -> Self::Enumeration { let mut all_tasks = Vec::new(); - #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + #( + #cfg_attrs + all_tasks.extend(<#task_types>::iter().map(RuntimeTask::from).collect::>()); + )* all_tasks.into_iter() } } diff --git a/substrate/frame/support/procedural/src/pallet/expand/tasks.rs b/substrate/frame/support/procedural/src/pallet/expand/tasks.rs index 7201c352d92c..b6346ca8ff34 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/tasks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/tasks.rs @@ -20,21 +20,25 @@ //! Home of the expansion code for the Tasks API use crate::pallet::{parse::tasks::*, Def}; -use derive_syn_parse::Parse; use inflector::Inflector; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote, ToTokens}; -use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; +use syn::{parse_quote_spanned, spanned::Spanned}; impl TaskEnumDef { /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the /// event they _don't_ specify one (which is actually the most common behavior) we have to /// generate one based on the existing [`TasksDef`]. This method performs that generation. - pub fn generate( - tasks: &TasksDef, - type_decl_bounded_generics: TokenStream2, - type_use_generics: TokenStream2, - ) -> Self { + pub fn generate(tasks: &TasksDef, def: &Def) -> Self { + // We use the span of the attribute to indicate that the error comes from code generated + // for the specific section, otherwise the item impl. + let span = tasks + .tasks_attr + .as_ref() + .map_or_else(|| tasks.item_impl.span(), |attr| attr.span()); + + let type_decl_bounded_generics = def.type_decl_bounded_generics(span); + let variants = if tasks.tasks_attr.is_some() { tasks .tasks @@ -58,7 +62,8 @@ impl TaskEnumDef { } else { Vec::new() }; - let mut task_enum_def: TaskEnumDef = parse_quote! { + + parse_quote_spanned! { span => /// Auto-generated enum that encapsulates all tasks defined by this pallet. /// /// Conceptually similar to the [`Call`] enum, but for tasks. This is only @@ -69,33 +74,32 @@ impl TaskEnumDef { #variants, )* } - }; - task_enum_def.type_use_generics = type_use_generics; - task_enum_def + } } } -impl ToTokens for TaskEnumDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let item_enum = &self.item_enum; - let ident = &item_enum.ident; - let vis = &item_enum.vis; - let attrs = &item_enum.attrs; - let generics = &item_enum.generics; - let variants = &item_enum.variants; - let scrate = &self.scrate; - let type_use_generics = &self.type_use_generics; - if self.attr.is_some() { +impl TaskEnumDef { + fn expand_to_tokens(&self, def: &Def) -> TokenStream2 { + if let Some(attr) = &self.attr { + let ident = &self.item_enum.ident; + let vis = &self.item_enum.vis; + let attrs = &self.item_enum.attrs; + let generics = &self.item_enum.generics; + let variants = &self.item_enum.variants; + let frame_support = &def.frame_support; + let type_use_generics = &def.type_use_generics(attr.span()); + let type_impl_generics = &def.type_impl_generics(attr.span()); + // `item_enum` is short-hand / generated enum - tokens.extend(quote! { + quote! { #(#attrs)* #[derive( - #scrate::CloneNoBound, - #scrate::EqNoBound, - #scrate::PartialEqNoBound, - #scrate::pallet_prelude::Encode, - #scrate::pallet_prelude::Decode, - #scrate::pallet_prelude::TypeInfo, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::pallet_prelude::Encode, + #frame_support::pallet_prelude::Decode, + #frame_support::pallet_prelude::TypeInfo, )] #[codec(encode_bound())] #[codec(decode_bound())] @@ -104,32 +108,25 @@ impl ToTokens for TaskEnumDef { #variants #[doc(hidden)] #[codec(skip)] - __Ignore(core::marker::PhantomData, #scrate::Never), + __Ignore(core::marker::PhantomData<(#type_use_generics)>, #frame_support::Never), } - impl core::fmt::Debug for #ident<#type_use_generics> { + impl<#type_impl_generics> core::fmt::Debug for #ident<#type_use_generics> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct(stringify!(#ident)).field("value", self).finish() } } - }); + } } else { // `item_enum` is a manually specified enum (no attribute) - tokens.extend(item_enum.to_token_stream()); + self.item_enum.to_token_stream() } } } -/// Represents an already-expanded [`TasksDef`]. -#[derive(Parse)] -pub struct ExpandedTasksDef { - pub task_item_impl: ItemImpl, - pub task_trait_impl: ItemImpl, -} - -impl ToTokens for TasksDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let scrate = &self.scrate; +impl TasksDef { + fn expand_to_tokens(&self, def: &Def) -> TokenStream2 { + let frame_support = &def.frame_support; let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); let enum_arguments = &self.enum_arguments; let enum_use = quote!(#enum_ident #enum_arguments); @@ -160,21 +157,21 @@ impl ToTokens for TasksDef { let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); let impl_generics = &self.item_impl.generics; - tokens.extend(quote! { + quote! { impl #impl_generics #enum_use { #(#task_fn_impls)* } - impl #impl_generics #scrate::traits::Task for #enum_use + impl #impl_generics #frame_support::traits::Task for #enum_use { - type Enumeration = #scrate::__private::IntoIter<#enum_use>; + type Enumeration = #frame_support::__private::IntoIter<#enum_use>; fn iter() -> Self::Enumeration { - let mut all_tasks = #scrate::__private::vec![]; + let mut all_tasks = #frame_support::__private::vec![]; #(all_tasks .extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* }) - .collect::<#scrate::__private::Vec<_>>()); + .collect::<#frame_support::__private::Vec<_>>()); )* all_tasks.into_iter() } @@ -193,7 +190,7 @@ impl ToTokens for TasksDef { } } - fn run(&self) -> Result<(), #scrate::pallet_prelude::DispatchError> { + fn run(&self) -> Result<(), #frame_support::pallet_prelude::DispatchError> { match self.clone() { #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => { <#enum_use>::#task_fn_names(#( #task_arg_names, )* ) @@ -203,64 +200,32 @@ impl ToTokens for TasksDef { } #[allow(unused_variables)] - fn weight(&self) -> #scrate::pallet_prelude::Weight { + fn weight(&self) -> #frame_support::pallet_prelude::Weight { match self.clone() { #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)* Task::__Ignore(_, _) => unreachable!(), } } } - }); + } } } -/// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. -/// -/// This modifies the underlying [`Def`] in addition to returning any tokens that were added. -pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { - let Some(tasks) = &mut def.tasks else { return quote!() }; - let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); - quote! { - #task_item_impl - #task_trait_impl - } -} +/// Generate code related to tasks. +pub fn expand_tasks(def: &Def) -> TokenStream2 { + let Some(tasks_def) = &def.tasks else { + return quote!(); + }; -/// Represents a fully-expanded [`TaskEnumDef`]. -#[derive(Parse)] -pub struct ExpandedTaskEnum { - pub item_enum: ItemEnum, - pub debug_impl: ItemImpl, -} + let default_task_enum = TaskEnumDef::generate(&tasks_def, def); -/// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns -/// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated -/// or defined. -pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { - let Some(task_enum) = &mut def.task_enum else { return quote!() }; - let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); - quote! { - #item_enum - #debug_impl - } -} + let task_enum = def.task_enum.as_ref().unwrap_or_else(|| &default_task_enum); + + let tasks_expansion = tasks_def.expand_to_tokens(def); + let task_enum_expansion = task_enum.expand_to_tokens(def); -/// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a -/// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. -pub fn expand_tasks(def: &mut Def) -> TokenStream2 { - if let Some(tasks_def) = &def.tasks { - if def.task_enum.is_none() { - def.task_enum = Some(TaskEnumDef::generate( - &tasks_def, - def.type_decl_bounded_generics(tasks_def.item_impl.span()), - def.type_use_generics(tasks_def.item_impl.span()), - )); - } - } - let tasks_extra_output = expand_tasks_impl(def); - let task_enum_extra_output = expand_task_enum(def); quote! { - #tasks_extra_output - #task_enum_extra_output + #tasks_expansion + #task_enum_expansion } } diff --git a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 1975f059152c..6d53de3133e8 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -33,7 +33,7 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); - let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + let task_part = def.tasks.as_ref().map(|_| quote::quote!(Task,)); let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); @@ -85,7 +85,7 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); - let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + let task_part_v2 = def.tasks.as_ref().map(|_| quote::quote!(+ Task)); let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index 5036f691690f..c9a150effccb 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -126,11 +126,11 @@ impl Def { }, Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() => call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?), - Some(PalletAttr::Tasks(_)) if tasks.is_none() => { + Some(PalletAttr::Tasks(span)) if tasks.is_none() => { let item_tokens = item.to_token_stream(); // `TasksDef::parse` needs to know if attr was provided so we artificially // re-insert it here - tasks = Some(syn::parse2::(quote::quote! { + tasks = Some(syn::parse2::(quote::quote_spanned! { span => #[pallet::tasks_experimental] #item_tokens })?); @@ -404,6 +404,9 @@ impl Def { if let Some(extra_constants) = &self.extra_constants { instances.extend_from_slice(&extra_constants.instances[..]); } + if let Some(task_enum) = &self.task_enum { + instances.push(task_enum.instance_usage.clone()); + } let mut errors = instances.into_iter().filter_map(|instances| { if instances.has_instance == self.config.has_instance { diff --git a/substrate/frame/support/procedural/src/pallet/parse/tasks.rs b/substrate/frame/support/procedural/src/pallet/parse/tasks.rs index ed860849a4db..5bff64643df1 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/tasks.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/tasks.rs @@ -25,8 +25,8 @@ use crate::assert_parse_error_matches; #[cfg(test)] use crate::pallet::parse::tests::simulate_manifest_dir; +use super::helper; use derive_syn_parse::Parse; -use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{ @@ -34,8 +34,8 @@ use syn::{ parse2, spanned::Spanned, token::{Bracket, Paren, PathSep, Pound}, - Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, PathArguments, - Result, TypePath, + Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, PathArguments, Result, + TypePath, }; pub mod keywords { @@ -57,8 +57,6 @@ pub struct TasksDef { pub tasks_attr: Option, pub tasks: Vec, pub item_impl: ItemImpl, - /// Path to `frame_support` - pub scrate: Path, pub enum_ident: Ident, pub enum_arguments: PathArguments, } @@ -114,11 +112,7 @@ impl syn::parse::Parse for TasksDef { let enum_ident = last_seg.ident.clone(); let enum_arguments = last_seg.arguments.clone(); - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) + Ok(TasksDef { tasks_attr, item_impl, tasks, enum_ident, enum_arguments }) } } @@ -146,12 +140,11 @@ pub type PalletTaskEnumAttr = PalletTaskAttr; /// Parsing for a manually-specified (or auto-generated) task enum, optionally including the /// attached `#[pallet::task_enum]` attribute. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct TaskEnumDef { pub attr: Option, pub item_enum: ItemEnum, - pub scrate: Path, - pub type_use_generics: TokenStream2, + pub instance_usage: helper::InstanceUsage, } impl syn::parse::Parse for TaskEnumDef { @@ -163,13 +156,10 @@ impl syn::parse::Parse for TaskEnumDef { None => None, }; - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - let type_use_generics = quote!(T); + let instance_usage = + helper::check_type_def_gen(&item_enum.generics, item_enum.ident.span())?; - Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) + Ok(TaskEnumDef { attr, item_enum, instance_usage }) } } @@ -896,7 +886,7 @@ fn test_parse_task_enum_def_non_task_name() { simulate_manifest_dir("../../examples/basic", || { parse2::(quote! { #[pallet::task_enum] - pub enum Something { + pub enum Something { Foo } }) @@ -921,7 +911,7 @@ fn test_parse_task_enum_def_missing_attr_allowed() { fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { simulate_manifest_dir("../../examples/basic", || { parse2::(quote! { - pub enum Foo { + pub enum Foo { Red, } }) @@ -951,7 +941,7 @@ fn test_parse_task_enum_def_wrong_item() { assert_parse_error_matches!( parse2::(quote! { #[pallet::task_enum] - pub struct Something; + pub struct Something; }), "expected `enum`" ); diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs b/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs index 234e220f49d8..bc66c09de7e8 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs @@ -39,5 +39,31 @@ mod pallet { } } +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(_i: u32, _j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::get(); + Ok(()) + } + } +} + fn main() { } diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs new file mode 100644 index 000000000000..52ae19dcb02d --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::task_enum] + pub enum Task {} + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for Task {} +} + +fn main() { +} diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr new file mode 100644 index 000000000000..1dc9e3d4aa11 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr @@ -0,0 +1,5 @@ +error: Invalid generic declaration, trait is defined with instance but generic use none + --> tests/pallet_ui/task_invalid_gen.rs:32:11 + | +32 | pub enum Task {} + | ^^^^ diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs new file mode 100644 index 000000000000..56392cbad2dc --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::task_enum] + pub enum Task {} + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for Task {} +} + +fn main() { +} diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr new file mode 100644 index 000000000000..448825e60155 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr @@ -0,0 +1,13 @@ +error: Invalid type def generics: expected `T` or `T: Config` or `T, I = ()` or `T: Config, I: 'static = ()` + --> tests/pallet_ui/task_invalid_gen2.rs:32:11 + | +32 | pub enum Task {} + | ^^^^ + +error: unexpected end of input, expected `T` + --> tests/pallet_ui/task_invalid_gen2.rs:18:1 + | +18 | #[frame_support::pallet(dev_mode)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/tasks.rs b/substrate/frame/support/test/tests/tasks.rs new file mode 100644 index 000000000000..97e58388362b --- /dev/null +++ b/substrate/frame/support/test/tests/tasks.rs @@ -0,0 +1,135 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "experimental")] + +#[frame_support::pallet(dev_mode)] +mod my_pallet { + use frame_support::pallet_prelude::{StorageValue, ValueQuery}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, (u32, u64), ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(i: u32, j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::put((i, j)); + Ok(()) + } + } +} + +// Another pallet for which we won't implement the default instance. +#[frame_support::pallet(dev_mode)] +mod my_pallet_2 { + use frame_support::pallet_prelude::{StorageValue, ValueQuery}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, (u32, u64), ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(i: u32, j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::put((i, j)); + Ok(()) + } + } +} + +type BlockNumber = u32; +type AccountId = u64; +type Header = sp_runtime::generic::Header; +type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +type Block = sp_runtime::generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime + { + System: frame_system, + MyPallet: my_pallet, + MyPallet2: my_pallet::, + #[cfg(feature = "frame-feature-testing")] + MyPallet3: my_pallet::, + MyPallet4: my_pallet_2::, + } +); + +// NOTE: Needed for derive_impl expansion +use frame_support::derive_impl; +#[frame_support::derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type AccountId = AccountId; +} + +impl my_pallet::Config for Runtime {} + +impl my_pallet::Config for Runtime {} + +#[cfg(feature = "frame-feature-testing")] +impl my_pallet::Config for Runtime {} + +impl my_pallet_2::Config for Runtime {} + +fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + + RuntimeGenesisConfig::default().build_storage().unwrap().into() +} + +#[test] +fn tasks_work() { + new_test_ext().execute_with(|| { + use frame_support::instances::{Instance1, Instance2}; + + let task = RuntimeTask::MyPallet(my_pallet::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet::SomeStorage::::get(), (0, 2)); + + let task = RuntimeTask::MyPallet2(my_pallet::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet::SomeStorage::::get(), (0, 2)); + + let task = + RuntimeTask::MyPallet4(my_pallet_2::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet_2::SomeStorage::::get(), (0, 2)); + }); +} From 4a867ab397130800fd034e8d523e7236d81c7b22 Mon Sep 17 00:00:00 2001 From: Javier Viola <363911+pepoviola@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:58:17 -0300 Subject: [PATCH 37/58] Disable tests reported in #6062 (#6064) Flaky tests reported in #6062 #6063 (already fixed) Thx! --- .gitlab/pipeline/zombienet/polkadot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 60870caf26c8..9a907d8d9946 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -158,7 +158,7 @@ zombienet-polkadot-functional-0010-validator-disabling: --local-dir="${LOCAL_DIR}/functional" --test="0010-validator-disabling.zndsl" -zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: +.zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: extends: - .zombienet-polkadot-common script: From ac8f90739d1225a386c6b85b4cb181c028d9d1b0 Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Fri, 25 Oct 2024 04:27:55 +0800 Subject: [PATCH 38/58] Fix a tiny typo (#6229) Just fix a tiny typo --- templates/solochain/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 42361a2ff360..f2eb49592be9 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -146,7 +146,7 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `TransactionExtension`` to the basic transaction logic. +/// The `TransactionExtension` to the basic transaction logic. pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, From 197faa802669c6ae74972dc3cc1f29c02d884bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 25 Oct 2024 00:53:03 +0200 Subject: [PATCH 39/58] pallet-message-queue: Fix max message size calculation (#6205) The max size of a message should not depend on the weight left in a given execution context. Instead the max message size depends on the service weights configured for the pallet. A message that may does not fit into `on_idle` is not automatically overweight, because it may can be executed successfully in `on_initialize` or in another block in `on_idle` when there is more weight left. --------- Co-authored-by: GitHub Action --- prdoc/pr_6205.prdoc | 8 ++ substrate/frame/message-queue/src/lib.rs | 49 ++++++++++- substrate/frame/message-queue/src/mock.rs | 2 +- substrate/frame/message-queue/src/tests.rs | 99 ++++++++++++---------- 4 files changed, 106 insertions(+), 52 deletions(-) create mode 100644 prdoc/pr_6205.prdoc diff --git a/prdoc/pr_6205.prdoc b/prdoc/pr_6205.prdoc new file mode 100644 index 000000000000..0874eb468db4 --- /dev/null +++ b/prdoc/pr_6205.prdoc @@ -0,0 +1,8 @@ +title: 'pallet-message-queue: Fix max message size calculation' +doc: +- audience: Runtime Dev + description: |- + The max size of a message should not depend on the weight left in a given execution context. Instead the max message size depends on the service weights configured for the pallet. A message that may does not fit into `on_idle` is not automatically overweight, because it may can be executed successfully in `on_initialize` or in another block in `on_idle` when there is more weight left. +crates: +- name: pallet-message-queue + bump: patch diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 31402f2a9d81..04620fa88d85 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -868,13 +868,26 @@ impl Pallet { } } - /// The maximal weight that a single message can consume. + /// The maximal weight that a single message ever can consume. /// /// Any message using more than this will be marked as permanently overweight and not /// automatically re-attempted. Returns `None` if the servicing of a message cannot begin. /// `Some(0)` means that only messages with no weight may be served. fn max_message_weight(limit: Weight) -> Option { - limit.checked_sub(&Self::single_msg_overhead()) + let service_weight = T::ServiceWeight::get().unwrap_or_default(); + let on_idle_weight = T::IdleMaxServiceWeight::get().unwrap_or_default(); + + // Whatever weight is set, the one with the biggest one is used as the maximum weight. If a + // message is tried in one context and fails, it will be retried in the other context later. + let max_message_weight = + if service_weight.any_gt(on_idle_weight) { service_weight } else { on_idle_weight }; + + if max_message_weight.is_zero() { + // If no service weight is set, we need to use the given limit as max message weight. + limit.checked_sub(&Self::single_msg_overhead()) + } else { + max_message_weight.checked_sub(&Self::single_msg_overhead()) + } } /// The overhead of servicing a single message. @@ -896,6 +909,8 @@ impl Pallet { fn do_integrity_test() -> Result<(), String> { ensure!(!MaxMessageLenOf::::get().is_zero(), "HeapSize too low"); + let max_block = T::BlockWeights::get().max_block; + if let Some(service) = T::ServiceWeight::get() { if Self::max_message_weight(service).is_none() { return Err(format!( @@ -904,6 +919,31 @@ impl Pallet { Self::single_msg_overhead(), )) } + + if service.any_gt(max_block) { + return Err(format!( + "ServiceWeight {service} is bigger than max block weight {max_block}" + )) + } + } + + if let Some(on_idle) = T::IdleMaxServiceWeight::get() { + if on_idle.any_gt(max_block) { + return Err(format!( + "IdleMaxServiceWeight {on_idle} is bigger than max block weight {max_block}" + )) + } + } + + if let (Some(service_weight), Some(on_idle)) = + (T::ServiceWeight::get(), T::IdleMaxServiceWeight::get()) + { + if !(service_weight.all_gt(on_idle) || + on_idle.all_gt(service_weight) || + service_weight == on_idle) + { + return Err("One of `ServiceWeight` or `IdleMaxServiceWeight` needs to be `all_gt` or both need to be equal.".into()) + } } Ok(()) @@ -1531,7 +1571,7 @@ impl Pallet { let mut weight = WeightMeter::with_limit(weight_limit); // Get the maximum weight that processing a single message may take: - let max_weight = Self::max_message_weight(weight_limit).unwrap_or_else(|| { + let overweight_limit = Self::max_message_weight(weight_limit).unwrap_or_else(|| { if matches!(context, ServiceQueuesContext::OnInitialize) { defensive!("Not enough weight to service a single message."); } @@ -1549,7 +1589,8 @@ impl Pallet { let mut last_no_progress = None; loop { - let (progressed, n) = Self::service_queue(next.clone(), &mut weight, max_weight); + let (progressed, n) = + Self::service_queue(next.clone(), &mut weight, overweight_limit); next = match n { Some(n) => if !progressed { diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index d3f719c62356..f1d341d1a5db 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -42,7 +42,7 @@ impl frame_system::Config for Test { type Block = Block; } parameter_types! { - pub const HeapSize: u32 = 24; + pub const HeapSize: u32 = 40; pub const MaxStale: u32 = 2; pub const ServiceWeight: Option = Some(Weight::from_parts(100, 100)); } diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index b75764b67bea..c81e486a40df 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -177,7 +177,7 @@ fn service_queues_failing_messages_works() { MessageQueue::enqueue_message(msg("stacklimitreached"), Here); MessageQueue::enqueue_message(msg("yield"), Here); // Starts with four pages. - assert_pages(&[0, 1, 2, 3, 4]); + assert_pages(&[0, 1, 2]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( @@ -209,7 +209,7 @@ fn service_queues_failing_messages_works() { assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_eq!(System::events().len(), 4); // Last page with the `yield` stays in. - assert_pages(&[4]); + assert_pages(&[2]); }); } @@ -313,7 +313,7 @@ fn reap_page_permanent_overweight_works() { // Create 10 pages more than the stale limit. let n = (MaxStale::get() + 10) as usize; for _ in 0..n { - MessageQueue::enqueue_message(msg("weight=2"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadatadata"), Here); } assert_eq!(Pages::::iter().count(), n); assert_eq!(MessageQueue::footprint(Here).pages, n as u32); @@ -334,7 +334,7 @@ fn reap_page_permanent_overweight_works() { break } assert_ok!(MessageQueue::do_reap_page(&Here, i)); - assert_eq!(QueueChanges::take(), vec![(Here, b.message_count - 1, b.size - 8)]); + assert_eq!(QueueChanges::take(), vec![(Here, b.message_count - 1, b.size - 23)]); } // Cannot reap any more pages. @@ -353,20 +353,20 @@ fn reaping_overweight_fails_properly() { build_and_execute::(|| { // page 0 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("a"), Here); // page 1 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("b"), Here); // page 2 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("c"), Here); // page 3 - MessageQueue::enqueue_message(msg("bigbig 1"), Here); + MessageQueue::enqueue_message(msg("bigbig 1 datadata"), Here); // page 4 - MessageQueue::enqueue_message(msg("bigbig 2"), Here); + MessageQueue::enqueue_message(msg("bigbig 2 datadata"), Here); // page 5 - MessageQueue::enqueue_message(msg("bigbig 3"), Here); + MessageQueue::enqueue_message(msg("bigbig 3 datadata"), Here); // Double-check that exactly these pages exist. assert_pages(&[0, 1, 2, 3, 4, 5]); @@ -385,7 +385,7 @@ fn reaping_overweight_fails_properly() { // 3 stale now: can take something 4 pages in history. assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 1"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 1 datadata"), Here)]); // Nothing reapable yet, because we haven't hit the stale limit. for (o, i, _) in Pages::::iter() { @@ -394,7 +394,7 @@ fn reaping_overweight_fails_properly() { assert_pages(&[0, 1, 2, 4, 5]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 2"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 2 datadata"), Here)]); assert_pages(&[0, 1, 2, 5]); // First is now reapable as it is too far behind the first ready page (5). @@ -406,7 +406,7 @@ fn reaping_overweight_fails_properly() { assert_pages(&[1, 2, 5]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 3"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 3 datadata"), Here)]); assert_noop!(MessageQueue::do_reap_page(&Here, 0), Error::::NoPage); assert_noop!(MessageQueue::do_reap_page(&Here, 3), Error::::NoPage); @@ -1062,29 +1062,29 @@ fn footprint_on_swept_works() { fn footprint_num_pages_works() { use MessageOrigin::*; build_and_execute::(|| { - MessageQueue::enqueue_message(msg("weight=2"), Here); - MessageQueue::enqueue_message(msg("weight=3"), Here); + MessageQueue::enqueue_message(msg("weight=200"), Here); + MessageQueue::enqueue_message(msg("weight=300"), Here); - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 2, 20)); // Mark the messages as overweight. assert_eq!(MessageQueue::service_queues(1.into_weight()), 0.into_weight()); assert_eq!(System::events().len(), 2); // `ready_pages` decreases but `page` count does not. - assert_eq!(MessageQueue::footprint(Here), fp(2, 0, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 2, 20)); // Now execute the second message. assert_eq!( - ::execute_overweight(3.into_weight(), (Here, 1, 0)) + ::execute_overweight(300.into_weight(), (Here, 0, 1)) .unwrap(), - 3.into_weight() + 300.into_weight() ); - assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 8)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 10)); // And the first one: assert_eq!( - ::execute_overweight(2.into_weight(), (Here, 0, 0)) + ::execute_overweight(200.into_weight(), (Here, 0, 0)) .unwrap(), - 2.into_weight() + 200.into_weight() ); assert_eq!(MessageQueue::footprint(Here), Default::default()); assert_eq!(MessageQueue::footprint(Here), fp(0, 0, 0, 0)); @@ -1104,7 +1104,7 @@ fn execute_overweight_works() { // Enqueue a message let origin = MessageOrigin::Here; - MessageQueue::enqueue_message(msg("weight=6"), origin); + MessageQueue::enqueue_message(msg("weight=200"), origin); // Load the current book let book = BookStateFor::::get(origin); assert_eq!(book.message_count, 1); @@ -1112,10 +1112,10 @@ fn execute_overweight_works() { // Mark the message as permanently overweight. assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); - assert_eq!(QueueChanges::take(), vec![(origin, 1, 8)]); + assert_eq!(QueueChanges::take(), vec![(origin, 1, 10)]); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=6"), + id: blake2_256(b"weight=200"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1132,9 +1132,9 @@ fn execute_overweight_works() { assert_eq!(Pages::::iter().count(), 1); assert!(QueueChanges::take().is_empty()); let consumed = - ::execute_overweight(7.into_weight(), (origin, 0, 0)) + ::execute_overweight(200.into_weight(), (origin, 0, 0)) .unwrap(); - assert_eq!(consumed, 6.into_weight()); + assert_eq!(consumed, 200.into_weight()); assert_eq!(QueueChanges::take(), vec![(origin, 0, 0)]); // There is no message left in the book. let book = BookStateFor::::get(origin); @@ -1162,7 +1162,7 @@ fn permanently_overweight_book_unknits() { set_weight("service_queue_base", 1.into_weight()); set_weight("service_page_base_completion", 1.into_weight()); - MessageQueue::enqueue_messages([msg("weight=9")].into_iter(), Here); + MessageQueue::enqueue_messages([msg("weight=200")].into_iter(), Here); // It is the only ready book. assert_ring(&[Here]); @@ -1170,7 +1170,7 @@ fn permanently_overweight_book_unknits() { assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=9"), + id: blake2_256(b"weight=200"), origin: Here, message_index: 0, page_index: 0, @@ -1201,19 +1201,19 @@ fn permanently_overweight_book_unknits_multiple() { set_weight("service_page_base_completion", 1.into_weight()); MessageQueue::enqueue_messages( - [msg("weight=1"), msg("weight=9"), msg("weight=9")].into_iter(), + [msg("weight=1"), msg("weight=200"), msg("weight=200")].into_iter(), Here, ); assert_ring(&[Here]); // Process the first message. assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); - assert_eq!(num_overweight_enqueued_events(), 0); + assert_eq!(num_overweight_enqueued_events(), 1); assert_eq!(MessagesProcessed::take().len(), 1); // Book is still ready since it was not marked as overweight yet. assert_ring(&[Here]); - assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight()); + assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); assert_eq!(num_overweight_enqueued_events(), 2); assert_eq!(MessagesProcessed::take().len(), 0); // Now it is overweight. @@ -1566,12 +1566,12 @@ fn service_queues_suspend_works() { fn execute_overweight_respects_suspension() { build_and_execute::(|| { let origin = MessageOrigin::Here; - MessageQueue::enqueue_message(msg("weight=5"), origin); + MessageQueue::enqueue_message(msg("weight=200"), origin); // Mark the message as permanently overweight. MessageQueue::service_queues(4.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=5"), + id: blake2_256(b"weight=200"), origin, message_index: 0, page_index: 0, @@ -1598,9 +1598,9 @@ fn execute_overweight_respects_suspension() { assert_last_event::( Event::Processed { - id: blake2_256(b"weight=5").into(), + id: blake2_256(b"weight=200").into(), origin, - weight_used: 5.into_weight(), + weight_used: 200.into_weight(), success: true, } .into(), @@ -1768,7 +1768,7 @@ fn recursive_overweight_while_service_is_forbidden() { // Check that the message was permanently overweight. assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=10"), + id: blake2_256(b"weight=200"), origin: There, message_index: 0, page_index: 0, @@ -1786,13 +1786,13 @@ fn recursive_overweight_while_service_is_forbidden() { Ok(()) })); - MessageQueue::enqueue_message(msg("weight=10"), There); + MessageQueue::enqueue_message(msg("weight=200"), There); MessageQueue::enqueue_message(msg("callback=0"), Here); // Mark it as permanently overweight. MessageQueue::service_queues(5.into_weight()); assert_ok!(::execute_overweight( - 10.into_weight(), + 200.into_weight(), (There, 0, 0) )); }); @@ -1812,7 +1812,7 @@ fn recursive_reap_page_is_forbidden() { // Create 10 pages more than the stale limit. let n = (MaxStale::get() + 10) as usize; for _ in 0..n { - MessageQueue::enqueue_message(msg("weight=2"), Here); + MessageQueue::enqueue_message(msg("weight=200"), Here); } // Mark all pages as stale since their message is permanently overweight. @@ -1886,6 +1886,11 @@ fn process_enqueued_on_idle_requires_enough_weight() { // Not enough weight to process on idle. Pallet::::on_idle(1, Weight::from_parts(0, 0)); assert_eq!(MessagesProcessed::take(), vec![]); + + assert!(!System::events().into_iter().any(|e| matches!( + e.event, + RuntimeEvent::MessageQueue(Event::::OverweightEnqueued { .. }) + ))); }) } @@ -1923,12 +1928,12 @@ fn execute_overweight_keeps_stack_ov_message() { // We need to create a mocked message that first reports insufficient weight, and then // `StackLimitReached`: IgnoreStackOvError::set(true); - MessageQueue::enqueue_message(msg("stacklimitreached"), Here); + MessageQueue::enqueue_message(msg("weight=200 stacklimitreached"), Here); MessageQueue::service_queues(0.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"stacklimitreached"), + id: blake2_256(b"weight=200 stacklimitreached"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1952,7 +1957,7 @@ fn execute_overweight_keeps_stack_ov_message() { ); assert_last_event::( Event::ProcessingFailed { - id: blake2_256(b"stacklimitreached").into(), + id: blake2_256(b"weight=200 stacklimitreached").into(), origin: MessageOrigin::Here, error: ProcessMessageError::StackLimitReached, } @@ -1964,16 +1969,16 @@ fn execute_overweight_keeps_stack_ov_message() { // Now let's process it normally: IgnoreStackOvError::set(true); assert_eq!( - ::execute_overweight(1.into_weight(), (Here, 0, 0)) + ::execute_overweight(200.into_weight(), (Here, 0, 0)) .unwrap(), - 1.into_weight() + 200.into_weight() ); assert_last_event::( Event::Processed { - id: blake2_256(b"stacklimitreached").into(), + id: blake2_256(b"weight=200 stacklimitreached").into(), origin: MessageOrigin::Here, - weight_used: 1.into_weight(), + weight_used: 200.into_weight(), success: true, } .into(), From ae2102c94632feb1027307a57b235f884cca1cc3 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:42:07 +0200 Subject: [PATCH 40/58] `RuntimeGenesiConfig`: json macro added (#5813) This PR adds `build_struct_json_patch` which helps to generate a JSON used for preset. Here is doc and example: https://github.com/paritytech/polkadot-sdk/blob/d868b858758d3886d16c8ba8feb3c93c188044f3/substrate/frame/support/src/generate_genesis_config.rs#L168-L266 And real-world usage: https://github.com/paritytech/polkadot-sdk/blob/d868b858758d3886d16c8ba8feb3c93c188044f3/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs#L37-L61 Closes #5700 --------- Co-authored-by: Sebastian Kunert --- Cargo.lock | 2 + .../src/genesis_config_presets.rs | 17 +- .../src/reference_docs/chain_spec_genesis.rs | 22 +- .../chain_spec_runtime/Cargo.toml | 2 + .../chain_spec_runtime/src/pallets.rs | 2 +- .../chain_spec_runtime/src/presets.rs | 42 +- .../tests/chain_spec_builder_tests.rs | 19 +- .../westend/src/genesis_config_presets.rs | 17 +- prdoc/pr_5813.prdoc | 18 + substrate/frame/support/Cargo.toml | 1 + .../support/src/generate_genesis_config.rs | 951 ++++++++++++++++++ substrate/frame/support/src/lib.rs | 6 +- 12 files changed, 1044 insertions(+), 55 deletions(-) create mode 100644 prdoc/pr_5813.prdoc create mode 100644 substrate/frame/support/src/generate_genesis_config.rs diff --git a/Cargo.lock b/Cargo.lock index 602891892a2b..335c465e0aa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2824,6 +2824,7 @@ name = "chain-spec-guide-runtime" version = "0.0.0" dependencies = [ "docify", + "frame-support", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -6442,6 +6443,7 @@ dependencies = [ name = "frame-support" version = "28.0.0" dependencies = [ + "Inflector", "aquamarine", "array-bytes", "assert_matches", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs index dc98d00f8f63..d15dd971a819 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs @@ -18,6 +18,7 @@ use crate::*; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use hex_literal::hex; use parachains_common::{AccountId, AuraId}; use sp_core::crypto::UncheckedInto; @@ -33,15 +34,14 @@ fn asset_hub_rococo_genesis( endowment: Balance, id: ParaId, ) -> serde_json::Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -54,16 +54,9 @@ fn asset_hub_rococo_genesis( ) }) .collect(), - ..Default::default() }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + }) } /// Encapsulates names of predefined presets. diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 3326f433f288..b7a0a648d0cf 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -100,17 +100,22 @@ //! others useful for testing. //! //! Internally, presets can be provided in a number of ways: -//! - JSON in string form: -#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] -//! - JSON using runtime types to serialize values: +//! - using [`build_struct_json_patch`] macro (**recommended**): #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)] +//! - JSON using runtime types to serialize values: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] +//! - JSON in string form: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] +//! //! It is worth noting that a preset does not have to be the full `RuntimeGenesisConfig`, in that //! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON //! patch that will be merged with the default value of `RuntimeGenesisConfig`. This approach should //! simplify maintenance of built-in presets. The following example illustrates a runtime genesis -//! config patch: +//! config patch with a single key built using [`build_struct_json_patch`] macro: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)] +//! This results in the following JSON blob: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", preset_4_json)] +//! //! //! ## Note on the importance of testing presets //! @@ -122,8 +127,8 @@ //! //! ## Note on the importance of using the `deny_unknown_fields` attribute //! -//! It is worth noting that it is easy to make a hard-to-spot mistake, as in the following example -//! ([`FooStruct`] does not contain `fieldC`): +//! It is worth noting that when manually building preset JSON blobs it is easy to make a +//! hard-to-spot mistake, as in the following example ([`FooStruct`] does not contain `fieldC`): #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)] //! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON //! blob does not fail. The misspelling is silently ignored due to the lack of the @@ -131,6 +136,10 @@ //! `GenesisConfig`. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)] //! +//! To avoid this problem [`build_struct_json_patch`] macro shall be used whenever possible (it +//! internally instantiates the struct before serializang it JSON blob, so all unknown fields shall +//! be caught at compilation time). +//! //! ## Runtime `GenesisConfig` raw format //! //! A raw format of genesis config contains just the state's keys and values as they are stored in @@ -182,6 +191,7 @@ //! [`get_preset`]: frame_support::genesis_builder_helper::get_preset //! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build //! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config +//! [`build_struct_json_patch`]: frame_support::build_struct_json_patch //! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig //! [`serde`]: https://serde.rs/field-attrs.html //! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 028495712032..07c0342f5fbe 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -12,6 +12,7 @@ publish = false [dependencies] docify = { workspace = true } codec = { workspace = true } +frame-support = { workspace = true } scale-info = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -49,6 +50,7 @@ std = [ "codec/std", "scale-info/std", + "frame-support/std", "frame/std", "pallet-balances/std", diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs index 2ff2d9539e2d..571632ecd272 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -118,7 +118,7 @@ pub mod pallet_foo { pub some_enum: FooEnum, pub some_struct: FooStruct, #[serde(skip)] - _phantom: PhantomData, + pub _phantom: PhantomData, } #[pallet::genesis_build] diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs index 02c2d90f7c82..c1316b2f8734 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -17,8 +17,12 @@ //! Presets for the chain-spec demo runtime. -use crate::pallets::{FooEnum, SomeFooData1, SomeFooData2}; +use crate::{ + pallets::{FooEnum, SomeFooData1, SomeFooData2}, + runtime::{BarConfig, FooConfig, RuntimeGenesisConfig}, +}; use alloc::vec; +use frame_support::build_struct_json_patch; use serde_json::{json, to_string, Value}; use sp_application_crypto::Ss58Codec; use sp_keyring::AccountKeyring; @@ -27,7 +31,7 @@ use sp_keyring::AccountKeyring; pub const PRESET_1: &str = "preset_1"; /// A demo preset with real types. pub const PRESET_2: &str = "preset_2"; -/// Another demo preset with real types. +/// Another demo preset with real types and manually created json object. pub const PRESET_3: &str = "preset_3"; /// A single value patch preset. pub const PRESET_4: &str = "preset_4"; @@ -58,21 +62,21 @@ fn preset_1() -> Value { } #[docify::export] -/// Function provides a preset demonstrating how use the actual types to create a preset. +/// Function provides a preset demonstrating how to create a preset using +/// [`build_struct_json_patch`] macro. fn preset_2() -> Value { - json!({ - "bar": { - "initialAccount": AccountKeyring::Ferdie.public().to_ss58check(), - }, - "foo": { - "someEnum": FooEnum::Data2(SomeFooData2 { values: vec![12,16] }), - "someInteger": 200 + build_struct_json_patch!(RuntimeGenesisConfig { + foo: FooConfig { + some_integer: 200, + some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) }, + bar: BarConfig { initial_account: Some(AccountKeyring::Ferdie.public().into()) }, }) } #[docify::export] -/// Function provides a preset demonstrating how use the actual types to create a preset. +/// Function provides a preset demonstrating how use the actual types to manually create a JSON +/// representing the preset. fn preset_3() -> Value { json!({ "bar": { @@ -92,22 +96,16 @@ fn preset_3() -> Value { #[docify::export] /// Function provides a minimal preset demonstrating how to patch single key in -/// `RuntimeGenesisConfig`. -fn preset_4() -> Value { - json!({ - "foo": { - "someEnum": { - "Data2": { - "values": "0x0c0f" - } - }, - }, +/// `RuntimeGenesisConfig` using [`build_struct_json_patch`] macro. +pub fn preset_4() -> Value { + build_struct_json_patch!(RuntimeGenesisConfig { + foo: FooConfig { some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) }, }) } #[docify::export] /// Function provides an invalid preset demonstrating how important is use of -/// [`deny_unknown_fields`] in data structures used in `GenesisConfig`. +/// `deny_unknown_fields` in data structures used in `GenesisConfig`. fn preset_invalid() -> Value { json!({ "foo": { diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index cc273685fcb4..c2fe5a6727e6 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -58,7 +58,7 @@ fn get_preset() { let output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); - //note: copy of chain_spec_guide_runtime::preset_1 + //note: copy of chain_spec_guide_runtime::preset_2 let expected_output = json!({ "bar": { "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", @@ -186,3 +186,20 @@ fn generate_para_chain_spec() { }); assert_eq!(output, expected_output, "Output did not match expected"); } + +#[test] +#[docify::export] +fn preset_4_json() { + assert_eq!( + chain_spec_guide_runtime::presets::preset_4(), + json!({ + "foo": { + "someEnum": { + "Data2": { + "values": "0x0c10" + } + }, + }, + }) + ); +} diff --git a/polkadot/runtime/westend/src/genesis_config_presets.rs b/polkadot/runtime/westend/src/genesis_config_presets.rs index 621ef38f0b75..a9e22898ddb4 100644 --- a/polkadot/runtime/westend/src/genesis_config_presets.rs +++ b/polkadot/runtime/westend/src/genesis_config_presets.rs @@ -23,6 +23,7 @@ use crate::{ #[cfg(not(feature = "std"))] use alloc::format; use alloc::{vec, vec::Vec}; +use frame_support::build_struct_json_patch; use pallet_staking::{Forcing, StakerStatus}; use polkadot_primitives::{AccountId, AssignmentId, SchedulerParams, ValidatorId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -170,7 +171,7 @@ fn westend_testnet_genesis( const ENDOWMENT: u128 = 1_000_000 * WND; const STASH: u128 = 100 * WND; - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, @@ -192,7 +193,6 @@ fn westend_testnet_genesis( ) }) .collect::>(), - ..Default::default() }, staking: StakingConfig { minimum_validator_count: 1, @@ -204,19 +204,12 @@ fn westend_testnet_genesis( invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::>(), force_era: Forcing::NotForcing, slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() }, - babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG }, sudo: SudoConfig { key: Some(root_key) }, configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, - registrar: RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID }, + }) } // staging_testnet diff --git a/prdoc/pr_5813.prdoc b/prdoc/pr_5813.prdoc new file mode 100644 index 000000000000..e48f29bbfb63 --- /dev/null +++ b/prdoc/pr_5813.prdoc @@ -0,0 +1,18 @@ +title: "build_struct_json_patch macro added" + +doc: + - audience: Runtime Dev + description: | + This PR adds a macro that allows to construct a RuntimeGenesisConfig preset + containing only provided fields, while performing the validation of the + entire struct. + + Related issue: https://github.com/paritytech/polkadot-sdk/issues/5700 + +crates: + - name: frame-support + bump: minor + - name: asset-hub-rococo-runtime + bump: patch + - name: westend-runtime + bump: patch diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 18ca4a3acdab..d7da034b3492 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -71,6 +71,7 @@ pretty_assertions = { workspace = true } sp-timestamp = { workspace = true } frame-system = { workspace = true, default-features = true } sp-crypto-hashing = { workspace = true, default-features = true } +Inflector = { workspace = true } [features] default = ["std"] diff --git a/substrate/frame/support/src/generate_genesis_config.rs b/substrate/frame/support/src/generate_genesis_config.rs new file mode 100644 index 000000000000..fc21e76c7427 --- /dev/null +++ b/substrate/frame/support/src/generate_genesis_config.rs @@ -0,0 +1,951 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Helper macro allowing to construct JSON representation of partially initialized structs. + +use serde_json::Value; +extern crate alloc; +use alloc::{borrow::Cow, format, string::String}; + +/// Represents the initialization method of a field within a struct. +/// +/// This enum provides information about how it was initialized and the field name (as a `String`). +/// +/// Intended to be used in `build_struct_json_patch` macro. +#[derive(Debug)] +pub enum InitializedField<'a> { + /// The field was partially initialized (e.g., specific fields within the struct were set + /// manually). + Partial(Cow<'a, str>), + /// The field was fully initialized (e.g., using `new()` or `default()` like methods). + Full(Cow<'a, str>), +} + +impl<'a> InitializedField<'a> { + /// Returns a name of the field. + pub fn get_name(&'a self) -> &'a str { + match self { + Self::Partial(s) | Self::Full(s) => s, + } + } + + /// Injects a prefix to the field name. + pub fn add_prefix(&mut self, prefix: &str) { + match self { + Self::Partial(s) | Self::Full(s) => *s = format!("{prefix}.{s}").into(), + }; + } + + /// Creates new partial field instiance. + pub fn partial(s: &'a str) -> Self { + Self::Partial(s.into()) + } + + /// Creates new full field instiance. + pub fn full(s: &'a str) -> Self { + Self::Full(s.into()) + } +} + +impl PartialEq for InitializedField<'_> { + fn eq(&self, other: &String) -> bool { + #[inline] + /// We need to respect the `camelCase` naming for field names. This means that + /// `"camelCaseKey"` should be considered equal to `"camel_case_key"`. This + /// function implements this comparison. + fn compare_keys(ident_chars: core::str::Chars, camel_chars: core::str::Chars) -> bool { + ident_chars + .filter(|c| *c != '_') + .map(|c| c.to_ascii_uppercase()) + .eq(camel_chars.map(|c| c.to_ascii_uppercase())) + } + match self { + InitializedField::Partial(field_name) | InitializedField::Full(field_name) => + field_name == other || compare_keys(field_name.chars(), other.chars()), + } + } +} + +/// Recursively removes keys from provided `json_value` object, retaining only specified keys. +/// +/// This function modifies the provided `json_value` in-place, keeping only the keys listed in +/// `keys_to_retain`. The keys are matched recursively by combining the current key with +/// the `current_root`, allowing for nested field retention. +/// +/// Keys marked as `Full`, are retained as-is. For keys marked as `Partial`, the +/// function recurses into nested objects to retain matching subfields. +/// +/// Function respects the `camelCase` serde_json attribute for structures. This means that +/// `"camelCaseKey"` key will be retained in JSON blob if `"camel_case_key"` exists in +/// `keys_to_retain`. +/// +/// Intended to be used from `build_struct_json_patch` macro. +pub fn retain_initialized_fields( + json_value: &mut Value, + keys_to_retain: &[InitializedField], + current_root: String, +) { + if let serde_json::Value::Object(ref mut map) = json_value { + map.retain(|key, value| { + let current_key = + if current_root.is_empty() { key.clone() } else { format!("{current_root}.{key}") }; + match keys_to_retain.iter().find(|key| **key == current_key) { + Some(InitializedField::Full(_)) => true, + Some(InitializedField::Partial(_)) => { + retain_initialized_fields(value, keys_to_retain, current_key.clone()); + true + }, + None => false, + } + }) + } +} + +/// Creates a JSON patch for given `struct_type`, supporting recursive field initialization. +/// +/// This macro creates a default `struct_type`, initializing specified fields (which can be nested) +/// with the provided values. Any fields not explicitly given are initialized with their default +/// values. The macro then serializes the fully initialized structure into a JSON blob, retaining +/// only the fields that were explicitly provided, either partially or fully initialized. +/// +/// Using this macro prevents errors from manually creating JSON objects, such as typos or +/// inconsistencies with the `struct_type` structure, by relying on the actual +/// struct definition. This ensures the generated JSON is valid and reflects any future changes +/// to the structure. +/// +/// # Example +/// +/// ```rust +/// use frame_support::build_struct_json_patch; +/// #[derive(Default, serde::Serialize, serde::Deserialize)] +/// #[serde(rename_all = "camelCase")] +/// struct RuntimeGenesisConfig { +/// a_field: u32, +/// b_field: B, +/// c_field: u32, +/// } +/// +/// #[derive(Default, serde::Serialize, serde::Deserialize)] +/// #[serde(rename_all = "camelCase")] +/// struct B { +/// i_field: u32, +/// j_field: u32, +/// } +/// impl B { +/// fn new() -> Self { +/// Self { i_field: 0, j_field: 2 } +/// } +/// } +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// a_field: 66, +/// }), +/// serde_json::json!({ +/// "aField": 66, +/// }) +/// ); +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// //"partial" initialization of `b_field` +/// b_field: B { +/// i_field: 2, +/// } +/// }), +/// serde_json::json!({ +/// "bField": {"iField": 2} +/// }) +/// ); +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// a_field: 66, +/// //"full" initialization of `b_field` +/// b_field: B::new() +/// }), +/// serde_json::json!({ +/// "aField": 66, +/// "bField": {"iField": 0, "jField": 2} +/// }) +/// ); +/// ``` +/// +/// In this example: +/// ```ignore +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// b_field: B { +/// i_field: 2, +/// } +/// }), +/// ``` +/// `b_field` is partially initialized, it will be expanded to: +/// ```ignore +/// RuntimeGenesisConfig { +/// b_field { +/// i_field: 2, +/// ..Default::default() +/// }, +/// ..Default::default() +/// } +/// ``` +/// While all other fields are initialized with default values. The macro serializes this, retaining +/// only the provided fields. +#[macro_export] +macro_rules! build_struct_json_patch { + ( + $($struct_type:ident)::+ { $($tail:tt)* } + ) => { + { + let mut keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + #[allow(clippy::needless_update)] + let struct_instance = $crate::build_struct_json_patch!($($struct_type)::+, keys @ { $($tail)* }); + let mut json_value = + $crate::__private::serde_json::to_value(struct_instance).expect("serialization to json should work. qed"); + $crate::generate_genesis_config::retain_initialized_fields(&mut json_value, &keys, Default::default()); + json_value + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ { $($tail:tt)* }) => { + $($struct_type)::+ { + ..$crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $keyi:ident : $value:tt } ) => { + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); + $all_keys.push( + $crate::generate_genesis_config::InitializedField::full(concat!(stringify!($key), ".", stringify!($keyi))) + ); + $($type)::+ { + $keyi:$value, + ..Default::default() + } + }, + ..Default::default() + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* } ) => { + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); + let mut inner_keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + let value = $crate::build_struct_json_patch!($($type)::+, inner_keys @ { $($body)* }); + for i in inner_keys.iter_mut() { + i.add_prefix(stringify!($key)); + }; + $all_keys.extend(inner_keys); + value + }, + ..Default::default() + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* }, $($tail:tt)* ) => { + $($struct_type)::+ { + $key : { + $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); + let mut inner_keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + let value = $crate::build_struct_json_patch!($($type)::+, inner_keys @ { $($body)* }); + for i in inner_keys.iter_mut() { + i.add_prefix(stringify!($key)); + }; + $all_keys.extend(inner_keys); + value + }, + .. $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr, $($tail:tt)* ) => { + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); + $value + }, + ..$crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + } + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr ) => { + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); + $value + }, + ..Default::default() + } + }; + + ($($struct_type:ident)::+, $all_keys:ident @ $(,)?) => { + $($struct_type)::+ { ..Default::default() } + }; +} + +#[cfg(test)] +mod test { + mod nested_mod { + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + pub struct InsideMod { + pub a: u32, + pub b: u32, + } + + pub mod nested_mod2 { + pub mod nested_mod3 { + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + pub struct InsideMod3 { + pub a: u32, + pub b: u32, + pub s: super::super::InsideMod, + } + } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct TestStruct { + a: u32, + b: u32, + s: S, + s3: S3, + t3: S3, + i: Nested1, + e: E, + t: nested_mod::InsideMod, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3, + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct S { + x: u32, + } + + impl S { + fn new(c: u32) -> Self { + Self { x: c } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct E(u8); + + #[derive(Default, Debug, serde::Serialize, serde::Deserialize)] + enum SomeEnum { + #[default] + A, + B(T), + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct S3 { + x: u32, + y: u32, + z: u32, + } + + impl S3 { + fn new(c: u32) -> Self { + Self { x: c, y: c, z: c } + } + + fn new_from_s(s: S) -> Self { + Self { x: s.x, ..Default::default() } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested3 { + a: u32, + b: u32, + s: S, + v: Vec<(u32, u32, u32, SomeEnum)>, + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested2 { + a: u32, + iii: Nested3, + v: Vec, + s3: S3, + } + + impl Nested2 { + fn new(a: u32) -> Self { + Nested2 { + a, + v: vec![a, a, a], + iii: Nested3 { a, b: a, ..Default::default() }, + s3: S3 { x: a, ..Default::default() }, + } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested1 { + a: u32, + ii: Nested2, + } + + macro_rules! test { + ($($struct:ident)::+ { $($v:tt)* }, { $($j:tt)* } ) => {{ + println!("--"); + let expected = serde_json::json!({ $($j)* }); + println!("json: {}", serde_json::to_string_pretty(&expected).unwrap()); + let value = build_struct_json_patch!($($struct)::+ { $($v)* }); + println!("gc: {}", serde_json::to_string_pretty(&value).unwrap()); + assert_eq!(value, expected); + }}; + } + + #[test] + fn test_generate_config_macro() { + let t = 5; + const C: u32 = 5; + test!(TestStruct { b: 5 }, { "b": 5 }); + #[allow(unused_braces)] + { + test!(TestStruct { b: { 4 + 34 } } , { "b": 38 }); + } + test!(TestStruct { s: S { x: 5 } }, { "s": { "x": 5 } }); + test!( + TestStruct { s: S::new(C) }, + { + "s": { "x": 5 } + } + ); + test!( + TestStruct { s: S { x: t } }, + { + "s": { "x": t } + } + ); + test!( + TestStruct { + b: 5, + s: S { x: t } + }, + { + "b": 5, + "s": { "x": 5 } + } + ); + test!( + TestStruct { s: S::new(C), b: 5 }, + { + "s": { "x": 5 }, "b": 5 + } + ); + test!( + TestStruct { s3: S3 { x: t } }, + { + "s3": { "x": 5 } + } + ); + test!( + TestStruct { + s3: S3 { x: t, y: 2 } + }, + { + "s3": { "x": 5, "y": 2 } + } + ); + // // + test!( + TestStruct { + s3: S3 { x: t, y: 2 }, + t3: S3 { x: 2 } + }, + { + "s3": { "x": t, "y": 2 }, + "t3": { "x": 2 } + } + + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { iii: Nested3 { a: 2 } } + } + } + , + { + "i": { + "ii": { "iii": { "a": 2 } } + } + } + + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) } + } + } + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5} } + } + } + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { s: S::new(C), a: 2 } + }, + a: 44 + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5} } + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2::new(66), + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "a": 66, + "s3": { "x":66, "y": 0, "z": 0 }, + "iii": { "a": 66,"b":66, "s": { "x": 0 }, "v": Vec::::default() }, + "v": vec![66,66,66] + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + a: 66, + s3: S3 { x: 66 }, + iii: Nested3 { + a: 66,b:66 + }, + v: vec![66,66,66] + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "a": 66, + "s3": { "x":66, }, + "iii": { "a": 66,"b":66, }, + "v": vec![66,66,66] + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) }, + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + s3: S3::new(5), + iii: Nested3 { a: 2, s: S::new(C) }, + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + "s3": {"x": 5, "y": 5, "z": 5 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + a: 3, + s3: S3 { x: 5 }, + b: 4, + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + } + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + let i = [0u32, 1u32, 2u32]; + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { + a: 2, + s: S::new(C), + v: i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + + { + "i": { + "ii": { + "iii": { + "a": 2, + "s": { "x": 5 }, + "v": i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + } + + #[test] + fn test_generate_config_macro_with_nested_mods() { + test!( + TestStruct { t: nested_mod::InsideMod { a: 32 } }, + { + "t" : { "a": 32 } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { a: 32 } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32 } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 32, + s: nested_mod::InsideMod { a: 34 }, + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34 } } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3::default() + }, + { + "t" : { "a": 32 }, + "u" : { "a": 0, "b": 0, "s": { "a": 0, "b": 0} } + } + ); + + let i = [0u32, 1u32, 2u32]; + const C: u32 = 5; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3::default(), + i: Nested1 { + ii: Nested2 { + iii: Nested3 { + a: 2, + s: S::new(C), + v: i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + }, + }, + { + "t" : { "a": 32 }, + "u" : { "a": 0, "b": 0, "s": { "a": 0, "b": 0} } , + "i": { + "ii": { + "iii": { + "a": 2, + "s": { "x": 5 }, + "v": i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + } + ); + } +} + +#[cfg(test)] +mod retain_keys_test { + use super::*; + use serde_json::json; + + macro_rules! check_initialized_field_eq_cc( + ( $s:literal ) => { + let field = InitializedField::full($s); + let cc = inflector::cases::camelcase::to_camel_case($s); + println!("field: {:?}, cc: {}", field, cc); + assert_eq!(field,cc); + } ; + ( &[ $f:literal $(, $r:literal)* ]) => { + let field = InitializedField::full( + concat!( $f $(,".",$r)+ ) + ); + let cc = [ $f $(,$r)+ ].into_iter() + .map(|s| inflector::cases::camelcase::to_camel_case(s)) + .collect::>() + .join("."); + println!("field: {:?}, cc: {}", field, cc); + assert_eq!(field,cc); + } ; + ); + + #[test] + fn test_initialized_field_eq_cc_string() { + check_initialized_field_eq_cc!("a_"); + check_initialized_field_eq_cc!("abc"); + check_initialized_field_eq_cc!("aBc"); + check_initialized_field_eq_cc!("aBC"); + check_initialized_field_eq_cc!("ABC"); + check_initialized_field_eq_cc!("2abs"); + check_initialized_field_eq_cc!("2Abs"); + check_initialized_field_eq_cc!("2ABs"); + check_initialized_field_eq_cc!("2aBs"); + check_initialized_field_eq_cc!("AlreadyCamelCase"); + check_initialized_field_eq_cc!("alreadyCamelCase"); + check_initialized_field_eq_cc!("C"); + check_initialized_field_eq_cc!("1a"); + check_initialized_field_eq_cc!("_1a"); + check_initialized_field_eq_cc!("a_b"); + check_initialized_field_eq_cc!("_a_b"); + check_initialized_field_eq_cc!("a___b"); + check_initialized_field_eq_cc!("__a_b"); + check_initialized_field_eq_cc!("_a___b_C"); + check_initialized_field_eq_cc!("__A___B_C"); + check_initialized_field_eq_cc!(&["a_b", "b_c"]); + check_initialized_field_eq_cc!(&["al_pha", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "_a___b_C"]); + check_initialized_field_eq_cc!(&["first_field", "al_pha_", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "__2nd_field", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "__2nd3and_field", "_a___b_C"]); + check_initialized_field_eq_cc!(&["_a1", "_a2", "_a3_"]); + } + + #[test] + fn test01() { + let mut v = json!({ + "a":1 + }); + let e = v.clone(); + retain_initialized_fields(&mut v, &[InitializedField::full("a")], String::default()); + assert_eq!(e, v); + } + + #[test] + fn test02() { + let mut v = json!({ + "a":1 + }); + retain_initialized_fields(&mut v, &[InitializedField::full("b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test03() { + let mut v = json!({}); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test04() { + let mut v = json!({}); + retain_initialized_fields(&mut v, &[InitializedField::full("b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test05() { + let mut v = json!({ + "a":1 + }); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test06() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test07() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + retain_initialized_fields(&mut v, &[InitializedField::full("a.b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test08() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + let e = json!({ + "a": { + "b":1, + } + }); + retain_initialized_fields( + &mut v, + &[InitializedField::partial("a"), InitializedField::full("a.b")], + String::default(), + ); + assert_eq!(e, v); + } + + #[test] + fn test09() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + let e = json!({ + "a": { + "b":1, + "c":2, + } + }); + retain_initialized_fields(&mut v, &[InitializedField::full("a")], String::default()); + assert_eq!(e, v); + } +} diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index cc805d724857..2e7ea0a07d7d 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -53,6 +53,7 @@ pub mod __private { pub use paste; pub use scale_info; pub use serde; + pub use serde_json; pub use sp_core::{Get, OpaqueMetadata, Void}; pub use sp_crypto_hashing_proc_macro; pub use sp_inherents; @@ -2588,9 +2589,12 @@ sp_core::generate_feature_enabled_macro!(try_runtime_enabled, feature = "try-run sp_core::generate_feature_enabled_macro!(try_runtime_or_std_enabled, any(feature = "try-runtime", feature = "std"), $); sp_core::generate_feature_enabled_macro!(try_runtime_and_std_not_enabled, all(not(feature = "try-runtime"), not(feature = "std")), $); -// Helper for implementing GenesisBuilder runtime API +/// Helper for implementing GenesisBuilder runtime API pub mod genesis_builder_helper; +/// Helper for generating the `RuntimeGenesisConfig` instance for presets. +pub mod generate_genesis_config; + #[cfg(test)] mod test { // use super::*; From d3f92200b4f4d71ff578619ed1a277a29a15a81e Mon Sep 17 00:00:00 2001 From: "Shoyu Vanilla (Flint)" Date: Fri, 25 Oct 2024 18:00:15 +0900 Subject: [PATCH 41/58] substrate-offchain: upgrade hyper to v1 (#5919) Closes #4896 --- Cargo.lock | 67 +++++--- Cargo.toml | 9 +- polkadot/node/service/src/lib.rs | 6 +- prdoc/pr_5919.prdoc | 19 +++ substrate/bin/node/cli/src/service.rs | 14 +- substrate/client/offchain/Cargo.toml | 10 +- substrate/client/offchain/src/api.rs | 2 +- substrate/client/offchain/src/api/http.rs | 183 +++++++++++++--------- substrate/client/offchain/src/lib.rs | 11 +- templates/minimal/node/src/service.rs | 12 +- templates/parachain/node/src/service.rs | 12 +- templates/solochain/node/src/service.rs | 12 +- 12 files changed, 211 insertions(+), 146 deletions(-) create mode 100644 prdoc/pr_5919.prdoc diff --git a/Cargo.lock b/Cargo.lock index 335c465e0aa3..3d5193fe0ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7501,16 +7501,17 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.3.1", "hyper-util", "log", - "rustls 0.23.10", + "rustls 0.23.14", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8148,7 +8149,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.23.2", "pin-project", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -8173,7 +8174,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.24.3", "pin-project", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -8287,11 +8288,11 @@ dependencies = [ "base64 0.22.1", "http-body 1.0.0", "hyper 1.3.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "jsonrpsee-core 0.24.3", "jsonrpsee-types 0.24.3", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-platform-verifier", "serde", "serde_json", @@ -17083,7 +17084,7 @@ dependencies = [ "quinn-proto 0.11.8", "quinn-udp 0.5.4", "rustc-hash 2.0.0", - "rustls 0.23.10", + "rustls 0.23.14", "socket2 0.5.7", "thiserror", "tokio", @@ -17117,7 +17118,7 @@ dependencies = [ "rand", "ring 0.17.7", "rustc-hash 2.0.0", - "rustls 0.23.10", + "rustls 0.23.14", "slab", "thiserror", "tinyvec", @@ -17610,7 +17611,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "ipnet", "js-sys", @@ -17620,7 +17621,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn 0.11.5", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pemfile 2.0.0", "rustls-pki-types", "serde", @@ -18199,22 +18200,22 @@ dependencies = [ "log", "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "subtle 2.5.0", "zeroize", ] [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "subtle 2.5.0", "zeroize", ] @@ -18244,6 +18245,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.0.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -18265,9 +18279,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-platform-verifier" @@ -18280,10 +18294,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-native-certs 0.7.0", "rustls-platform-verifier-android", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "security-framework", "security-framework-sys", "webpki-roots 0.26.3", @@ -18308,9 +18322,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.7", "rustls-pki-types", @@ -19485,14 +19499,17 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.29", - "hyper-rustls 0.24.2", + "http-body-util", + "hyper 1.3.1", + "hyper-rustls 0.27.3", + "hyper-util", "log", "num_cpus", "once_cell", "parity-scale-codec", "parking_lot 0.12.3", "rand", + "rustls 0.23.14", "sc-block-builder", "sc-client-api", "sc-client-db", @@ -24976,7 +24993,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -25700,7 +25717,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 6ba91de3c09b..dd1fa40e146a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -757,6 +757,8 @@ femme = { version = "2.2.1" } filetime = { version = "0.2.16" } finality-grandpa = { version = "0.16.2", default-features = false } finality-relay = { path = "bridges/relays/finality" } +first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } +first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } flate2 = { version = "1.0" } fnv = { version = "1.0.6" } fork-tree = { path = "substrate/utils/fork-tree", default-features = false } @@ -805,12 +807,8 @@ http = { version = "1.1" } http-body = { version = "1", default-features = false } http-body-util = { version = "0.1.2", default-features = false } hyper = { version = "1.3.1", default-features = false } -hyper-rustls = { version = "0.24.2" } +hyper-rustls = { version = "0.27.3", default-features = false, features = ["http1", "http2", "logging", "ring", "rustls-native-certs", "tls12"] } hyper-util = { version = "0.1.5", default-features = false } -# TODO: remove hyper v0.14 https://github.com/paritytech/polkadot-sdk/issues/4896 -first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } -first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } -hyperv14 = { package = "hyper", version = "0.14.29", default-features = false } impl-serde = { version = "0.5.0", default-features = false } impl-trait-for-tuples = { version = "0.2.2" } indexmap = { version = "2.0.0" } @@ -1127,6 +1125,7 @@ rstest = { version = "0.18.2" } rustc-hash = { version = "1.1.0" } rustc-hex = { version = "2.1.0", default-features = false } rustix = { version = "0.36.7", default-features = false } +rustls = { version = "0.23.14", default-features = false, features = ["logging", "ring", "std", "tls12"] } rustversion = { version = "1.0.17" } rusty-fork = { version = "0.3.0", default-features = false } safe-mix = { version = "1.0", default-features = false } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index d6f24159e1da..abba91a38a97 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -1044,7 +1044,7 @@ pub fn new_full< is_validator: role.is_authority(), enable_http_requests: false, custom_extensions: move |_| vec![], - }) + })? .run(client.clone(), task_manager.spawn_handle()) .boxed(), ); @@ -1436,7 +1436,7 @@ pub fn new_chain_ops( } else if config.chain_spec.is_kusama() { chain_ops!(config, None) } else if config.chain_spec.is_westend() { - return chain_ops!(config, None) + return chain_ops!(config, None); } else { chain_ops!(config, None) } @@ -1488,7 +1488,7 @@ pub fn revert_backend( let revertible = blocks.min(best_number - finalized); if revertible == 0 { - return Ok(()) + return Ok(()); } let number = best_number - revertible; diff --git a/prdoc/pr_5919.prdoc b/prdoc/pr_5919.prdoc new file mode 100644 index 000000000000..1b48a24a9e28 --- /dev/null +++ b/prdoc/pr_5919.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "substrate-offchain: upgrade hyper to v1" + +doc: + - audience: Node Dev + description: | + Bump depencency `hyper` of `substrait-offchain` for http from `0.14` to `1`. + This changed APIs a bit; + - `sc_offchain::Offchainworker::new()` now returns `std::io::Result` (Previously was `Self`) + +crates: + - name: sc-offchain + bump: major + - name: polkadot-service + bump: patch + - name: staging-node-cli + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 7b166f94bcc8..057e0bbdcefc 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -790,9 +790,7 @@ pub fn new_full_base::Hash>>( ); if enable_offchain_worker { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-work", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), keystore: Some(keystore_container.keystore()), @@ -806,9 +804,11 @@ pub fn new_full_base::Hash>>( custom_extensions: move |_| { vec![Box::new(statement_store.clone().as_statement_store_ext()) as Box<_>] }, - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } @@ -992,7 +992,7 @@ mod tests { sc_consensus_babe::authorship::claim_slot(slot.into(), &epoch, &keystore) .map(|(digest, _)| digest) { - break (babe_pre_digest, epoch_descriptor) + break (babe_pre_digest, epoch_descriptor); } slot += 1; diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml index bbbe7018d106..71b40211e126 100644 --- a/substrate/client/offchain/Cargo.toml +++ b/substrate/client/offchain/Cargo.toml @@ -22,15 +22,15 @@ codec = { features = ["derive"], workspace = true, default-features = true } fnv = { workspace = true } futures = { workspace = true } futures-timer = { workspace = true } -hyperv14 = { features = [ - "http2", - "stream", -], workspace = true, default-features = true } -hyper-rustls = { features = ["http2"], workspace = true } +http-body-util = { workspace = true } +hyper = { features = ["http1", "http2"], workspace = true, default-features = true } +hyper-rustls = { workspace = true } +hyper-util = { features = ["client-legacy", "http1", "http2"], workspace = true } num_cpus = { workspace = true } once_cell = { workspace = true } parking_lot = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } +rustls = { workspace = true } threadpool = { workspace = true } tracing = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } diff --git a/substrate/client/offchain/src/api.rs b/substrate/client/offchain/src/api.rs index 19ccdbcf498f..a5981f14c093 100644 --- a/substrate/client/offchain/src/api.rs +++ b/substrate/client/offchain/src/api.rs @@ -326,7 +326,7 @@ mod tests { fn offchain_api() -> (Api, AsyncApi) { sp_tracing::try_init_simple(); let mock = Arc::new(TestNetwork()); - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); AsyncApi::new(mock, false, shared_client) } diff --git a/substrate/client/offchain/src/api/http.rs b/substrate/client/offchain/src/api/http.rs index 73407b1359d7..56f5c0230094 100644 --- a/substrate/client/offchain/src/api/http.rs +++ b/substrate/client/offchain/src/api/http.rs @@ -27,14 +27,14 @@ //! (i.e.: the socket should continue being processed) in the background even if the runtime isn't //! actively calling any function. -use hyperv14 as hyper; - use crate::api::timestamp; use bytes::buf::{Buf, Reader}; use fnv::FnvHashMap; use futures::{channel::mpsc, future, prelude::*}; -use hyper::{client, Body, Client as HyperClient}; +use http_body_util::{combinators::BoxBody, StreamBody}; +use hyper::body::Body as _; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use hyper_util::{client::legacy as client, rt::TokioExecutor}; use once_cell::sync::Lazy; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_core::offchain::{HttpError, HttpRequestId, HttpRequestStatus, Timestamp}; @@ -48,21 +48,26 @@ use std::{ const LOG_TARGET: &str = "offchain-worker::http"; +pub type Body = BoxBody; + +type Sender = mpsc::Sender, hyper::Error>>; +type Receiver = mpsc::Receiver, hyper::Error>>; + +type HyperClient = client::Client, Body>; +type LazyClient = Lazy HyperClient + Send>>; + /// Wrapper struct used for keeping the hyper_rustls client running. #[derive(Clone)] -pub struct SharedClient(Arc, Body>>>); +pub struct SharedClient(Arc); impl SharedClient { - pub fn new() -> Self { - Self(Arc::new(Lazy::new(|| { - let connector = HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build(); - HyperClient::builder().build(connector) - }))) + pub fn new() -> std::io::Result { + let builder = HttpsConnectorBuilder::new() + .with_provider_and_native_roots(rustls::crypto::ring::default_provider())?; + Ok(Self(Arc::new(Lazy::new(Box::new(|| { + let connector = builder.https_or_http().enable_http1().enable_http2().build(); + client::Client::builder(TokioExecutor::new()).build(connector) + }))))) } } @@ -105,23 +110,23 @@ pub struct HttpApi { /// One active request within `HttpApi`. enum HttpApiRequest { /// The request object is being constructed locally and not started yet. - NotDispatched(hyper::Request, hyper::body::Sender), + NotDispatched(hyper::Request, Sender), /// The request has been dispatched and we're in the process of sending out the body (if the /// field is `Some`) or waiting for a response (if the field is `None`). - Dispatched(Option), + Dispatched(Option), /// Received a response. Response(HttpApiRequestRp), /// A request has been dispatched but the worker notified us of an error. We report this /// failure to the user as an `IoError` and remove the request from the list as soon as /// possible. - Fail(hyper::Error), + Fail(client::Error), } /// A request within `HttpApi` that has received a response. struct HttpApiRequestRp { /// We might still be writing the request's body when the response comes. /// This field allows to continue writing that body. - sending_body: Option, + sending_body: Option, /// Status code of the response. status_code: hyper::StatusCode, /// Headers of the response. @@ -132,7 +137,7 @@ struct HttpApiRequestRp { /// Elements extracted from the channel are first put into `current_read_chunk`. /// If the channel produces an error, then that is translated into an `IoError` and the request /// is removed from the list. - body: stream::Fuse>>, + body: stream::Fuse, /// Chunk that has been extracted from the channel and that is currently being read. /// Reading data from the response should read from this field in priority. current_read_chunk: Option>, @@ -144,7 +149,9 @@ impl HttpApi { // Start by building the prototype of the request. // We do this first so that we don't touch anything in `self` if building the prototype // fails. - let (body_sender, body) = hyper::Body::channel(); + let (body_sender, receiver) = mpsc::channel(0); + let body = StreamBody::new(receiver); + let body = BoxBody::new(body); let mut request = hyper::Request::new(body); *request.method_mut() = hyper::Method::from_bytes(method.as_bytes()).map_err(|_| ())?; *request.uri_mut() = hyper::Uri::from_maybe_shared(uri.to_owned()).map_err(|_| ())?; @@ -158,7 +165,7 @@ impl HttpApi { target: LOG_TARGET, "Overflow in offchain worker HTTP request ID assignment" ); - return Err(()) + return Err(()); }, }; self.requests @@ -213,7 +220,7 @@ impl HttpApi { // Closure that writes data to a sender, taking the deadline into account. Can return `Ok` // (if the body has been written), or `DeadlineReached`, or `IoError`. // If `IoError` is returned, don't forget to remove the request from the list. - let mut poll_sender = move |sender: &mut hyper::body::Sender| -> Result<(), HttpError> { + let mut poll_sender = move |sender: &mut Sender| -> Result<(), HttpError> { let mut when_ready = future::maybe_done(future::poll_fn(|cx| sender.poll_ready(cx))); futures::executor::block_on(future::select(&mut when_ready, &mut deadline)); match when_ready { @@ -221,12 +228,15 @@ impl HttpApi { future::MaybeDone::Done(Err(_)) => return Err(HttpError::IoError), future::MaybeDone::Future(_) | future::MaybeDone::Gone => { debug_assert!(matches!(deadline, future::MaybeDone::Done(..))); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); }, }; futures::executor::block_on( - sender.send_data(hyper::body::Bytes::from(chunk.to_owned())), + async { + future::poll_fn(|cx| sender.poll_ready(cx)).await?; + sender.start_send(Ok(hyper::body::Frame::data(hyper::body::Bytes::from(chunk.to_owned())))) + } ) .map_err(|_| { tracing::error!(target: "offchain-worker::http", "HTTP sender refused data despite being ready"); @@ -250,13 +260,13 @@ impl HttpApi { match poll_sender(&mut sender) { Err(HttpError::IoError) => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, "Encountered io error while trying to add new chunk to body"); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, other => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, res = ?other, "Added chunk to body"); self.requests .insert(request_id, HttpApiRequest::Dispatched(Some(sender))); - return other + return other; }, } } else { @@ -265,7 +275,7 @@ impl HttpApi { // Writing an empty body is a hint that we should stop writing. Dropping // the sender. self.requests.insert(request_id, HttpApiRequest::Dispatched(None)); - return Ok(()) + return Ok(()); } }, @@ -281,13 +291,13 @@ impl HttpApi { ) { Err(HttpError::IoError) => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, "Encountered io error while trying to add new chunk to body"); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, other => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, res = ?other, "Added chunk to body"); self.requests .insert(request_id, HttpApiRequest::Response(response)); - return other + return other; }, } } else { @@ -302,7 +312,7 @@ impl HttpApi { ..response }), ); - return Ok(()) + return Ok(()); } }, @@ -311,7 +321,7 @@ impl HttpApi { // If the request has already failed, return without putting back the request // in the list. - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, v @ HttpApiRequest::Dispatched(None) | @@ -320,7 +330,7 @@ impl HttpApi { // We have already finished sending this body. self.requests.insert(request_id, v); - return Err(HttpError::Invalid) + return Err(HttpError::Invalid); }, } } @@ -340,7 +350,7 @@ impl HttpApi { Some(HttpApiRequest::Dispatched(sending_body)) | Some(HttpApiRequest::Response(HttpApiRequestRp { sending_body, .. })) => { let _ = sending_body.take(); - continue + continue; }, _ => continue, }; @@ -405,7 +415,7 @@ impl HttpApi { }, } } - return output + return output; } } @@ -418,7 +428,7 @@ impl HttpApi { msg } else { debug_assert!(matches!(deadline, future::MaybeDone::Done(..))); - continue + continue; } }; @@ -458,7 +468,7 @@ impl HttpApi { None => { tracing::error!(target: "offchain-worker::http", "Worker has crashed"); - return ids.iter().map(|_| HttpRequestStatus::IoError).collect() + return ids.iter().map(|_| HttpRequestStatus::IoError).collect(); }, } } @@ -498,14 +508,14 @@ impl HttpApi { // and we still haven't received a response. Some(rq @ HttpApiRequest::Dispatched(_)) => { self.requests.insert(request_id, rq); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); }, // The request has failed. Some(HttpApiRequest::Fail { .. }) => return Err(HttpError::IoError), // Request hasn't been dispatched yet; reading the body is invalid. Some(rq @ HttpApiRequest::NotDispatched(_, _)) => { self.requests.insert(request_id, rq); - return Err(HttpError::Invalid) + return Err(HttpError::Invalid); }, None => return Err(HttpError::Invalid), }; @@ -526,12 +536,12 @@ impl HttpApi { ..response }), ); - return Ok(n) + return Ok(n); }, Err(err) => { // This code should never be reached unless there's a logic error somewhere. tracing::error!(target: "offchain-worker::http", "Failed to read from current read chunk: {:?}", err); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, } } @@ -544,7 +554,10 @@ impl HttpApi { if let future::MaybeDone::Done(next_body) = next_body { match next_body { - Some(Ok(chunk)) => response.current_read_chunk = Some(chunk.reader()), + Some(Ok(chunk)) => + if let Ok(chunk) = chunk.into_data() { + response.current_read_chunk = Some(chunk.reader()); + }, Some(Err(_)) => return Err(HttpError::IoError), None => return Ok(0), // eof } @@ -552,7 +565,7 @@ impl HttpApi { if let future::MaybeDone::Done(_) = deadline { self.requests.insert(request_id, HttpApiRequest::Response(response)); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); } } } @@ -587,7 +600,7 @@ enum ApiToWorker { /// ID to send back when the response comes back. id: HttpRequestId, /// Request to start executing. - request: hyper::Request, + request: hyper::Request, }, } @@ -608,14 +621,14 @@ enum WorkerToApi { /// the next item. /// Can also be used to send an error, in case an error happened on the HTTP socket. After /// an error is sent, the channel will close. - body: mpsc::Receiver>, + body: Receiver, }, /// A request has failed because of an error. The request is then no longer valid. Fail { /// The ID that was passed to the worker. id: HttpRequestId, /// Error that happened. - error: hyper::Error, + error: client::Error, }, } @@ -626,7 +639,7 @@ pub struct HttpWorker { /// Used to receive messages from the `HttpApi`. from_api: TracingUnboundedReceiver, /// The engine that runs HTTP requests. - http_client: Arc, Body>>>, + http_client: Arc, /// HTTP requests that are being worked on by the engine. requests: Vec<(HttpRequestId, HttpWorkerRequest)>, } @@ -634,13 +647,13 @@ pub struct HttpWorker { /// HTTP request being processed by the worker. enum HttpWorkerRequest { /// Request has been dispatched and is waiting for a response from the Internet. - Dispatched(hyper::client::ResponseFuture), + Dispatched(client::ResponseFuture), /// Progressively reading the body of the response and sending it to the channel. ReadBody { /// Body to read `Chunk`s from. Only used if the channel is ready to accept data. - body: hyper::Body, + body: Body, /// Channel to the [`HttpApi`] where we send the chunks to. - tx: mpsc::Sender>, + tx: Sender, }, } @@ -663,12 +676,12 @@ impl Future for HttpWorker { let response = match Future::poll(Pin::new(&mut future), cx) { Poll::Pending => { me.requests.push((id, HttpWorkerRequest::Dispatched(future))); - continue + continue; }, Poll::Ready(Ok(response)) => response, Poll::Ready(Err(error)) => { let _ = me.to_api.unbounded_send(WorkerToApi::Fail { id, error }); - continue // don't insert the request back + continue; // don't insert the request back }, }; @@ -684,9 +697,12 @@ impl Future for HttpWorker { body: body_rx, }); - me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx: body_tx })); + me.requests.push(( + id, + HttpWorkerRequest::ReadBody { body: Body::new(body), tx: body_tx }, + )); cx.waker().wake_by_ref(); // reschedule in order to poll the new future - continue + continue; }, HttpWorkerRequest::ReadBody { mut body, mut tx } => { @@ -697,12 +713,11 @@ impl Future for HttpWorker { Poll::Ready(Err(_)) => continue, // don't insert the request back Poll::Pending => { me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx })); - continue + continue; }, } - // `tx` is ready. Read a chunk from the socket and send it to the channel. - match Stream::poll_next(Pin::new(&mut body), cx) { + match Pin::new(&mut body).poll_frame(cx) { Poll::Ready(Some(Ok(chunk))) => { let _ = tx.start_send(Ok(chunk)); me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx })); @@ -762,19 +777,22 @@ mod tests { }; use crate::api::timestamp; use core::convert::Infallible; - use futures::{future, StreamExt}; + use futures::future; + use http_body_util::BodyExt; use sp_core::offchain::{Duration, Externalities, HttpError, HttpRequestId, HttpRequestStatus}; use std::sync::LazyLock; // Using LazyLock to avoid spawning lots of different SharedClients, // as spawning a SharedClient is CPU-intensive and opens lots of fds. - static SHARED_CLIENT: LazyLock = LazyLock::new(|| SharedClient::new()); + static SHARED_CLIENT: LazyLock = LazyLock::new(|| SharedClient::new().unwrap()); // Returns an `HttpApi` whose worker is ran in the background, and a `SocketAddr` to an HTTP // server that runs in the background as well. macro_rules! build_api_server { () => { - build_api_server!(hyper::Response::new(hyper::Body::from("Hello World!"))) + build_api_server!(hyper::Response::new(http_body_util::Full::new( + hyper::body::Bytes::from("Hello World!") + ))) }; ( $response:expr ) => {{ let hyper_client = SHARED_CLIENT.clone(); @@ -785,21 +803,32 @@ mod tests { let rt = tokio::runtime::Runtime::new().unwrap(); let worker = rt.spawn(worker); let server = rt.spawn(async move { - let server = hyper::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve( - hyper::service::make_service_fn(|_| async move { - Ok::<_, Infallible>(hyper::service::service_fn( - move |req: hyper::Request| async move { - // Wait until the complete request was received and processed, - // otherwise the tests are flaky. - let _ = req.into_body().collect::>().await; - - Ok::<_, Infallible>($response) - }, - )) - }), - ); - let _ = addr_tx.send(server.local_addr()); - server.await.map_err(drop) + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 0)); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + let _ = addr_tx.send(listener.local_addr().unwrap()); + loop { + let (stream, _) = listener.accept().await.unwrap(); + let io = hyper_util::rt::TokioIo::new(stream); + tokio::task::spawn(async move { + if let Err(err) = hyper::server::conn::http1::Builder::new() + .serve_connection( + io, + hyper::service::service_fn( + move |req: hyper::Request| async move { + // Wait until the complete request was received and + // processed, otherwise the tests are flaky. + let _ = req.into_body().collect().await; + + Ok::<_, Infallible>($response) + }, + ), + ) + .await + { + eprintln!("Error serving connection: {:?}", err); + } + }); + } }); let _ = rt.block_on(future::join(worker, server)); }); @@ -839,7 +868,7 @@ mod tests { let (mut api, addr) = build_api_server!(hyper::Response::builder() .version(hyper::Version::HTTP_2) - .body(hyper::Body::from("Hello World!")) + .body(http_body_util::Full::new(hyper::body::Bytes::from("Hello World!"))) .unwrap()); let id = api.request_start("POST", &format!("http://{}", addr)).unwrap(); @@ -1097,7 +1126,7 @@ mod tests { #[test] fn shared_http_client_is_only_initialized_on_access() { - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); { let mock = Arc::new(TestNetwork()); @@ -1112,7 +1141,7 @@ mod tests { // Check that the http client wasn't initialized, because it wasn't used. assert!(Lazy::into_value(Arc::try_unwrap(shared_client.0).unwrap()).is_err()); - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); { let mock = Arc::new(TestNetwork()); diff --git a/substrate/client/offchain/src/lib.rs b/substrate/client/offchain/src/lib.rs index 3d5728aad17d..b0a7a66520b7 100644 --- a/substrate/client/offchain/src/lib.rs +++ b/substrate/client/offchain/src/lib.rs @@ -153,14 +153,14 @@ impl OffchainWorkers { enable_http_requests, custom_extensions, }: OffchainWorkerOptions, - ) -> Self { - Self { + ) -> std::io::Result { + Ok(Self { runtime_api_provider, thread_pool: Mutex::new(ThreadPool::with_name( "offchain-worker".into(), num_cpus::get(), )), - shared_http_client: api::SharedClient::new(), + shared_http_client: api::SharedClient::new()?, enable_http_requests, keystore, offchain_db: offchain_db.map(OffchainDb::new), @@ -168,7 +168,7 @@ impl OffchainWorkers { is_validator, network_provider, custom_extensions: Box::new(custom_extensions), - } + }) } } @@ -466,7 +466,8 @@ mod tests { is_validator: false, enable_http_requests: false, custom_extensions: |_| Vec::new(), - }); + }) + .unwrap(); futures::executor::block_on(offchain.on_block_imported(&header)); // then diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index 6ba6959202c4..169993e2e93a 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -161,9 +161,7 @@ pub fn new_full::Ha })?; if config.offchain_worker.enabled { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-worker", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), is_validator: config.role.is_authority(), @@ -175,9 +173,11 @@ pub fn new_full::Ha network_provider: Arc::new(network.clone()), enable_http_requests: true, custom_extensions: |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-worker", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index dd7dff2ebf16..57ffcb9049d8 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -287,9 +287,7 @@ pub async fn start_parachain_node( if parachain_config.offchain_worker.enabled { use futures::FutureExt; - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-work", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), keystore: Some(params.keystore_container.keystore()), @@ -301,9 +299,11 @@ pub async fn start_parachain_node( is_validator: parachain_config.role.is_authority(), enable_http_requests: false, custom_extensions: move |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 4192128b6724..2524906fd508 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -197,9 +197,7 @@ pub fn new_full< })?; if config.offchain_worker.enabled { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-worker", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), is_validator: config.role.is_authority(), @@ -211,9 +209,11 @@ pub fn new_full< network_provider: Arc::new(network.clone()), enable_http_requests: true, custom_extensions: |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-worker", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } From 5482b6b612d208c45923a39b22cbbd5044d68b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 25 Oct 2024 11:27:46 +0200 Subject: [PATCH 42/58] pallet-revive: Add stateful address mapping (#6096) Fixes #5576 This allows contracts to be used with an AccountId32 through normal extrinsics and not only through the eth compat layer. It works by adding a new extrinsic `map_account` that lets people register their AccountId32. --------- Co-authored-by: command-bot <> Co-authored-by: GitHub Action Co-authored-by: Cyrill Leutwiler --- .../assets/asset-hub-westend/src/lib.rs | 2 +- prdoc/pr_6096.prdoc | 15 + substrate/bin/node/runtime/src/lib.rs | 2 +- .../contracts/locking_delegate_dependency.rs | 4 +- .../fixtures/contracts/self_destruct.rs | 4 +- .../contracts/terminate_and_send_to_eve.rs | 33 + .../src/parachain/contracts_config.rs | 2 +- substrate/frame/revive/src/address.rs | 288 ++++- .../frame/revive/src/benchmarking/mod.rs | 112 +- substrate/frame/revive/src/evm/runtime.rs | 4 +- substrate/frame/revive/src/exec.rs | 121 +- substrate/frame/revive/src/lib.rs | 79 +- substrate/frame/revive/src/test_utils.rs | 39 +- substrate/frame/revive/src/tests.rs | 115 +- substrate/frame/revive/src/weights.rs | 1089 +++++++++-------- 15 files changed, 1235 insertions(+), 674 deletions(-) create mode 100644 prdoc/pr_6096.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 9a60de77a584..adfa2b74df65 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -957,7 +957,7 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; type UnsafeUnstableInterface = ConstBool; diff --git a/prdoc/pr_6096.prdoc b/prdoc/pr_6096.prdoc new file mode 100644 index 000000000000..c77c323a2e08 --- /dev/null +++ b/prdoc/pr_6096.prdoc @@ -0,0 +1,15 @@ +title: 'pallet-revive: Add stateful address mapping' +doc: +- audience: + - Runtime Dev + description: |- + Fixes #5576 + + This allows contracts to be used with an AccountId32 through normal extrinsics and not only through the eth compat layer. It works by adding a new extrinsic `map_account` that lets people register their AccountId32. +crates: +- name: pallet-revive-fixtures + bump: minor +- name: pallet-revive-mock-network + bump: patch +- name: pallet-revive + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d6a17856e470..89b5bf26ce75 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1415,7 +1415,7 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; type UnsafeUnstableInterface = ConstBool; diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 2efacb4e683f..54c7c7f3d5e2 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -23,7 +23,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ETH_ALICE: [u8; 20] = [1u8; 20]; +const ALICE_FALLBACK: [u8; 20] = [1u8; 20]; /// Load input data and perform the action specified by the input. /// If `delegate_call` is true, then delegate call into the contract. @@ -44,7 +44,7 @@ fn load_input(delegate_call: bool) { }, // 3 = Terminate 3 => { - api::terminate(Ð_ALICE); + api::terminate(&ALICE_FALLBACK); }, // Everything else is a noop _ => {}, diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 524979991ec7..2f37706634bd 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -21,7 +21,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ETH_DJANGO: [u8; 20] = [4u8; 20]; +const DJANGO_FALLBACK: [u8; 20] = [4u8; 20]; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -52,6 +52,6 @@ pub extern "C" fn call() { .unwrap(); } else { // Try to terminate and give balance to django. - api::terminate(Ð_DJANGO); + api::terminate(&DJANGO_FALLBACK); } } diff --git a/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs b/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs new file mode 100644 index 000000000000..c078f9d46c1d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let eve = [5u8; 20]; + api::terminate(&eve); +} diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs index c13c337d1667..a2fa7cbf7068 100644 --- a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -20,7 +20,7 @@ use frame_support::derive_impl; #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] impl pallet_revive::Config for Runtime { - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type Currency = Balances; type Time = super::Timestamp; type Xcm = pallet_xcm::Pallet; diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index 4633fce1f327..45b5bf822dc9 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -17,61 +17,173 @@ //! Functions that deal contract addresses. +use crate::{ensure, AddressSuffix, Config, Error, HoldReason}; use alloc::vec::Vec; -use sp_core::H160; +use core::marker::PhantomData; +use frame_support::traits::{fungible::MutateHold, tokens::Precision}; +use sp_core::{Get, H160}; use sp_io::hashing::keccak_256; -use sp_runtime::AccountId32; +use sp_runtime::{AccountId32, DispatchResult, SaturatedConversion, Saturating}; /// Map between the native chain account id `T` and an Ethereum [`H160`]. /// /// This trait exists only to emulate specialization for different concrete /// native account ids. **Not** to make the mapping user configurable. Hence -/// the trait is `Sealed` and only one mandatory implementor [`DefaultAddressMapper`] -/// exists. +/// the trait is `Sealed` and depending on your runtime configuration you need +/// to pick either [`AccountId32Mapper`] or [`H160Mapper`]. Picking the wrong +/// one will result in a compilation error. No footguns here. /// /// Please note that we assume that the native account is at least 20 bytes and /// only implement this type for a `T` where this is the case. Luckily, this is the -/// case for all existing runtimes as of right now. Reasing is that this will allow +/// case for all existing runtimes as of right now. Reasoning is that this will allow /// us to reverse an address -> account_id mapping by just stripping the prefix. -pub trait AddressMapper: private::Sealed { - /// Convert an account id to an ethereum address. - /// - /// This mapping is **not** required to be reversible. - fn to_address(account_id: &T) -> H160; +/// +/// We require the mapping to be reversible. Since we are potentially dealing with types of +/// different sizes one direction of the mapping is necessarily lossy. This requires the mapping to +/// make use of the [`AddressSuffix`] storage item to reverse the mapping. +pub trait AddressMapper: private::Sealed { + /// Convert an account id to an ethereum adress. + fn to_address(account_id: &T::AccountId) -> H160; /// Convert an ethereum address to a native account id. + fn to_account_id(address: &H160) -> T::AccountId; + + /// Same as [`Self::to_account_id`] but always returns the fallback account. + /// + /// This skips the query into [`AddressSuffix`] and always returns the stateless + /// fallback account. This is useful when we know for a fact that the `address` + /// in question is originally a `H160`. This is usually only the case when we + /// generated a new contract address. + fn to_fallback_account_id(address: &H160) -> T::AccountId; + + /// Create a stateful mapping for `account_id` + /// + /// This will enable `to_account_id` to map back to the original + /// `account_id` instead of the fallback account id. + fn map(account_id: &T::AccountId) -> DispatchResult; + + /// Remove the mapping in order to reclaim the deposit. /// - /// This mapping is **required** to be reversible. - fn to_account_id(address: &H160) -> T; + /// There is no reason why one would unmap their `account_id` except + /// for reclaiming the deposit. + fn unmap(account_id: &T::AccountId) -> DispatchResult; - /// Same as [`Self::to_account_id`] but when we know the address is a contract. + /// Returns true if the `account_id` is useable as an origin. /// - /// This is only the case when we just generated the new address. - fn to_account_id_contract(address: &H160) -> T; + /// This means either the `account_id` doesn't require a stateful mapping + /// or a stateful mapping exists. + fn is_mapped(account_id: &T::AccountId) -> bool; } mod private { pub trait Sealed {} - impl Sealed for super::DefaultAddressMapper {} + impl Sealed for super::AccountId32Mapper {} + impl Sealed for super::H160Mapper {} } -/// The only implementor for `AddressMapper`. -pub enum DefaultAddressMapper {} +/// The mapper to be used if the account id is `AccountId32`. +/// +/// It converts between addresses by either truncating the last 12 bytes or +/// suffixing them. The suffix is queried from [`AddressSuffix`] and will fall +/// back to all `0xEE` if no suffix was registered. This means contracts and +/// plain wallets controlled by an `secp256k1` always have a `0xEE` suffixed +/// account. +pub struct AccountId32Mapper(PhantomData); + +/// The mapper to be used if the account id is `H160`. +/// +/// It just trivially returns its inputs and doesn't make use of any state. +pub struct H160Mapper(PhantomData); -impl AddressMapper for DefaultAddressMapper { +impl AddressMapper for AccountId32Mapper +where + T: Config, +{ fn to_address(account_id: &AccountId32) -> H160 { H160::from_slice(&>::as_ref(&account_id)[..20]) } fn to_account_id(address: &H160) -> AccountId32 { + if let Some(suffix) = >::get(address) { + let mut account_id = Self::to_fallback_account_id(address); + let account_bytes: &mut [u8; 32] = account_id.as_mut(); + account_bytes[20..].copy_from_slice(suffix.as_slice()); + account_id + } else { + Self::to_fallback_account_id(address) + } + } + + fn to_fallback_account_id(address: &H160) -> AccountId32 { let mut account_id = AccountId32::new([0xEE; 32]); - >::as_mut(&mut account_id)[..20] - .copy_from_slice(address.as_bytes()); + let account_bytes: &mut [u8; 32] = account_id.as_mut(); + account_bytes[..20].copy_from_slice(address.as_bytes()); account_id } - fn to_account_id_contract(address: &H160) -> AccountId32 { - Self::to_account_id(address) + fn map(account_id: &T::AccountId) -> DispatchResult { + ensure!(!Self::is_mapped(account_id), >::AccountAlreadyMapped); + + let account_bytes: &[u8; 32] = account_id.as_ref(); + + // each mapping entry stores one AccountId32 distributed between key and value + let deposit = T::DepositPerByte::get() + .saturating_mul(account_bytes.len().saturated_into()) + .saturating_add(T::DepositPerItem::get()); + + let suffix: [u8; 12] = account_bytes[20..] + .try_into() + .expect("Skipping 20 byte of a an 32 byte array will fit into 12 bytes; qed"); + T::Currency::hold(&HoldReason::AddressMapping.into(), account_id, deposit)?; + >::insert(Self::to_address(account_id), suffix); + Ok(()) + } + + fn unmap(account_id: &T::AccountId) -> DispatchResult { + // will do nothing if address is not mapped so no check required + >::remove(Self::to_address(account_id)); + T::Currency::release_all( + &HoldReason::AddressMapping.into(), + account_id, + Precision::BestEffort, + )?; + Ok(()) + } + + fn is_mapped(account_id: &T::AccountId) -> bool { + let account_bytes: &[u8; 32] = account_id.as_ref(); + &account_bytes[20..] == &[0xEE; 12] || + >::contains_key(Self::to_address(account_id)) + } +} + +impl AddressMapper for H160Mapper +where + T: Config, + crate::AccountIdOf: AsRef<[u8; 20]> + From, +{ + fn to_address(account_id: &T::AccountId) -> H160 { + H160::from_slice(account_id.as_ref()) + } + + fn to_account_id(address: &H160) -> T::AccountId { + Self::to_fallback_account_id(address) + } + + fn to_fallback_account_id(address: &H160) -> T::AccountId { + (*address).into() + } + + fn map(_account_id: &T::AccountId) -> DispatchResult { + Ok(()) + } + + fn unmap(_account_id: &T::AccountId) -> DispatchResult { + Ok(()) + } + + fn is_mapped(_account_id: &T::AccountId) -> bool { + true } } @@ -102,7 +214,16 @@ pub fn create2(deployer: &H160, code: &[u8], input_data: &[u8], salt: &[u8; 32]) #[cfg(test)] mod test { use super::*; - use crate::test_utils::ALICE_ADDR; + use crate::{ + test_utils::*, + tests::{ExtBuilder, Test}, + AddressMapper, Error, + }; + use frame_support::{ + assert_err, + traits::fungible::{InspectHold, Mutate}, + }; + use pretty_assertions::assert_eq; use sp_core::{hex2array, H160}; #[test] @@ -125,4 +246,123 @@ mod test { H160(hex2array!("7f31e795e5836a19a8f919ab5a9de9a197ecd2b6")), ) } + + #[test] + fn fallback_map_works() { + assert!(::AddressMapper::is_mapped(&ALICE)); + assert_eq!( + ALICE_FALLBACK, + ::AddressMapper::to_fallback_account_id(&ALICE_ADDR) + ); + assert_eq!(ALICE_ADDR, ::AddressMapper::to_address(&ALICE_FALLBACK)); + } + + #[test] + fn map_works() { + ExtBuilder::default().build().execute_with(|| { + ::Currency::set_balance(&EVE, 1_000_000); + // before mapping the fallback account is returned + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!(EVE_FALLBACK, ::AddressMapper::to_account_id(&EVE_ADDR)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + + // when mapped the full account id is returned + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + assert_eq!(EVE, ::AddressMapper::to_account_id(&EVE_ADDR)); + assert!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ) > 0 + ); + }); + } + + #[test] + fn map_fallback_account_fails() { + ExtBuilder::default().build().execute_with(|| { + assert!(::AddressMapper::is_mapped(&ALICE)); + // alice is an e suffixed account and hence cannot be mapped + assert_err!( + ::AddressMapper::map(&ALICE), + >::AccountAlreadyMapped, + ); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &ALICE + ), + 0 + ); + }); + } + + #[test] + fn double_map_fails() { + ExtBuilder::default().build().execute_with(|| { + assert!(!::AddressMapper::is_mapped(&EVE)); + ::Currency::set_balance(&EVE, 1_000_000); + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + let deposit = ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE, + ); + assert_err!( + ::AddressMapper::map(&EVE), + >::AccountAlreadyMapped, + ); + assert!(::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + deposit + ); + }); + } + + #[test] + fn unmap_works() { + ExtBuilder::default().build().execute_with(|| { + ::Currency::set_balance(&EVE, 1_000_000); + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + assert!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ) > 0 + ); + + ::AddressMapper::unmap(&EVE).unwrap(); + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + + // another unmap is a noop + ::AddressMapper::unmap(&EVE).unwrap(); + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + }); + } } diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index ebafb6c7054a..8c9bf2cf70f0 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -63,6 +63,7 @@ const UNBALANCED_TRIE_LAYERS: u32 = 20; struct Contract { caller: T::AccountId, account_id: T::AccountId, + address: H160, } impl Contract @@ -71,11 +72,6 @@ where BalanceOf: Into + TryFrom, MomentOf: Into, { - /// Returns the address of the contract. - fn address(&self) -> H160 { - T::AddressMapper::to_address(&self.account_id) - } - /// Create new contract and use a default account id as instantiator. fn new(module: WasmModule, data: Vec) -> Result, &'static str> { Self::with_index(0, module, data) @@ -98,9 +94,12 @@ where ) -> Result, &'static str> { T::Currency::set_balance(&caller, caller_funding::()); let salt = Some([0xffu8; 32]); + let origin: T::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + + Contracts::::map_account(origin.clone()).unwrap(); let outcome = Contracts::::bare_instantiate( - RawOrigin::Signed(caller.clone()).into(), + origin, 0u32.into(), Weight::MAX, default_deposit_limit::(), @@ -112,8 +111,8 @@ where ); let address = outcome.result?.addr; - let account_id = T::AddressMapper::to_account_id_contract(&address); - let result = Contract { caller, account_id: account_id.clone() }; + let account_id = T::AddressMapper::to_fallback_account_id(&address); + let result = Contract { caller, address, account_id }; ContractInfoOf::::insert(&address, result.info()?); @@ -143,7 +142,7 @@ where info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) .map_err(|_| "Failed to write storage to restoration dest")?; } - >::insert(T::AddressMapper::to_address(&self.account_id), info); + >::insert(&self.address, info); Ok(()) } @@ -223,6 +222,7 @@ fn default_deposit_limit() -> BalanceOf { T: Config + pallet_balances::Config, MomentOf: Into, ::RuntimeEvent: From>, + ::RuntimeCall: From>, as Currency>::Balance: From>, )] mod benchmarks { @@ -263,13 +263,12 @@ mod benchmarks { let instance = Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; let value = Pallet::::min_balance(); - let callee = T::AddressMapper::to_address(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] call( RawOrigin::Signed(instance.caller.clone()), - callee, + instance.address, value, Weight::MAX, storage_deposit, @@ -293,9 +292,10 @@ mod benchmarks { T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); + Contracts::::map_account(origin.clone().into()).unwrap(); let deployer = T::AddressMapper::to_address(&caller); let addr = crate::address::create2(&deployer, &code, &input, &salt); - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let account_id = T::AddressMapper::to_fallback_account_id(&addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt)); @@ -305,9 +305,14 @@ mod benchmarks { // uploading the code reserves some balance in the callers account let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller); assert_eq!( T::Currency::balance(&caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance(), ); // contract has the full value assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); @@ -323,33 +328,31 @@ mod benchmarks { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let origin = RawOrigin::Signed(caller.clone()); + Contracts::::map_account(origin.clone().into()).unwrap(); let WasmModule { code, .. } = WasmModule::dummy(); let storage_deposit = default_deposit_limit::(); let deployer = T::AddressMapper::to_address(&caller); let addr = crate::address::create2(&deployer, &code, &input, &salt); - let hash = - Contracts::::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let hash = Contracts::::bare_upload_code(origin.clone().into(), code, storage_deposit)? + .code_hash; + let account_id = T::AddressMapper::to_fallback_account_id(&addr); #[extrinsic_call] - _( - RawOrigin::Signed(caller.clone()), - value, - Weight::MAX, - storage_deposit, - hash, - input, - Some(salt), - ); + _(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt)); let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &account_id); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id); // value was removed from the caller assert_eq!( T::Currency::total_balance(&caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance(), ); // contract has the full value assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); @@ -371,11 +374,10 @@ mod benchmarks { Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); - let callee = T::AddressMapper::to_address(&instance.account_id); let before = T::Currency::balance(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] - _(origin, callee, value, Weight::MAX, storage_deposit, data); + _(origin, instance.address, value, Weight::MAX, storage_deposit, data); let deposit = T::Currency::balance_on_hold( &HoldReason::StorageDepositReserve.into(), &instance.account_id, @@ -384,10 +386,15 @@ mod benchmarks { &HoldReason::CodeUploadDepositReserve.into(), &instance.caller, ); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller); // value and value transferred via call should be removed from the caller assert_eq!( T::Currency::balance(&instance.caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance() ); // contract should have received the value assert_eq!(T::Currency::balance(&instance.account_id), before + value); @@ -447,14 +454,46 @@ mod benchmarks { let storage_deposit = default_deposit_limit::(); let hash = >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let callee = T::AddressMapper::to_address(&instance.account_id); assert_ne!(instance.info()?.code_hash, hash); #[extrinsic_call] - _(RawOrigin::Root, callee, hash); + _(RawOrigin::Root, instance.address, hash); assert_eq!(instance.info()?.code_hash, hash); Ok(()) } + #[benchmark(pov_mode = Measured)] + fn map_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + assert!(!T::AddressMapper::is_mapped(&caller)); + #[extrinsic_call] + _(origin); + assert!(T::AddressMapper::is_mapped(&caller)); + } + + #[benchmark(pov_mode = Measured)] + fn unmap_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + >::map_account(origin.clone().into()).unwrap(); + assert!(T::AddressMapper::is_mapped(&caller)); + #[extrinsic_call] + _(origin); + assert!(!T::AddressMapper::is_mapped(&caller)); + } + + #[benchmark(pov_mode = Measured)] + fn dispatch_as_fallback_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + let dispatchable = frame_system::Call::remark { remark: vec![] }.into(); + #[extrinsic_call] + _(origin, Box::new(dispatchable)); + } + #[benchmark(pov_mode = Measured)] fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) { let mut setup = CallSetup::::new(WasmModule::noop()); @@ -636,7 +675,7 @@ mod benchmarks { build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]); >::insert::<_, BoundedVec<_, _>>( - contract.address(), + contract.address, immutable_data.clone().try_into().unwrap(), ); @@ -669,10 +708,7 @@ mod benchmarks { } assert_ok!(result); - assert_eq!( - &memory[..], - &>::get(setup.contract().address()).unwrap()[..] - ); + assert_eq!(&memory[..], &>::get(setup.contract().address).unwrap()[..]); } #[benchmark(pov_mode = Measured)] @@ -835,7 +871,7 @@ mod benchmarks { assert_eq!( record.event, - crate::Event::ContractEmitted { contract: instance.address(), data, topics }.into(), + crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(), ); } @@ -1542,7 +1578,7 @@ mod benchmarks { let salt = [42u8; 32]; let deployer = T::AddressMapper::to_address(&account_id); let addr = crate::address::create2(&deployer, &code.code, &input, &salt); - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let account_id = T::AddressMapper::to_fallback_account_id(&addr); let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,); let mut offset = { diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 4c3fdeca720c..80406a49bca2 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -290,7 +290,7 @@ pub trait EthExtra { })?; let signer = - ::AddressMapper::to_account_id_contract(&signer); + ::AddressMapper::to_fallback_account_id(&signer); let TransactionLegacyUnsigned { nonce, chain_id, to, value, input, gas, gas_price, .. } = tx.transaction_legacy_unsigned; @@ -419,7 +419,7 @@ mod test { /// Get the [`AccountId`] of the account. pub fn account_id(&self) -> AccountIdOf { let address = self.address(); - ::AddressMapper::to_account_id_contract(&address) + ::AddressMapper::to_fallback_account_id(&address) } /// Get the [`H160`] address of the account. diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 759fba9f1c6a..9f3a75c0090d 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -167,6 +167,18 @@ impl Origin { Origin::Root => Err(DispatchError::RootNotAllowed), } } + + /// Make sure that this origin is mapped. + /// + /// We require an origin to be mapped in order to be used in a `Stack`. Otherwise + /// [`Stack::caller`] returns an address that can't be reverted to the original address. + fn ensure_mapped(&self) -> DispatchResult { + match self { + Self::Root => Ok(()), + Self::Signed(account_id) if T::AddressMapper::is_mapped(account_id) => Ok(()), + Self::Signed(_) => Err(>::AccountUnmapped.into()), + } + } } /// An interface that provides access to the external environment in which the @@ -752,7 +764,7 @@ where )? { stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output) } else { - Self::transfer_no_contract(&origin, &dest, value) + Self::transfer_from_origin(&origin, &dest, value) } } @@ -833,6 +845,7 @@ where value: BalanceOf, debug_message: Option<&'a mut DebugBuffer>, ) -> Result, ExecError> { + origin.ensure_mapped()?; let Some((first_frame, executable)) = Self::new_frame( args, value, @@ -925,7 +938,7 @@ where *executable.code_hash(), )?; ( - T::AddressMapper::to_account_id_contract(&address), + T::AddressMapper::to_fallback_account_id(&address), contract, executable, None, @@ -1241,13 +1254,13 @@ where } /// Transfer some funds from `from` to `to`. - fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> DispatchResult { + fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> ExecResult { // this avoids events to be emitted for zero balance transfers if !value.is_zero() { T::Currency::transfer(from, to, value, Preservation::Preserve) .map_err(|_| Error::::TransferFailed)?; } - Ok(()) + Ok(Default::default()) } /// Same as `transfer` but `from` is an `Origin`. @@ -1255,28 +1268,17 @@ where from: &Origin, to: &T::AccountId, value: BalanceOf, - ) -> DispatchResult { + ) -> ExecResult { // If the from address is root there is no account to transfer from, and therefore we can't // take any `value` other than 0. let from = match from { Origin::Signed(caller) => caller, - Origin::Root if value.is_zero() => return Ok(()), - Origin::Root => return DispatchError::RootNotAllowed.into(), + Origin::Root if value.is_zero() => return Ok(Default::default()), + Origin::Root => return Err(DispatchError::RootNotAllowed.into()), }; Self::transfer(from, to, value) } - /// Same as `transfer_from_origin` but creates an `ExecReturnValue` on success. - fn transfer_no_contract( - from: &Origin, - to: &T::AccountId, - value: BalanceOf, - ) -> ExecResult { - Self::transfer_from_origin(from, to, value) - .map(|_| ExecReturnValue::default()) - .map_err(Into::into) - } - /// Reference to the current (top) frame. fn top_frame(&self) -> &Frame { top_frame!(self) @@ -1379,12 +1381,7 @@ where )? { self.run(executable, input_data) } else { - Self::transfer_no_contract( - &Origin::from_account_id(self.account_id().clone()), - &dest, - value, - )?; - Ok(()) + Self::transfer(&self.account_id(), &dest, value).map(|_| ()) } }; @@ -1488,6 +1485,8 @@ where &T::AddressMapper::to_account_id(to), value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, ) + .map(|_| ()) + .map_err(|error| error.error) } fn get_storage(&mut self, key: &Key) -> Option> { @@ -2031,7 +2030,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, success_ch); set_balance(&ALICE, 100); - let balance = get_balance(&BOB_CONTRACT_ID); + let balance = get_balance(&BOB_FALLBACK); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); @@ -2047,7 +2046,7 @@ mod tests { .unwrap(); assert_eq!(get_balance(&ALICE), 100 - value); - assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + assert_eq!(get_balance(&BOB_FALLBACK), balance + value); }); } @@ -2069,7 +2068,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, delegate_ch); set_balance(&ALICE, 100); - let balance = get_balance(&BOB_CONTRACT_ID); + let balance = get_balance(&BOB_FALLBACK); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); @@ -2085,7 +2084,7 @@ mod tests { .unwrap(); assert_eq!(get_balance(&ALICE), 100 - value); - assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + assert_eq!(get_balance(&BOB_FALLBACK), balance + value); }); } @@ -2326,9 +2325,10 @@ mod tests { // Record the caller for bob. WitnessedCallerBob::mutate(|caller| { let origin = ctx.ext.caller(); - *caller = Some(::AddressMapper::to_address( - &origin.account_id().unwrap(), - )); + *caller = + Some(<::AddressMapper as AddressMapper>::to_address( + &origin.account_id().unwrap(), + )); }); // Call into CHARLIE contract. @@ -2350,9 +2350,10 @@ mod tests { // Record the caller for charlie. WitnessedCallerCharlie::mutate(|caller| { let origin = ctx.ext.caller(); - *caller = Some(::AddressMapper::to_address( - &origin.account_id().unwrap(), - )); + *caller = + Some(<::AddressMapper as AddressMapper>::to_address( + &origin.account_id().unwrap(), + )); }); exec_success() }); @@ -2716,10 +2717,11 @@ mod tests { ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address ); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the newly created account has the expected code hash and // there are instantiation event. @@ -2771,10 +2773,11 @@ mod tests { Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address ); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the account has not been created. assert!(ContractInfo::::load_code_hash(&instantiated_contract_id).is_none()); @@ -2837,10 +2840,11 @@ mod tests { let instantiated_contract_address = *instantiated_contract_address.borrow().as_ref().unwrap(); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the newly created account has the expected code hash and // there are instantiation event. @@ -2895,7 +2899,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB_FALLBACK, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -3025,7 +3029,8 @@ mod tests { fn recursive_call_during_constructor_is_balance_transfer() { let code = MockLoader::insert(Constructor, |ctx, _| { let account_id = ctx.ext.account_id().clone(); - let addr = ::AddressMapper::to_address(&account_id); + let addr = + <::AddressMapper as AddressMapper>::to_address(&account_id); let balance = ctx.ext.balance(); // Calling ourselves during the constructor will trigger a balance @@ -3086,7 +3091,8 @@ mod tests { fn cannot_send_more_balance_than_available_to_self() { let code_hash = MockLoader::insert(Call, |ctx, _| { let account_id = ctx.ext.account_id().clone(); - let addr = ::AddressMapper::to_address(&account_id); + let addr = + <::AddressMapper as AddressMapper>::to_address(&account_id); let balance = ctx.ext.balance(); assert_err!( @@ -3358,7 +3364,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB_CONTRACT_ID, + sender: BOB_FALLBACK, hash: remark_hash }), topics: vec![], @@ -3442,7 +3448,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB_CONTRACT_ID, + sender: BOB_FALLBACK, hash: remark_hash }), topics: vec![], @@ -3506,7 +3512,10 @@ mod tests { ) .unwrap(); - let account_id = ::AddressMapper::to_account_id_contract(&addr); + let account_id = + <::AddressMapper as AddressMapper>::to_fallback_account_id( + &addr, + ); assert_eq!(System::account_nonce(&ALICE), alice_nonce); assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); @@ -4299,7 +4308,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4484,7 +4493,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4601,7 +4610,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4645,7 +4654,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 11752e47cf2a..1967f9868d2b 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -48,6 +48,7 @@ use crate::{ storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, RuntimeCosts, WasmBlob}, }; +use alloc::boxed::Box; use codec::{Codec, Decode, Encode}; use environmental::*; use frame_support::{ @@ -78,7 +79,7 @@ use sp_runtime::{ }; pub use crate::{ - address::{create1, create2, AddressMapper, DefaultAddressMapper}, + address::{create1, create2, AccountId32Mapper, AddressMapper}, debug::Tracing, exec::MomentOf, pallet::*, @@ -160,11 +161,9 @@ pub mod pallet { /// The overarching call type. #[pallet::no_default_bounds] - type RuntimeCall: Dispatchable - + GetDispatchInfo - + codec::Decode - + core::fmt::Debug - + IsType<::RuntimeCall>; + type RuntimeCall: Parameter + + Dispatchable + + GetDispatchInfo; /// Overarching hold reason. #[pallet::no_default_bounds] @@ -233,9 +232,9 @@ pub mod pallet { #[pallet::constant] type CodeHashLockupDepositPercent: Get; - /// Only valid type is [`DefaultAddressMapper`]. - #[pallet::no_default_bounds] - type AddressMapper: AddressMapper>; + /// Use either valid type is [`address::AccountId32Mapper`] or [`address::H160Mapper`]. + #[pallet::no_default] + type AddressMapper: AddressMapper; /// Make contract callable functions marked as `#[unstable]` available. /// @@ -361,7 +360,6 @@ pub mod pallet { #[inject_runtime_type] type RuntimeCall = (); - type AddressMapper = DefaultAddressMapper; type CallFilter = (); type ChainExtension = (); type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -562,6 +560,12 @@ pub mod pallet { /// Immutable data can only be set during deploys and only be read during calls. /// Additionally, it is only valid to set the data once and it must not be empty. InvalidImmutableAccess, + /// An `AccountID32` account tried to interact with the pallet without having a mapping. + /// + /// Call [`Pallet::map_account`] in order to create a mapping for the account. + AccountUnmapped, + /// Tried to map an account that is already mapped. + AccountAlreadyMapped, } /// A reason for the pallet contracts placing a hold on funds. @@ -571,6 +575,8 @@ pub mod pallet { CodeUploadDepositReserve, /// The Pallet has reserved it for storage deposit. StorageDepositReserve, + /// Deposit for creating an address mapping in [`AddressSuffix`]. + AddressMapping, } /// A mapping from a contract's code hash to its code. @@ -602,6 +608,14 @@ pub mod pallet { pub(crate) type DeletionQueueCounter = StorageValue<_, DeletionQueueManager, ValueQuery>; + /// Map a Ethereum address to its original `AccountId32`. + /// + /// Stores the last 12 byte for addresses that were originally an `AccountId32` instead + /// of an `H160`. Register your `AccountId32` using [`Pallet::map_account`] in order to + /// use it with this pallet. + #[pallet::storage] + pub(crate) type AddressSuffix = StorageMap<_, Identity, H160, [u8; 12]>; + #[pallet::extra_constants] impl Pallet { #[pallet::constant_name(ApiVersion)] @@ -995,6 +1009,51 @@ pub mod pallet { Ok(()) }) } + + /// Register the callers account id so that it can be used in contract interactions. + /// + /// This will error if the origin is already mapped or is a eth native `Address20`. It will + /// take a deposit that can be released by calling [`Self::unmap_account`]. + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::map_account())] + pub fn map_account(origin: OriginFor) -> DispatchResult { + let origin = ensure_signed(origin)?; + T::AddressMapper::map(&origin) + } + + /// Unregister the callers account id in order to free the deposit. + /// + /// There is no reason to ever call this function other than freeing up the deposit. + /// This is only useful when the account should no longer be used. + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::unmap_account())] + pub fn unmap_account(origin: OriginFor) -> DispatchResult { + let origin = ensure_signed(origin)?; + T::AddressMapper::unmap(&origin) + } + + /// Dispatch an `call` with the origin set to the callers fallback address. + /// + /// Every `AccountId32` can control its corresponding fallback account. The fallback account + /// is the `AccountId20` with the last 12 bytes set to `0xEE`. This is essentially a + /// recovery function in case an `AccountId20` was used without creating a mapping first. + #[pallet::call_index(9)] + #[pallet::weight({ + let dispatch_info = call.get_dispatch_info(); + ( + T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight), + dispatch_info.class + ) + })] + pub fn dispatch_as_fallback_account( + origin: OriginFor, + call: Box<::RuntimeCall>, + ) -> DispatchResultWithPostInfo { + let origin = ensure_signed(origin)?; + let unmapped_account = + T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin)); + call.dispatch(RawOrigin::Signed(unmapped_account).into()) + } } } diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 92c21297a3ec..acd9a4cda38a 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -27,38 +27,35 @@ use frame_support::weights::Weight; use sp_core::H160; pub use sp_runtime::AccountId32; -const fn ee_suffix(addr: H160) -> AccountId32 { - let mut id = [0u8; 32]; - let mut i = 0; - while i < 20 { - id[i] = addr.0[i]; +const fn ee_suffix(mut account: [u8; 32]) -> AccountId32 { + let mut i = 20; + while i < 32 { + account[i] = 0xee; i += 1; } - - let mut j = 20; - while j < 32 { - id[j] = 0xee; - j += 1; - } - - AccountId32::new(id) + AccountId32::new(account) } -pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE: AccountId32 = ee_suffix([1u8; 32]); pub const ALICE_ADDR: H160 = H160([1u8; 20]); -pub const ETH_ALICE: AccountId32 = ee_suffix(ALICE_ADDR); +pub const ALICE_FALLBACK: AccountId32 = ee_suffix([1u8; 32]); -pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const BOB: AccountId32 = ee_suffix([2u8; 32]); pub const BOB_ADDR: H160 = H160([2u8; 20]); -pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); +pub const BOB_FALLBACK: AccountId32 = ee_suffix([2u8; 32]); -pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const CHARLIE: AccountId32 = ee_suffix([3u8; 32]); pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); -pub const CHARLIE_CONTRACT_ID: AccountId32 = ee_suffix(CHARLIE_ADDR); +pub const CHARLIE_FALLBACK: AccountId32 = ee_suffix([3u8; 32]); -pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); +pub const DJANGO: AccountId32 = ee_suffix([4u8; 32]); pub const DJANGO_ADDR: H160 = H160([4u8; 20]); -pub const ETH_DJANGO: AccountId32 = ee_suffix(DJANGO_ADDR); +pub const DJANGO_FALLBACK: AccountId32 = ee_suffix([4u8; 32]); + +/// Eve is a non ee account and hence needs a stateful mapping. +pub const EVE: AccountId32 = AccountId32::new([5u8; 32]); +pub const EVE_ADDR: H160 = H160([5u8; 20]); +pub const EVE_FALLBACK: AccountId32 = ee_suffix([5u8; 32]); pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 94af7dbd04d8..1b5e64739d88 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -39,9 +39,9 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::Memory, weights::WeightInfo, - BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, - DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, PristineCode, - H160, + AccountId32Mapper, BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, + ContractInfoOf, DebugInfo, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, + PristineCode, H160, }; use crate::test_utils::builder::Contract; @@ -114,7 +114,8 @@ pub mod test_utils { pub fn place_contract(address: &AccountIdOf, code_hash: sp_core::H256) { set_balance(address, Contracts::min_balance() * 10); >::insert(code_hash, CodeInfo::new(address.clone())); - let address = ::AddressMapper::to_address(&address); + let address = + <::AddressMapper as AddressMapper>::to_address(&address); let contract = >::new(&address, 0, code_hash).unwrap(); >::insert(address, contract); } @@ -508,13 +509,13 @@ parameter_types! { #[derive_impl(crate::config_preludes::TestDefaultConfig)] impl Config for Test { type Time = Timestamp; + type AddressMapper = AccountId32Mapper; type Currency = Balances; type CallFilter = TestFilter; type ChainExtension = (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; - type AddressMapper = DefaultAddressMapper; type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; @@ -635,9 +636,9 @@ mod run_tests { ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 100_000_000); assert!(!>::contains_key(BOB_ADDR)); - assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 0); + assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 0); let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result(); - assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 42); + assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 42); assert_eq!(result, Default::default()); }); } @@ -1302,7 +1303,7 @@ mod run_tests { let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let _ = ::Currency::set_balance(Ð_DJANGO, 1_000_000); + let _ = ::Currency::set_balance(&DJANGO_FALLBACK, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. @@ -1330,7 +1331,7 @@ mod run_tests { // Check that the beneficiary (django) got remaining balance. assert_eq!( - ::Currency::free_balance(ETH_DJANGO), + ::Currency::free_balance(DJANGO_FALLBACK), 1_000_000 + 100_000 + min_balance ); @@ -1382,7 +1383,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: contract.account_id.clone(), - to: ETH_DJANGO, + to: DJANGO_FALLBACK, amount: 100_000 + min_balance, }), topics: vec![], @@ -1545,7 +1546,7 @@ mod run_tests { // Sending at least the minimum balance should result in success but // no code called. - assert_eq!(test_utils::get_balance(Ð_DJANGO), 0); + assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 0); let result = builder::bare_call(bob.addr) .data( AsRef::<[u8]>::as_ref(&DJANGO_ADDR) @@ -1556,7 +1557,7 @@ mod run_tests { ) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::Success); - assert_eq!(test_utils::get_balance(Ð_DJANGO), 55); + assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55); let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) @@ -3732,7 +3733,7 @@ mod run_tests { // Instantiate the caller contract with the given input. let instantiate = |input: &(u32, H256)| { builder::bare_instantiate(Code::Upload(wasm_caller.clone())) - .origin(RuntimeOrigin::signed(ETH_ALICE)) + .origin(RuntimeOrigin::signed(ALICE_FALLBACK)) .data(input.encode()) .build() }; @@ -3740,13 +3741,13 @@ mod run_tests { // Call contract with the given input. let call = |addr_caller: &H160, input: &(u32, H256)| { builder::bare_call(*addr_caller) - .origin(RuntimeOrigin::signed(ETH_ALICE)) + .origin(RuntimeOrigin::signed(ALICE_FALLBACK)) .data(input.encode()) .build() }; const ED: u64 = 2000; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000); // Instantiate with lock_delegate_dependency should fail since the code is not yet on // chain. @@ -3760,7 +3761,7 @@ mod run_tests { for code in callee_codes.iter() { let CodeUploadReturnValue { deposit: deposit_per_code, .. } = Contracts::bare_upload_code( - RuntimeOrigin::signed(ETH_ALICE), + RuntimeOrigin::signed(ALICE_FALLBACK), code.clone(), deposit_limit::(), ) @@ -3790,7 +3791,7 @@ mod run_tests { // Removing the code should fail, since we have added a dependency. assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0]), + Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]), >::CodeInUse ); @@ -3848,14 +3849,17 @@ mod run_tests { ); // Since we unlocked the dependency we should now be able to remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code( + RuntimeOrigin::signed(ALICE_FALLBACK), + callee_hashes[0] + )); // Calling should fail since the delegated contract is not on chain anymore. assert_err!(call(&addr_caller, &noop_input).result, Error::::ContractTrapped); // Add the dependency back. Contracts::upload_code( - RuntimeOrigin::signed(ETH_ALICE), + RuntimeOrigin::signed(ALICE_FALLBACK), callee_codes[0].clone(), deposit_limit::(), ) @@ -3863,15 +3867,18 @@ mod run_tests { call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); // Call terminate should work, and return the deposit. - let balance_before = test_utils::get_balance(Ð_ALICE); + let balance_before = test_utils::get_balance(&ALICE_FALLBACK); assert_ok!(call(&addr_caller, &terminate_input).result); assert_eq!( - test_utils::get_balance(Ð_ALICE), + test_utils::get_balance(&ALICE_FALLBACK), ED + balance_before + contract.storage_base_deposit() + dependency_deposit ); // Terminate should also remove the dependency, so we can remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code( + RuntimeOrigin::signed(ALICE_FALLBACK), + callee_hashes[0] + )); }); } @@ -4109,13 +4116,13 @@ mod run_tests { let (wasm, _code_hash) = compile_module("balance_of").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); - let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000); let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); // The fixture asserts a non-zero returned free balance of the account; - // The ETH_ALICE account is endowed; + // The ALICE_FALLBACK account is endowed; // Hence we should not revert assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build()); @@ -4510,4 +4517,64 @@ mod run_tests { .build()); }); } + + #[test] + fn origin_must_be_mapped() { + let (code, hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + ::Currency::set_balance(&ALICE, 1_000_000); + ::Currency::set_balance(&EVE, 1_000_000); + + let eve = RuntimeOrigin::signed(EVE); + + // alice can instantiate as she doesn't need a mapping + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // without a mapping eve can neither call nor instantiate + assert_err!( + builder::bare_call(addr).origin(eve.clone()).build().result, + >::AccountUnmapped + ); + assert_err!( + builder::bare_instantiate(Code::Existing(hash)) + .origin(eve.clone()) + .build() + .result, + >::AccountUnmapped + ); + + // after mapping eve is usable as an origin + >::map_account(eve.clone()).unwrap(); + assert_ok!(builder::bare_call(addr).origin(eve.clone()).build().result); + assert_ok!(builder::bare_instantiate(Code::Existing(hash)).origin(eve).build().result); + }); + } + + #[test] + fn mapped_address_works() { + let (code, _) = compile_module("terminate_and_send_to_eve").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + ::Currency::set_balance(&ALICE, 1_000_000); + + // without a mapping everything will be send to the fallback account + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code.clone())).build_and_unwrap_contract(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 0); + builder::bare_call(addr).build_and_unwrap_result(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 100); + + // after mapping it will be sent to the real eve account + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + // need some balance to pay for the map deposit + ::Currency::set_balance(&EVE, 1_000); + >::map_account(RuntimeOrigin::signed(EVE)).unwrap(); + builder::bare_call(addr).build_and_unwrap_result(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 100); + assert_eq!(::Currency::total_balance(&EVE), 1_100); + }); + } } diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 9a1b2310b4eb..3203a0cba9fb 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-10-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-jniz7bxx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-dr4vwrkf-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -58,6 +58,9 @@ pub trait WeightInfo { fn upload_code(c: u32, ) -> Weight; fn remove_code() -> Weight; fn set_code() -> Weight; + fn map_account() -> Weight; + fn unmap_account() -> Weight; + fn dispatch_as_fallback_account() -> Weight; fn noop_host_fn(r: u32, ) -> Weight; fn seal_caller() -> Weight; fn seal_is_contract() -> Weight; @@ -126,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_712_000 picoseconds. - Weight::from_parts(2_882_000, 1594) + // Minimum execution time: 3_053_000 picoseconds. + Weight::from_parts(3_150_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -135,18 +138,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + k * (69 ±0)` - // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(13_668_000, 382) - // Standard Error: 2_208 - .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) + // Measured: `425 + k * (69 ±0)` + // Estimated: `415 + k * (70 ±0)` + // Minimum execution time: 15_219_000 picoseconds. + Weight::from_parts(12_576_960, 415) + // Standard Error: 1_429 + .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -158,21 +163,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(c: u32, ) -> Weight { + fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 80_390_000 picoseconds. - Weight::from_parts(83_627_295, 4931) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 88_906_000 picoseconds. + Weight::from_parts(93_353_224, 7459) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -185,19 +190,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `6232` - // Minimum execution time: 184_708_000 picoseconds. - Weight::from_parts(177_995_416, 6232) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `416` + // Estimated: `6360` + // Minimum execution time: 202_688_000 picoseconds. + Weight::from_parts(197_366_807, 6360) + // Standard Error: 13 + .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -205,19 +212,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `4706` - // Minimum execution time: 150_137_000 picoseconds. - Weight::from_parts(136_548_469, 4706) + // Measured: `1313` + // Estimated: `4779` + // Minimum execution time: 169_246_000 picoseconds. + Weight::from_parts(149_480_457, 4779) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -230,17 +239,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 83_178_000 picoseconds. - Weight::from_parts(84_633_000, 4931) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 91_129_000 picoseconds. + Weight::from_parts(94_220_000, 7459) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. @@ -248,23 +257,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 51_526_000 picoseconds. - Weight::from_parts(54_565_973, 3574) + // Minimum execution time: 54_849_000 picoseconds. + Weight::from_parts(57_508_591, 3574) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 41_885_000 picoseconds. - Weight::from_parts(42_467_000, 3750) + // Minimum execution time: 45_017_000 picoseconds. + Weight::from_parts(46_312_000, 3750) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -274,113 +283,153 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `6432` - // Minimum execution time: 24_905_000 picoseconds. - Weight::from_parts(25_483_000, 6432) + // Measured: `529` + // Estimated: `6469` + // Minimum execution time: 26_992_000 picoseconds. + Weight::from_parts(28_781_000, 6469) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + fn map_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 44_031_000 picoseconds. + Weight::from_parts(45_133_000, 3574) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:0 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn unmap_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `56` + // Estimated: `3521` + // Minimum execution time: 35_681_000 picoseconds. + Weight::from_parts(36_331_000, 3521) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `Measured`) + fn dispatch_as_fallback_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 11_550_000 picoseconds. + Weight::from_parts(12_114_000, 3610) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } /// The range of component `r` is `[0, 1600]`. fn noop_host_fn(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_979_000 picoseconds. - Weight::from_parts(8_272_348, 0) - // Standard Error: 137 - .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) + // Minimum execution time: 7_063_000 picoseconds. + Weight::from_parts(7_671_454, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 251_000 picoseconds. - Weight::from_parts(318_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 6_966_000 picoseconds. - Weight::from_parts(7_240_000, 3737) + // Measured: `306` + // Estimated: `3771` + // Minimum execution time: 7_397_000 picoseconds. + Weight::from_parts(7_967_000, 3771) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `369` - // Estimated: `3834` - // Minimum execution time: 7_589_000 picoseconds. - Weight::from_parts(7_958_000, 3834) + // Measured: `403` + // Estimated: `3868` + // Minimum execution time: 8_395_000 picoseconds. + Weight::from_parts(8_863_000, 3868) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(292_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 283_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 298_000 picoseconds. + Weight::from_parts(334_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(298_000, 0) + // Minimum execution time: 262_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 277_000 picoseconds. + Weight::from_parts(297_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 651_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 620_000 picoseconds. + Weight::from_parts(706_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_476_000 picoseconds. - Weight::from_parts(4_671_000, 0) + // Minimum execution time: 5_475_000 picoseconds. + Weight::from_parts(5_706_000, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 3_800_000 picoseconds. - Weight::from_parts(3_968_000, 3517) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `264` + // Estimated: `3729` + // Minimum execution time: 9_141_000 picoseconds. + Weight::from_parts(9_674_000, 3729) + .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[1, 4096]`. fn seal_get_immutable_data(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `205 + n * (1 ±0)` - // Estimated: `3670 + n * (1 ±0)` - // Minimum execution time: 5_845_000 picoseconds. - Weight::from_parts(6_473_478, 3670) - // Standard Error: 4 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + // Measured: `238 + n * (1 ±0)` + // Estimated: `3703 + n * (1 ±0)` + // Minimum execution time: 6_443_000 picoseconds. + Weight::from_parts(7_252_595, 3703) + // Standard Error: 12 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -391,39 +440,39 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_980_000 picoseconds. - Weight::from_parts(2_324_567, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + // Minimum execution time: 2_745_000 picoseconds. + Weight::from_parts(3_121_250, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 249_000 picoseconds. + Weight::from_parts(263_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -431,8 +480,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_650_000 picoseconds. - Weight::from_parts(5_783_000, 1552) + // Minimum execution time: 6_147_000 picoseconds. + Weight::from_parts(6_562_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -440,21 +489,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 427_000 picoseconds. - Weight::from_parts(351_577, 0) + // Minimum execution time: 453_000 picoseconds. + Weight::from_parts(548_774, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(746_316, 0) + // Minimum execution time: 264_000 picoseconds. + Weight::from_parts(490_374, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:33 w:33) @@ -466,13 +517,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + n * (88 ±0)` - // Estimated: `3737 + n * (2563 ±0)` - // Minimum execution time: 15_988_000 picoseconds. - Weight::from_parts(18_796_705, 3737) - // Standard Error: 10_437 - .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `323 + n * (88 ±0)` + // Estimated: `3788 + n * (2563 ±0)` + // Minimum execution time: 22_833_000 picoseconds. + Weight::from_parts(24_805_620, 3788) + // Standard Error: 9_498 + .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -484,22 +535,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_128_112, 0) - // Standard Error: 2_947 - .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) + // Minimum execution time: 4_969_000 picoseconds. + Weight::from_parts(4_994_916, 0) + // Standard Error: 3_727 + .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 292_000 picoseconds. - Weight::from_parts(1_297_376, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(928_905, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -507,8 +558,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_812_000 picoseconds. - Weight::from_parts(8_171_000, 744) + // Minimum execution time: 8_612_000 picoseconds. + Weight::from_parts(9_326_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -517,8 +568,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_179_000 picoseconds. - Weight::from_parts(45_068_000, 10754) + // Minimum execution time: 44_542_000 picoseconds. + Weight::from_parts(45_397_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -527,8 +578,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_964_000 picoseconds. - Weight::from_parts(9_336_000, 744) + // Minimum execution time: 10_343_000 picoseconds. + Weight::from_parts(10_883_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -538,8 +589,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 45_606_000 picoseconds. - Weight::from_parts(47_190_000, 10754) + // Minimum execution time: 46_835_000 picoseconds. + Weight::from_parts(47_446_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -551,12 +602,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_077_000 picoseconds. - Weight::from_parts(9_823_489, 247) - // Standard Error: 54 - .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) - // Standard Error: 54 - .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) + // Minimum execution time: 10_604_000 picoseconds. + Weight::from_parts(11_282_849, 247) + // Standard Error: 48 + .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) + // Standard Error: 48 + .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -568,10 +619,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_812_000 picoseconds. - Weight::from_parts(9_626_925, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) + // Minimum execution time: 10_081_000 picoseconds. + Weight::from_parts(11_186_557, 247) + // Standard Error: 68 + .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -583,10 +634,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_143_000 picoseconds. - Weight::from_parts(9_229_363, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(9_939_492, 247) + // Standard Error: 69 + .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -597,10 +648,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_899_000 picoseconds. - Weight::from_parts(8_591_860, 247) - // Standard Error: 56 - .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) + // Minimum execution time: 8_525_000 picoseconds. + Weight::from_parts(9_522_265, 247) + // Standard Error: 66 + .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -611,10 +662,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_215_000 picoseconds. - Weight::from_parts(10_198_528, 247) - // Standard Error: 75 - .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) + // Minimum execution time: 10_603_000 picoseconds. + Weight::from_parts(11_817_752, 247) + // Standard Error: 82 + .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -623,36 +674,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_406_000 picoseconds. - Weight::from_parts(1_515_000, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_615_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_782_000 picoseconds. - Weight::from_parts(1_890_000, 0) + // Minimum execution time: 1_932_000 picoseconds. + Weight::from_parts(2_064_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_380_000 picoseconds. - Weight::from_parts(1_422_000, 0) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_545_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_583_000, 0) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(1_801_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_045_000 picoseconds. - Weight::from_parts(1_138_000, 0) + // Minimum execution time: 1_026_000 picoseconds. + Weight::from_parts(1_137_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -660,60 +711,63 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_039_000 picoseconds. - Weight::from_parts(2_317_406, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) - // Standard Error: 13 - .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) + // Minimum execution time: 2_446_000 picoseconds. + Weight::from_parts(2_644_525, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_251_392, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) + // Minimum execution time: 2_085_000 picoseconds. + Weight::from_parts(2_379_853, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_763_000 picoseconds. - Weight::from_parts(1_951_912, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) + // Minimum execution time: 1_876_000 picoseconds. + Weight::from_parts(2_073_689, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_536_000 picoseconds. - Weight::from_parts(1_779_085, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_914_470, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_343_000 picoseconds. - Weight::from_parts(2_587_750, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) + // Minimum execution time: 2_479_000 picoseconds. + Weight::from_parts(2_758_250, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `0` - // Minimum execution time: 9_250_000 picoseconds. - Weight::from_parts(9_637_000, 0) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 15_745_000 picoseconds. + Weight::from_parts(16_300_000, 3817) + .saturating_add(T::DbWeight::get().reads(1_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -724,17 +778,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1221 + t * (103 ±0)` - // Estimated: `4686 + t * (103 ±0)` - // Minimum execution time: 33_333_000 picoseconds. - Weight::from_parts(34_378_774, 4686) - // Standard Error: 41_131 - .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) + // Measured: `1309 + t * (140 ±0)` + // Estimated: `4774 + t * (140 ±0)` + // Minimum execution time: 39_639_000 picoseconds. + Weight::from_parts(40_909_376, 4774) + // Standard Error: 54_479 + .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -742,10 +796,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1048` - // Estimated: `4513` - // Minimum execution time: 27_096_000 picoseconds. - Weight::from_parts(27_934_000, 4513) + // Measured: `1081` + // Estimated: `4546` + // Minimum execution time: 29_651_000 picoseconds. + Weight::from_parts(31_228_000, 4546) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -759,12 +813,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1257` - // Estimated: `4715` - // Minimum execution time: 118_412_000 picoseconds. - Weight::from_parts(106_130_041, 4715) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + // Measured: `1327` + // Estimated: `4792` + // Minimum execution time: 126_995_000 picoseconds. + Weight::from_parts(114_028_446, 4792) + // Standard Error: 11 + .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -773,73 +827,73 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 614_000 picoseconds. - Weight::from_parts(4_320_897, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(973_524, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_062_000 picoseconds. - Weight::from_parts(4_571_371, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) + // Minimum execution time: 1_118_000 picoseconds. + Weight::from_parts(795_498, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 609_000 picoseconds. - Weight::from_parts(4_008_056, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(667_024, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 608_000 picoseconds. - Weight::from_parts(3_839_383, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) + // Minimum execution time: 605_000 picoseconds. + Weight::from_parts(675_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_110_000 picoseconds. - Weight::from_parts(31_941_593, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) + // Minimum execution time: 42_743_000 picoseconds. + Weight::from_parts(26_131_984, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_798_000 picoseconds. - Weight::from_parts(49_225_000, 0) + // Minimum execution time: 50_838_000 picoseconds. + Weight::from_parts(52_248_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_576_000 picoseconds. - Weight::from_parts(12_731_000, 0) + // Minimum execution time: 12_605_000 picoseconds. + Weight::from_parts(12_796_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `266` - // Estimated: `3731` - // Minimum execution time: 14_306_000 picoseconds. - Weight::from_parts(15_011_000, 3731) + // Measured: `300` + // Estimated: `3765` + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(16_932_000, 3765) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -847,10 +901,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `3768` - // Minimum execution time: 10_208_000 picoseconds. - Weight::from_parts(10_514_000, 3768) + // Measured: `338` + // Estimated: `3803` + // Minimum execution time: 11_499_000 picoseconds. + Weight::from_parts(12_104_000, 3803) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -858,10 +912,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` + // Measured: `338` // Estimated: `3561` - // Minimum execution time: 9_062_000 picoseconds. - Weight::from_parts(9_414_000, 3561) + // Minimum execution time: 10_308_000 picoseconds. + Weight::from_parts(11_000_000, 3561) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -870,10 +924,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_074_000 picoseconds. - Weight::from_parts(9_646_158, 0) - // Standard Error: 58 - .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(9_180_011, 0) + // Standard Error: 63 + .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) } } @@ -885,8 +939,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_712_000 picoseconds. - Weight::from_parts(2_882_000, 1594) + // Minimum execution time: 3_053_000 picoseconds. + Weight::from_parts(3_150_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -894,18 +948,20 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + k * (69 ±0)` - // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(13_668_000, 382) - // Standard Error: 2_208 - .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) + // Measured: `425 + k * (69 ±0)` + // Estimated: `415 + k * (70 ±0)` + // Minimum execution time: 15_219_000 picoseconds. + Weight::from_parts(12_576_960, 415) + // Standard Error: 1_429 + .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -917,21 +973,21 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(c: u32, ) -> Weight { + fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 80_390_000 picoseconds. - Weight::from_parts(83_627_295, 4931) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 88_906_000 picoseconds. + Weight::from_parts(93_353_224, 7459) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -944,19 +1000,21 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `6232` - // Minimum execution time: 184_708_000 picoseconds. - Weight::from_parts(177_995_416, 6232) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `416` + // Estimated: `6360` + // Minimum execution time: 202_688_000 picoseconds. + Weight::from_parts(197_366_807, 6360) + // Standard Error: 13 + .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -964,19 +1022,21 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `4706` - // Minimum execution time: 150_137_000 picoseconds. - Weight::from_parts(136_548_469, 4706) + // Measured: `1313` + // Estimated: `4779` + // Minimum execution time: 169_246_000 picoseconds. + Weight::from_parts(149_480_457, 4779) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -989,17 +1049,17 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 83_178_000 picoseconds. - Weight::from_parts(84_633_000, 4931) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 91_129_000 picoseconds. + Weight::from_parts(94_220_000, 7459) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. @@ -1007,23 +1067,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 51_526_000 picoseconds. - Weight::from_parts(54_565_973, 3574) + // Minimum execution time: 54_849_000 picoseconds. + Weight::from_parts(57_508_591, 3574) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 41_885_000 picoseconds. - Weight::from_parts(42_467_000, 3750) + // Minimum execution time: 45_017_000 picoseconds. + Weight::from_parts(46_312_000, 3750) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1033,113 +1093,153 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `6432` - // Minimum execution time: 24_905_000 picoseconds. - Weight::from_parts(25_483_000, 6432) + // Measured: `529` + // Estimated: `6469` + // Minimum execution time: 26_992_000 picoseconds. + Weight::from_parts(28_781_000, 6469) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + fn map_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 44_031_000 picoseconds. + Weight::from_parts(45_133_000, 3574) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:0 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn unmap_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `56` + // Estimated: `3521` + // Minimum execution time: 35_681_000 picoseconds. + Weight::from_parts(36_331_000, 3521) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `Measured`) + fn dispatch_as_fallback_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 11_550_000 picoseconds. + Weight::from_parts(12_114_000, 3610) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } /// The range of component `r` is `[0, 1600]`. fn noop_host_fn(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_979_000 picoseconds. - Weight::from_parts(8_272_348, 0) - // Standard Error: 137 - .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) + // Minimum execution time: 7_063_000 picoseconds. + Weight::from_parts(7_671_454, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 251_000 picoseconds. - Weight::from_parts(318_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 6_966_000 picoseconds. - Weight::from_parts(7_240_000, 3737) + // Measured: `306` + // Estimated: `3771` + // Minimum execution time: 7_397_000 picoseconds. + Weight::from_parts(7_967_000, 3771) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `369` - // Estimated: `3834` - // Minimum execution time: 7_589_000 picoseconds. - Weight::from_parts(7_958_000, 3834) + // Measured: `403` + // Estimated: `3868` + // Minimum execution time: 8_395_000 picoseconds. + Weight::from_parts(8_863_000, 3868) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(292_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 283_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 298_000 picoseconds. + Weight::from_parts(334_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(298_000, 0) + // Minimum execution time: 262_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 277_000 picoseconds. + Weight::from_parts(297_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 651_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 620_000 picoseconds. + Weight::from_parts(706_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_476_000 picoseconds. - Weight::from_parts(4_671_000, 0) + // Minimum execution time: 5_475_000 picoseconds. + Weight::from_parts(5_706_000, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 3_800_000 picoseconds. - Weight::from_parts(3_968_000, 3517) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `264` + // Estimated: `3729` + // Minimum execution time: 9_141_000 picoseconds. + Weight::from_parts(9_674_000, 3729) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[1, 4096]`. fn seal_get_immutable_data(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `205 + n * (1 ±0)` - // Estimated: `3670 + n * (1 ±0)` - // Minimum execution time: 5_845_000 picoseconds. - Weight::from_parts(6_473_478, 3670) - // Standard Error: 4 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + // Measured: `238 + n * (1 ±0)` + // Estimated: `3703 + n * (1 ±0)` + // Minimum execution time: 6_443_000 picoseconds. + Weight::from_parts(7_252_595, 3703) + // Standard Error: 12 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1150,39 +1250,39 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_980_000 picoseconds. - Weight::from_parts(2_324_567, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + // Minimum execution time: 2_745_000 picoseconds. + Weight::from_parts(3_121_250, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 249_000 picoseconds. + Weight::from_parts(263_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1190,8 +1290,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_650_000 picoseconds. - Weight::from_parts(5_783_000, 1552) + // Minimum execution time: 6_147_000 picoseconds. + Weight::from_parts(6_562_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -1199,21 +1299,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 427_000 picoseconds. - Weight::from_parts(351_577, 0) + // Minimum execution time: 453_000 picoseconds. + Weight::from_parts(548_774, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(746_316, 0) + // Minimum execution time: 264_000 picoseconds. + Weight::from_parts(490_374, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:33 w:33) @@ -1225,13 +1327,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + n * (88 ±0)` - // Estimated: `3737 + n * (2563 ±0)` - // Minimum execution time: 15_988_000 picoseconds. - Weight::from_parts(18_796_705, 3737) - // Standard Error: 10_437 - .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `323 + n * (88 ±0)` + // Estimated: `3788 + n * (2563 ±0)` + // Minimum execution time: 22_833_000 picoseconds. + Weight::from_parts(24_805_620, 3788) + // Standard Error: 9_498 + .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -1243,22 +1345,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_128_112, 0) - // Standard Error: 2_947 - .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) + // Minimum execution time: 4_969_000 picoseconds. + Weight::from_parts(4_994_916, 0) + // Standard Error: 3_727 + .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 292_000 picoseconds. - Weight::from_parts(1_297_376, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(928_905, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1266,8 +1368,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_812_000 picoseconds. - Weight::from_parts(8_171_000, 744) + // Minimum execution time: 8_612_000 picoseconds. + Weight::from_parts(9_326_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1276,8 +1378,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_179_000 picoseconds. - Weight::from_parts(45_068_000, 10754) + // Minimum execution time: 44_542_000 picoseconds. + Weight::from_parts(45_397_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1286,8 +1388,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_964_000 picoseconds. - Weight::from_parts(9_336_000, 744) + // Minimum execution time: 10_343_000 picoseconds. + Weight::from_parts(10_883_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1297,8 +1399,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 45_606_000 picoseconds. - Weight::from_parts(47_190_000, 10754) + // Minimum execution time: 46_835_000 picoseconds. + Weight::from_parts(47_446_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1310,12 +1412,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_077_000 picoseconds. - Weight::from_parts(9_823_489, 247) - // Standard Error: 54 - .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) - // Standard Error: 54 - .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) + // Minimum execution time: 10_604_000 picoseconds. + Weight::from_parts(11_282_849, 247) + // Standard Error: 48 + .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) + // Standard Error: 48 + .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1327,10 +1429,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_812_000 picoseconds. - Weight::from_parts(9_626_925, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) + // Minimum execution time: 10_081_000 picoseconds. + Weight::from_parts(11_186_557, 247) + // Standard Error: 68 + .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1342,10 +1444,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_143_000 picoseconds. - Weight::from_parts(9_229_363, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(9_939_492, 247) + // Standard Error: 69 + .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1356,10 +1458,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_899_000 picoseconds. - Weight::from_parts(8_591_860, 247) - // Standard Error: 56 - .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) + // Minimum execution time: 8_525_000 picoseconds. + Weight::from_parts(9_522_265, 247) + // Standard Error: 66 + .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1370,10 +1472,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_215_000 picoseconds. - Weight::from_parts(10_198_528, 247) - // Standard Error: 75 - .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) + // Minimum execution time: 10_603_000 picoseconds. + Weight::from_parts(11_817_752, 247) + // Standard Error: 82 + .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1382,36 +1484,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_406_000 picoseconds. - Weight::from_parts(1_515_000, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_615_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_782_000 picoseconds. - Weight::from_parts(1_890_000, 0) + // Minimum execution time: 1_932_000 picoseconds. + Weight::from_parts(2_064_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_380_000 picoseconds. - Weight::from_parts(1_422_000, 0) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_545_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_583_000, 0) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(1_801_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_045_000 picoseconds. - Weight::from_parts(1_138_000, 0) + // Minimum execution time: 1_026_000 picoseconds. + Weight::from_parts(1_137_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -1419,60 +1521,63 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_039_000 picoseconds. - Weight::from_parts(2_317_406, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) - // Standard Error: 13 - .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) + // Minimum execution time: 2_446_000 picoseconds. + Weight::from_parts(2_644_525, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_251_392, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) + // Minimum execution time: 2_085_000 picoseconds. + Weight::from_parts(2_379_853, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_763_000 picoseconds. - Weight::from_parts(1_951_912, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) + // Minimum execution time: 1_876_000 picoseconds. + Weight::from_parts(2_073_689, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_536_000 picoseconds. - Weight::from_parts(1_779_085, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_914_470, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_343_000 picoseconds. - Weight::from_parts(2_587_750, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) + // Minimum execution time: 2_479_000 picoseconds. + Weight::from_parts(2_758_250, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `0` - // Minimum execution time: 9_250_000 picoseconds. - Weight::from_parts(9_637_000, 0) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 15_745_000 picoseconds. + Weight::from_parts(16_300_000, 3817) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -1483,17 +1588,17 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1221 + t * (103 ±0)` - // Estimated: `4686 + t * (103 ±0)` - // Minimum execution time: 33_333_000 picoseconds. - Weight::from_parts(34_378_774, 4686) - // Standard Error: 41_131 - .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) + // Measured: `1309 + t * (140 ±0)` + // Estimated: `4774 + t * (140 ±0)` + // Minimum execution time: 39_639_000 picoseconds. + Weight::from_parts(40_909_376, 4774) + // Standard Error: 54_479 + .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1501,10 +1606,10 @@ impl WeightInfo for () { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1048` - // Estimated: `4513` - // Minimum execution time: 27_096_000 picoseconds. - Weight::from_parts(27_934_000, 4513) + // Measured: `1081` + // Estimated: `4546` + // Minimum execution time: 29_651_000 picoseconds. + Weight::from_parts(31_228_000, 4546) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -1518,12 +1623,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1257` - // Estimated: `4715` - // Minimum execution time: 118_412_000 picoseconds. - Weight::from_parts(106_130_041, 4715) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + // Measured: `1327` + // Estimated: `4792` + // Minimum execution time: 126_995_000 picoseconds. + Weight::from_parts(114_028_446, 4792) + // Standard Error: 11 + .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1532,73 +1637,73 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 614_000 picoseconds. - Weight::from_parts(4_320_897, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(973_524, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_062_000 picoseconds. - Weight::from_parts(4_571_371, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) + // Minimum execution time: 1_118_000 picoseconds. + Weight::from_parts(795_498, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 609_000 picoseconds. - Weight::from_parts(4_008_056, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(667_024, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 608_000 picoseconds. - Weight::from_parts(3_839_383, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) + // Minimum execution time: 605_000 picoseconds. + Weight::from_parts(675_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_110_000 picoseconds. - Weight::from_parts(31_941_593, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) + // Minimum execution time: 42_743_000 picoseconds. + Weight::from_parts(26_131_984, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_798_000 picoseconds. - Weight::from_parts(49_225_000, 0) + // Minimum execution time: 50_838_000 picoseconds. + Weight::from_parts(52_248_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_576_000 picoseconds. - Weight::from_parts(12_731_000, 0) + // Minimum execution time: 12_605_000 picoseconds. + Weight::from_parts(12_796_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `266` - // Estimated: `3731` - // Minimum execution time: 14_306_000 picoseconds. - Weight::from_parts(15_011_000, 3731) + // Measured: `300` + // Estimated: `3765` + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(16_932_000, 3765) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1606,10 +1711,10 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `3768` - // Minimum execution time: 10_208_000 picoseconds. - Weight::from_parts(10_514_000, 3768) + // Measured: `338` + // Estimated: `3803` + // Minimum execution time: 11_499_000 picoseconds. + Weight::from_parts(12_104_000, 3803) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1617,10 +1722,10 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` + // Measured: `338` // Estimated: `3561` - // Minimum execution time: 9_062_000 picoseconds. - Weight::from_parts(9_414_000, 3561) + // Minimum execution time: 10_308_000 picoseconds. + Weight::from_parts(11_000_000, 3561) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1629,9 +1734,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_074_000 picoseconds. - Weight::from_parts(9_646_158, 0) - // Standard Error: 58 - .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(9_180_011, 0) + // Standard Error: 63 + .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) } } From 807dc3b1eb69d19a31de60a875de623433f0287e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 25 Oct 2024 11:31:21 +0200 Subject: [PATCH 43/58] Fix migrations for pallet-xcm (#6148) Relates to: https://github.com/paritytech/polkadot-sdk/pull/4826 Relates to: https://github.com/paritytech/polkadot-sdk/issues/3214 ## Description `pallet-xcm` stores some operational data that uses `Versioned*` XCM types. When we add a new XCM version (XV), we deprecate XV-2 and remove XV-3. Without proper migration, this can lead to issues with [undecodable storage](https://github.com/paritytech/polkadot-sdk/actions/runs/11381324568/job/31662577532?pr=6092), as was identified on the XCMv5 branch where XCMv2 was removed. This PR extends the existing `MigrateToLatestXcmVersion` to include migration for the `Queries`, `LockedFungibles`, and `RemoteLockedFungibles` storage types. Additionally, more checks were added to `try_state` for these types. ## TODO - [x] create tracking issue for `polkadot-fellows` https://github.com/polkadot-fellows/runtimes/issues/492 - [x] Add missing `MigrateToLatestXcmVersion` for westend - [x] fix pallet-xcm `Queries` - fails for Westend https://github.com/paritytech/polkadot-sdk/actions/runs/11381324568/job/31662577532?pr=6092 - `V2` was removed from `Versioned*` stuff, but we have a live data with V2 e.g. Queries - e.g. Kusama or Polkadot relay chains ``` VersionNotifier: { origin: { V2: { parents: 0 interior: { X1: { Parachain: 2,124 } } } } isActive: true } ``` ![image](https://github.com/user-attachments/assets/f59f761b-46a7-4def-8aea-45c4e41c0a00) - [x] fix also for `RemoteLockedFungibles` - [x] fix also for `LockedFungibles` ## Follow-ups - [ ] deploy on Westend chains before XCMv5 - [ ] https://github.com/paritytech/polkadot-sdk/issues/6188 --------- Co-authored-by: command-bot <> Co-authored-by: GitHub Action Co-authored-by: Francisco Aguirre --- polkadot/runtime/westend/src/lib.rs | 2 + .../parachain/xcm_config.rs | 2 +- .../relay_chain/xcm_config.rs | 2 +- polkadot/xcm/pallet-xcm/src/lib.rs | 42 +- polkadot/xcm/pallet-xcm/src/migration.rs | 379 +++++++++++++++++- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 172 +++++++- .../single_asset_adapter/mock.rs | 2 +- .../xcm/xcm-builder/src/tests/pay/mock.rs | 3 +- polkadot/xcm/xcm-runtime-apis/tests/mock.rs | 3 +- prdoc/pr_6148.prdoc | 17 + 10 files changed, 610 insertions(+), 14 deletions(-) create mode 100644 prdoc/pr_6148.prdoc diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index a91ca399db41..4941d91df57d 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1809,6 +1809,8 @@ pub mod migrations { >, parachains_shared::migration::MigrateToV1, parachains_scheduler::migration::MigrateV2ToV3, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); } diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs index 7cb230f6e006..f8a1826b8ab1 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs @@ -168,7 +168,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs index a31e664d8216..e7b602df7338 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs @@ -142,7 +142,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 254bc2c7b83d..9b8f735b478f 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2807,6 +2807,44 @@ impl Pallet { /// set. #[cfg(any(feature = "try-runtime", test))] pub fn do_try_state() -> Result<(), TryRuntimeError> { + use migration::data::NeedsMigration; + + // Take the minimum version between `SafeXcmVersion` and `latest - 1` and ensure that the + // operational data is stored at least at that version, for example, to prevent issues when + // removing older XCM versions. + let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::::get() + { + XCM_VERSION.saturating_sub(1).min(safe_xcm_version) + } else { + XCM_VERSION.saturating_sub(1) + }; + + // check `Queries` + ensure!( + !Queries::::iter_values() + .any(|data| data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!") + ); + + // check `LockedFungibles` + ensure!( + !LockedFungibles::::iter_values() + .any(|data| data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other( + "`LockedFungibles` data should be migrated to the higher xcm version!" + ) + ); + + // check `RemoteLockedFungibles` + ensure!( + !RemoteLockedFungibles::::iter() + .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) || + data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other( + "`RemoteLockedFungibles` data should be migrated to the higher xcm version!" + ) + ); + // if migration has been already scheduled, everything is ok and data will be eventually // migrated if CurrentMigration::::exists() { @@ -2887,7 +2925,7 @@ impl xcm_executor::traits::Enact for UnlockTicket { let mut maybe_remove_index = None; let mut locked = BalanceOf::::zero(); let mut found = false; - // We could just as well do with with an into_iter, filter_map and collect, however this way + // We could just as well do with an into_iter, filter_map and collect, however this way // avoids making an allocation. for (i, x) in locks.iter_mut().enumerate() { if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) { @@ -3268,7 +3306,7 @@ impl OnResponse for Pallet { }); return Weight::zero() } - return match maybe_notify { + match maybe_notify { Some((pallet_index, call_index)) => { // This is a bit horrible, but we happen to know that the `Call` will // be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`. diff --git a/polkadot/xcm/pallet-xcm/src/migration.rs b/polkadot/xcm/pallet-xcm/src/migration.rs index 2c5b2620f535..80154f57ddfb 100644 --- a/polkadot/xcm/pallet-xcm/src/migration.rs +++ b/polkadot/xcm/pallet-xcm/src/migration.rs @@ -15,7 +15,8 @@ // along with Polkadot. If not, see . use crate::{ - pallet::CurrentMigration, Config, Pallet, VersionMigrationStage, VersionNotifyTargets, + pallet::CurrentMigration, Config, CurrentXcmVersion, Pallet, VersionMigrationStage, + VersionNotifyTargets, }; use frame_support::{ pallet_prelude::*, @@ -25,6 +26,307 @@ use frame_support::{ const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; +/// Utilities for handling XCM version migration for the relevant data. +pub mod data { + use crate::*; + + /// A trait for handling XCM versioned data migration for the requested `XcmVersion`. + pub(crate) trait NeedsMigration { + type MigratedData; + + /// Returns true if data does not match `minimal_allowed_xcm_version`. + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool; + + /// Attempts to migrate data. `Ok(None)` means no migration is needed. + /// `Ok(Some(Self::MigratedData))` should contain the migrated data. + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()>; + } + + /// Implementation of `NeedsMigration` for `LockedFungibles` data. + impl NeedsMigration for BoundedVec<(B, VersionedLocation), M> { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.iter() + .any(|(_, unlocker)| unlocker.identify_version() < minimal_allowed_xcm_version) + } + + fn try_migrate( + mut self, + to_xcm_version: XcmVersion, + ) -> Result, ()> { + let mut was_modified = false; + for locked in self.iter_mut() { + if locked.1.identify_version() < to_xcm_version { + let Ok(new_unlocker) = locked.1.clone().into_version(to_xcm_version) else { + return Err(()) + }; + locked.1 = new_unlocker; + was_modified = true; + } + } + + if was_modified { + Ok(Some(self)) + } else { + Ok(None) + } + } + } + + /// Implementation of `NeedsMigration` for `Queries` data. + impl NeedsMigration for QueryStatus { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + match &self { + QueryStatus::Pending { responder, maybe_match_querier, .. } => + responder.identify_version() < minimal_allowed_xcm_version || + maybe_match_querier + .as_ref() + .map(|v| v.identify_version() < minimal_allowed_xcm_version) + .unwrap_or(false), + QueryStatus::VersionNotifier { origin, .. } => + origin.identify_version() < minimal_allowed_xcm_version, + QueryStatus::Ready { response, .. } => + response.identify_version() < minimal_allowed_xcm_version, + } + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + // do migration + match self { + QueryStatus::Pending { responder, maybe_match_querier, maybe_notify, timeout } => { + let Ok(responder) = responder.into_version(to_xcm_version) else { + return Err(()) + }; + let Ok(maybe_match_querier) = + maybe_match_querier.map(|mmq| mmq.into_version(to_xcm_version)).transpose() + else { + return Err(()) + }; + Ok(Some(QueryStatus::Pending { + responder, + maybe_match_querier, + maybe_notify, + timeout, + })) + }, + QueryStatus::VersionNotifier { origin, is_active } => origin + .into_version(to_xcm_version) + .map(|origin| Some(QueryStatus::VersionNotifier { origin, is_active })), + QueryStatus::Ready { response, at } => response + .into_version(to_xcm_version) + .map(|response| Some(QueryStatus::Ready { response, at })), + } + } + } + + /// Implementation of `NeedsMigration` for `RemoteLockedFungibles` key type. + impl NeedsMigration for (XcmVersion, A, VersionedAssetId) { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.0 < minimal_allowed_xcm_version || + self.2.identify_version() < minimal_allowed_xcm_version + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + let Ok(asset_id) = self.2.into_version(to_xcm_version) else { return Err(()) }; + Ok(Some((to_xcm_version, self.1, asset_id))) + } + } + + /// Implementation of `NeedsMigration` for `RemoteLockedFungibles` data. + impl> NeedsMigration + for RemoteLockedFungibleRecord + { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.owner.identify_version() < minimal_allowed_xcm_version || + self.locker.identify_version() < minimal_allowed_xcm_version + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + let RemoteLockedFungibleRecord { amount, owner, locker, consumers } = self; + + let Ok(owner) = owner.into_version(to_xcm_version) else { return Err(()) }; + let Ok(locker) = locker.into_version(to_xcm_version) else { return Err(()) }; + + Ok(Some(RemoteLockedFungibleRecord { amount, owner, locker, consumers })) + } + } + + impl Pallet { + /// Migrates relevant data to the `required_xcm_version`. + pub(crate) fn migrate_data_to_xcm_version( + weight: &mut Weight, + required_xcm_version: XcmVersion, + ) { + const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_data_to_xcm_version"; + + // check and migrate `Queries` + let queries_to_migrate = Queries::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`Queries` cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in queries_to_migrate { + tracing::info!( + target: LOG_TARGET, + query_id = ?id, + ?new_data, + "Migrating `Queries`" + ); + Queries::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `LockedFungibles` + let locked_fungibles_to_migrate = + LockedFungibles::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`LockedFungibles` cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in locked_fungibles_to_migrate { + tracing::info!( + target: LOG_TARGET, + account_id = ?id, + ?new_data, + "Migrating `LockedFungibles`" + ); + LockedFungibles::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `RemoteLockedFungibles` - 1. step - just data + let remote_locked_fungibles_to_migrate = + RemoteLockedFungibles::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`RemoteLockedFungibles` data cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in remote_locked_fungibles_to_migrate { + tracing::info!( + target: LOG_TARGET, + key = ?id, + amount = ?new_data.amount, + locker = ?new_data.locker, + owner = ?new_data.owner, + consumers_count = ?new_data.consumers.len(), + "Migrating `RemoteLockedFungibles` data" + ); + RemoteLockedFungibles::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `RemoteLockedFungibles` - 2. step - key + let remote_locked_fungibles_keys_to_migrate = RemoteLockedFungibles::::iter_keys() + .filter_map(|key| { + if key.needs_migration(required_xcm_version) { + let old_key = key.clone(); + match key.try_migrate(required_xcm_version) { + Ok(Some(new_key)) => Some((old_key, new_key)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + id = ?old_key, + ?required_xcm_version, + "`RemoteLockedFungibles` key cannot be migrated!" + ); + None + }, + } + } else { + None + } + }); + for (old_key, new_key) in remote_locked_fungibles_keys_to_migrate { + weight.saturating_add(T::DbWeight::get().reads(1)); + // make sure, that we don't override accidentally other data + if RemoteLockedFungibles::::get(&new_key).is_some() { + tracing::error!( + target: LOG_TARGET, + ?old_key, + ?new_key, + "`RemoteLockedFungibles` already contains data for a `new_key`!" + ); + // let's just skip for now, could be potentially caused with missing this + // migration before (manual clean-up?). + continue; + } + + tracing::info!( + target: LOG_TARGET, + ?old_key, + ?new_key, + "Migrating `RemoteLockedFungibles` key" + ); + + // now we can swap the keys + RemoteLockedFungibles::::swap::< + ( + NMapKey, + NMapKey, + NMapKey, + ), + _, + _, + >(&old_key, &new_key); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + } +} + pub mod v1 { use super::*; use crate::{CurrentMigration, VersionMigrationStage}; @@ -84,7 +386,80 @@ pub mod v1 { pub struct MigrateToLatestXcmVersion(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToLatestXcmVersion { fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // trigger expensive/lazy migration (kind of multi-block) CurrentMigration::::put(VersionMigrationStage::default()); - T::DbWeight::get().writes(1) + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + // migrate other operational data to the latest XCM version in-place + let latest = CurrentXcmVersion::get(); + Pallet::::migrate_data_to_xcm_version(&mut weight, latest); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: alloc::vec::Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use data::NeedsMigration; + const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_to_latest"; + + let latest = CurrentXcmVersion::get(); + + let number_of_queries_to_migrate = crate::Queries::::iter() + .filter(|(id, data)| { + let needs_migration = data.needs_migration(latest); + if needs_migration { + tracing::warn!( + target: LOG_TARGET, + query_id = ?id, + query = ?data, + "Query was not migrated!" + ) + } + needs_migration + }) + .count(); + + let number_of_locked_fungibles_to_migrate = crate::LockedFungibles::::iter() + .filter_map(|(id, data)| { + if data.needs_migration(latest) { + tracing::warn!( + target: LOG_TARGET, + ?id, + ?data, + "LockedFungibles item was not migrated!" + ); + Some(true) + } else { + None + } + }) + .count(); + + let number_of_remote_locked_fungibles_to_migrate = + crate::RemoteLockedFungibles::::iter() + .filter_map(|(key, data)| { + if key.needs_migration(latest) || data.needs_migration(latest) { + tracing::warn!( + target: LOG_TARGET, + ?key, + "RemoteLockedFungibles item was not migrated!" + ); + Some(true) + } else { + None + } + }) + .count(); + + ensure!(number_of_queries_to_migrate == 0, "must migrate all `Queries`."); + ensure!(number_of_locked_fungibles_to_migrate == 0, "must migrate all `LockedFungibles`."); + ensure!( + number_of_remote_locked_fungibles_to_migrate == 0, + "must migrate all `RemoteLockedFungibles`." + ); + + Ok(()) } } diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index c16c1a1ba986..e98a8f8d2ce7 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -19,11 +19,15 @@ pub(crate) mod assets_transfer; use crate::{ - mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, - RecordedXcm, ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, + migration::data::NeedsMigration, + mock::*, + pallet::{LockedFungibles, RemoteLockedFungibles, SupportedVersion}, + AssetTraps, Config, CurrentMigration, Error, ExecuteControllerWeightInfo, + LatestVersionedLocation, Pallet, Queries, QueryStatus, RecordedXcm, RemoteLockedFungibleRecord, + ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo, }; +use bounded_collections::BoundedVec; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, traits::{Currency, Hooks}, @@ -1258,6 +1262,168 @@ fn multistage_migration_works() { }) } +#[test] +fn migrate_data_to_xcm_version_works() { + new_test_ext_with_balances(vec![]).execute_with(|| { + // check `try-state` + assert!(Pallet::::do_try_state().is_ok()); + + let latest_version = XCM_VERSION; + let previous_version = XCM_VERSION - 1; + + // `Queries` migration + { + let origin = VersionedLocation::from(Location::parent()); + let query_id1 = 0; + let query_id2 = 2; + let query_as_latest = + QueryStatus::VersionNotifier { origin: origin.clone(), is_active: true }; + let query_as_previous = QueryStatus::VersionNotifier { + origin: origin.into_version(previous_version).unwrap(), + is_active: true, + }; + assert_ne!(query_as_latest, query_as_previous); + assert!(!query_as_latest.needs_migration(latest_version)); + assert!(!query_as_latest.needs_migration(previous_version)); + assert!(query_as_previous.needs_migration(latest_version)); + assert!(!query_as_previous.needs_migration(previous_version)); + + // store two queries: migrated and not migrated + Queries::::insert(query_id1, query_as_latest.clone()); + Queries::::insert(query_id2, query_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + // no change for query_id1 + assert_eq!(Queries::::get(query_id1), Some(query_as_latest.clone())); + // change for query_id2 + assert_eq!(Queries::::get(query_id2), Some(query_as_latest)); + assert!(Pallet::::do_try_state().is_ok()); + } + + // `LockedFungibles` migration + { + let account1 = AccountId::new([13u8; 32]); + let account2 = AccountId::new([58u8; 32]); + let unlocker = VersionedLocation::from(Location::parent()); + let lockeds_as_latest = BoundedVec::truncate_from(vec![(0, unlocker.clone())]); + let lockeds_as_previous = BoundedVec::truncate_from(vec![( + 0, + unlocker.into_version(previous_version).unwrap(), + )]); + assert_ne!(lockeds_as_latest, lockeds_as_previous); + assert!(!lockeds_as_latest.needs_migration(latest_version)); + assert!(!lockeds_as_latest.needs_migration(previous_version)); + assert!(lockeds_as_previous.needs_migration(latest_version)); + assert!(!lockeds_as_previous.needs_migration(previous_version)); + + // store two lockeds: migrated and not migrated + LockedFungibles::::insert(&account1, lockeds_as_latest.clone()); + LockedFungibles::::insert(&account2, lockeds_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + // no change for account1 + assert_eq!(LockedFungibles::::get(&account1), Some(lockeds_as_latest.clone())); + // change for account2 + assert_eq!(LockedFungibles::::get(&account2), Some(lockeds_as_latest)); + assert!(Pallet::::do_try_state().is_ok()); + } + + // `RemoteLockedFungibles` migration + { + let account1 = AccountId::new([13u8; 32]); + let account2 = AccountId::new([58u8; 32]); + let account3 = AccountId::new([97u8; 32]); + let asset_id = VersionedAssetId::from(AssetId(Location::parent())); + let owner = VersionedLocation::from(Location::parent()); + let locker = VersionedLocation::from(Location::parent()); + let key1_as_latest = (latest_version, account1, asset_id.clone()); + let key2_as_latest = (latest_version, account2, asset_id.clone()); + let key3_as_previous = ( + previous_version, + account3.clone(), + asset_id.clone().into_version(previous_version).unwrap(), + ); + let expected_key3_as_latest = (latest_version, account3, asset_id); + let data_as_latest = RemoteLockedFungibleRecord { + amount: Default::default(), + owner: owner.clone(), + locker: locker.clone(), + consumers: Default::default(), + }; + let data_as_previous = RemoteLockedFungibleRecord { + amount: Default::default(), + owner: owner.into_version(previous_version).unwrap(), + locker: locker.into_version(previous_version).unwrap(), + consumers: Default::default(), + }; + assert_ne!(data_as_latest.owner, data_as_previous.owner); + assert_ne!(data_as_latest.locker, data_as_previous.locker); + assert!(!key1_as_latest.needs_migration(latest_version)); + assert!(!key1_as_latest.needs_migration(previous_version)); + assert!(!key2_as_latest.needs_migration(latest_version)); + assert!(!key2_as_latest.needs_migration(previous_version)); + assert!(key3_as_previous.needs_migration(latest_version)); + assert!(!key3_as_previous.needs_migration(previous_version)); + assert!(!expected_key3_as_latest.needs_migration(latest_version)); + assert!(!expected_key3_as_latest.needs_migration(previous_version)); + assert!(!data_as_latest.needs_migration(latest_version)); + assert!(!data_as_latest.needs_migration(previous_version)); + assert!(data_as_previous.needs_migration(latest_version)); + assert!(!data_as_previous.needs_migration(previous_version)); + + // store three lockeds: + // fully migrated + RemoteLockedFungibles::::insert(&key1_as_latest, data_as_latest.clone()); + // only key migrated + RemoteLockedFungibles::::insert(&key2_as_latest, data_as_previous.clone()); + // neither key nor data migrated + RemoteLockedFungibles::::insert(&key3_as_previous, data_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + let assert_locked_eq = + |left: Option>, + right: Option>| { + match (left, right) { + (None, Some(_)) | (Some(_), None) => + assert!(false, "Received unexpected message"), + (None, None) => (), + (Some(l), Some(r)) => { + assert_eq!(l.owner, r.owner); + assert_eq!(l.locker, r.locker); + }, + } + }; + + // no change + assert_locked_eq( + RemoteLockedFungibles::::get(&key1_as_latest), + Some(data_as_latest.clone()), + ); + // change - data migrated + assert_locked_eq( + RemoteLockedFungibles::::get(&key2_as_latest), + Some(data_as_latest.clone()), + ); + // fully migrated + assert_locked_eq(RemoteLockedFungibles::::get(&key3_as_previous), None); + assert_locked_eq( + RemoteLockedFungibles::::get(&expected_key3_as_latest), + Some(data_as_latest.clone()), + ); + assert!(Pallet::::do_try_state().is_ok()); + } + }) +} + #[test] fn record_xcm_works() { let balances = vec![(ALICE, INITIAL_BALANCE)]; diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs index 4d9809e84f88..e6fe8e45c265 100644 --- a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs @@ -312,7 +312,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame_support::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index d76ff21b8597..26ea226313f0 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -126,7 +126,6 @@ parameter_types! { pub const AnyNetwork: Option = None; pub UniversalLocation: InteriorLocation = (ByGenesis([0; 32]), Parachain(42)).into(); pub UnitWeightCost: u64 = 1_000; - pub static AdvertisedXcmVersion: u32 = 3; pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); pub CurrencyPerSecondPerByte: (AssetId, u128, u128) = (AssetId(RelayLocation::get()), 1, 1); pub TrustedAssets: (AssetFilter, Location) = (All.into(), Here.into()); @@ -267,7 +266,7 @@ impl pallet_xcm::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type TrustedLockers = (); type SovereignAccountOf = SovereignAccountOf; type Currency = Balances; diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index b3afa23503e3..4a5de8875007 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -145,7 +145,6 @@ parameter_types! { pub const MaxInstructions: u32 = 100; pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); - pub static AdvertisedXcmVersion: XcmVersion = 4; pub const HereLocation: Location = Location::here(); pub const RelayLocation: Location = Location::parent(); pub const MaxAssetsIntoHolding: u32 = 64; @@ -349,7 +348,7 @@ impl pallet_xcm::Config for TestRuntime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = EnsureRoot; type TrustedLockers = (); type SovereignAccountOf = (); diff --git a/prdoc/pr_6148.prdoc b/prdoc/pr_6148.prdoc new file mode 100644 index 000000000000..430a58dfefbb --- /dev/null +++ b/prdoc/pr_6148.prdoc @@ -0,0 +1,17 @@ +title: Fix migrations for pallet-xcm +doc: +- audience: Runtime Dev + description: |- + `pallet-xcm` stores some operational data that uses `Versioned*` XCM types. When we add a new XCM version (XV), we deprecate XV-2 and remove XV-3. + This PR extends the existing `MigrateToLatestXcmVersion` to include migration for the `Queries`, `LockedFungibles`, and `RemoteLockedFungibles` storage types. + Additionally, more checks were added to `try_state` for these types. + +crates: +- name: westend-runtime + bump: patch +- name: staging-xcm-builder + bump: none +- name: xcm-runtime-apis + bump: none +- name: pallet-xcm + bump: patch From ce09c208a22c1c6dad3fb335bf7f9a3b500f73f6 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 25 Oct 2024 17:40:42 +0300 Subject: [PATCH 44/58] asset-hubs: simplify xcm-config (#6222) `ForeignCreatorsSovereignAccountOf` is used by `ForeignCreators` filter to convert location to `AccountId`, _after_ `ForeignCreators::IsForeign` filter passes for an (asset, location) pair. The `IsForeign` filter is the actual differentiator, so if a location passes it, we should support converting it to an `AccountId`. As such, this commit replaces `ForeignCreatorsSovereignAccountOf` converter with the more general `LocationToAccountId` converter. Signed-off-by: Adrian Catangiu --- .../runtimes/assets/asset-hub-rococo/src/lib.rs | 4 ++-- .../runtimes/assets/asset-hub-rococo/src/xcm_config.rs | 8 -------- .../runtimes/assets/asset-hub-rococo/tests/tests.rs | 8 ++++---- .../runtimes/assets/asset-hub-westend/src/lib.rs | 8 ++++---- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 8 -------- .../runtimes/assets/asset-hub-westend/tests/tests.rs | 9 ++++----- 6 files changed, 14 insertions(+), 31 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index f768f803aeaf..420fa67f41f3 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -82,7 +82,7 @@ use parachains_common::{ use sp_runtime::{Perbill, RuntimeDebug}; use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ - ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, + ForeignAssetsConvertedConcreteId, GovernanceLocation, LocationToAccountId, PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, }; @@ -424,7 +424,7 @@ impl pallet_assets::Config for Runtime { FromNetwork, xcm_config::bridging::to_westend::WestendOrEthereumAssetFromAssetHubWestend, ), - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, AccountId, xcm::v4::Location, >; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 32fbfb6d0199..637c5900f7da 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -512,14 +512,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type ForeignCreatorsSovereignAccountOf = ( - SiblingParachainConvertsVia, - AccountId32Aliases, - ParentIsPreset, - GlobalConsensusEthereumConvertsFor, - GlobalConsensusParachainConvertsFor, -); - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 6e10f9168990..814323135325 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -21,8 +21,8 @@ use asset_hub_rococo_runtime::{ xcm_config, xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig, + ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot, + TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig, }, AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, @@ -941,7 +941,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( CheckingAccount, WeightToFee, ParachainSystem, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, collator_session_keys(), slot_durations(), @@ -1015,7 +1015,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p Runtime, XcmConfig, WeightToFee, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, Location, WithLatestLocationConverter, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index adfa2b74df65..066a1c2bdfdc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -79,9 +79,9 @@ use testnet_parachains_constants::westend::{ consensus::*, currency::*, fee::WeightToFee, snowbridge::EthereumNetwork, time::*, }; use xcm_config::{ - ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, - PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, + ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, + TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, WestendLocation, + XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -423,7 +423,7 @@ impl pallet_assets::Config for Runtime { FromNetwork, xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, ), - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, AccountId, xcm::v4::Location, >; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index cfd9fd2fd463..b554d00508be 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -535,14 +535,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type ForeignCreatorsSovereignAccountOf = ( - SiblingParachainConvertsVia, - AccountId32Aliases, - ParentIsPreset, - GlobalConsensusEthereumConvertsFor, - GlobalConsensusParachainConvertsFor, -); - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index ff84bdea69f4..6a48b19ce946 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -21,9 +21,8 @@ use asset_hub_westend_runtime::{ xcm_config, xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TrustBackedAssetsPalletLocation, WestendLocation, - XcmConfig, + ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot, + TrustBackedAssetsPalletLocation, WestendLocation, XcmConfig, }, AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, @@ -966,7 +965,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( CheckingAccount, WeightToFee, ParachainSystem, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, collator_session_keys(), slot_durations(), @@ -1044,7 +1043,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p Runtime, XcmConfig, WeightToFee, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, xcm::v4::Location, WithLatestLocationConverter, From 3d11c7ff9d85e78a189909fba92910cbe23bc7ed Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:21:29 +0300 Subject: [PATCH 45/58] Switch node side to v2 candidate receipts (#5679) on top of https://github.com/paritytech/polkadot-sdk/pull/5423 This PR implements the plumbing work required for https://github.com/paritytech/polkadot-sdk/issues/5047 . I also added additional helper methods gated by feature "test" in primitives. TODO: - [x] PRDoc --------- Signed-off-by: Andrei Sandu Co-authored-by: GitHub Action --- cumulus/client/consensus/common/src/tests.rs | 2 +- cumulus/client/network/src/lib.rs | 12 +- cumulus/client/network/src/tests.rs | 22 ++- cumulus/client/pov-recovery/src/lib.rs | 6 +- cumulus/client/pov-recovery/src/tests.rs | 5 +- .../src/lib.rs | 5 +- .../client/relay-chain-interface/src/lib.rs | 6 +- .../src/blockchain_rpc_client.rs | 28 ++- .../relay-chain-rpc-interface/src/lib.rs | 5 +- .../src/rpc_client.rs | 19 +- polkadot/node/collation-generation/Cargo.toml | 1 + polkadot/node/collation-generation/src/lib.rs | 10 +- .../node/collation-generation/src/tests.rs | 84 ++++----- polkadot/node/core/approval-voting/Cargo.toml | 1 + .../approval-voting/src/approval_checking.rs | 10 +- .../approval-voting/src/approval_db/v1/mod.rs | 4 +- .../approval-voting/src/approval_db/v2/mod.rs | 4 +- .../src/approval_db/v2/tests.rs | 14 +- .../approval-voting/src/approval_db/v3/mod.rs | 4 +- .../src/approval_db/v3/tests.rs | 17 +- .../node/core/approval-voting/src/import.rs | 41 +++-- polkadot/node/core/approval-voting/src/lib.rs | 25 +-- polkadot/node/core/approval-voting/src/ops.rs | 4 +- .../approval-voting/src/persisted_entries.rs | 4 +- .../node/core/approval-voting/src/tests.rs | 107 +++++------ polkadot/node/core/av-store/src/lib.rs | 4 +- polkadot/node/core/av-store/src/tests.rs | 4 +- polkadot/node/core/backing/Cargo.toml | 1 + polkadot/node/core/backing/src/error.rs | 2 +- polkadot/node/core/backing/src/lib.rs | 46 ++--- polkadot/node/core/backing/src/tests/mod.rs | 35 ++-- .../src/tests/prospective_parachains.rs | 22 +-- .../node/core/bitfield-signing/src/lib.rs | 2 +- .../node/core/bitfield-signing/src/tests.rs | 6 +- .../node/core/candidate-validation/src/lib.rs | 17 +- .../core/candidate-validation/src/tests.rs | 51 ++++-- .../node/core/dispute-coordinator/Cargo.toml | 1 + .../core/dispute-coordinator/src/db/v1.rs | 25 +-- .../core/dispute-coordinator/src/import.rs | 6 +- .../dispute-coordinator/src/initialized.rs | 15 +- .../node/core/dispute-coordinator/src/lib.rs | 2 +- .../src/participation/mod.rs | 9 +- .../src/participation/queues/mod.rs | 5 +- .../src/participation/queues/tests.rs | 4 +- .../src/participation/tests.rs | 3 +- .../dispute-coordinator/src/scraping/mod.rs | 5 +- .../dispute-coordinator/src/scraping/tests.rs | 8 +- .../core/dispute-coordinator/src/tests.rs | 30 +-- .../node/core/parachains-inherent/src/lib.rs | 2 +- .../core/prospective-parachains/Cargo.toml | 1 + .../src/fragment_chain/mod.rs | 20 +- .../src/fragment_chain/tests.rs | 20 +- .../core/prospective-parachains/src/lib.rs | 13 +- .../core/prospective-parachains/src/tests.rs | 25 +-- polkadot/node/core/provisioner/Cargo.toml | 2 + .../disputes/prioritized_selection/tests.rs | 14 +- polkadot/node/core/provisioner/src/lib.rs | 20 +- polkadot/node/core/provisioner/src/tests.rs | 58 +++--- polkadot/node/core/runtime-api/src/cache.rs | 22 ++- polkadot/node/core/runtime-api/src/tests.rs | 22 ++- polkadot/node/malus/src/variants/common.rs | 10 +- .../variants/dispute_finalized_candidates.rs | 2 +- .../src/variants/suggest_garbage_candidate.rs | 13 +- .../src/requester/fetch_task/mod.rs | 8 +- .../src/requester/mod.rs | 2 +- .../src/requester/tests.rs | 2 +- .../src/tests/mock.rs | 8 +- .../src/tests/mod.rs | 2 +- .../src/tests/state.rs | 2 +- .../network/availability-recovery/src/lib.rs | 8 +- .../availability-recovery/src/tests.rs | 34 ++-- .../src/collator_side/collation.rs | 4 +- .../src/collator_side/mod.rs | 13 +- .../src/validator_side/collation.rs | 13 +- .../src/validator_side/mod.rs | 26 +-- .../src/validator_side/tests/mod.rs | 44 +++-- .../tests/prospective_parachains.rs | 20 +- .../src/receiver/batches/batch.rs | 2 +- .../src/receiver/batches/mod.rs | 2 +- .../dispute-distribution/src/receiver/mod.rs | 2 +- .../src/sender/send_task.rs | 2 +- .../dispute-distribution/src/tests/mock.rs | 8 +- .../dispute-distribution/src/tests/mod.rs | 4 +- .../protocol/src/request_response/v1.rs | 7 +- .../protocol/src/request_response/v2.rs | 4 +- .../network/statement-distribution/Cargo.toml | 1 + .../src/legacy_v1/mod.rs | 9 +- .../src/legacy_v1/requester.rs | 4 +- .../src/legacy_v1/responder.rs | 4 +- .../src/legacy_v1/tests.rs | 41 +++-- .../src/v2/candidates.rs | 12 +- .../statement-distribution/src/v2/mod.rs | 16 +- .../statement-distribution/src/v2/requests.rs | 22 ++- .../src/v2/tests/mod.rs | 6 +- .../node/overseer/examples/minimal-example.rs | 6 +- polkadot/node/overseer/src/tests.rs | 19 +- .../node/primitives/src/disputes/message.rs | 3 +- polkadot/node/primitives/src/disputes/mod.rs | 6 +- polkadot/node/primitives/src/lib.rs | 8 +- .../node/service/src/parachains_db/upgrade.rs | 6 +- polkadot/node/subsystem-bench/Cargo.toml | 2 +- .../src/lib/approval/helpers.rs | 6 +- .../src/lib/approval/message_generator.rs | 4 +- .../subsystem-bench/src/lib/approval/mod.rs | 5 +- .../src/lib/availability/mod.rs | 2 +- .../src/lib/availability/test_state.rs | 14 +- .../src/lib/mock/runtime_api.rs | 7 +- .../src/lib/statement/test_state.rs | 21 ++- polkadot/node/subsystem-types/src/messages.rs | 37 ++-- .../subsystem-types/src/runtime_client.rs | 56 ++---- .../src/inclusion_emulator/mod.rs | 4 +- polkadot/node/subsystem-util/src/lib.rs | 15 +- .../node/subsystem-util/src/runtime/mod.rs | 12 +- .../test-parachains/adder/collator/src/lib.rs | 2 +- .../undying/collator/src/lib.rs | 2 +- polkadot/primitives/src/v8/mod.rs | 2 +- polkadot/primitives/src/vstaging/mod.rs | 173 ++++++++++++++++-- polkadot/primitives/test-helpers/src/lib.rs | 36 +++- .../runtime/parachains/src/inclusion/tests.rs | 2 +- .../parachains/src/paras_inherent/tests.rs | 4 +- polkadot/statement-table/src/lib.rs | 4 +- prdoc/pr_5679.prdoc | 80 ++++++++ 122 files changed, 1136 insertions(+), 721 deletions(-) create mode 100644 prdoc/pr_5679.prdoc diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 94e2304011be..79e620db3bfa 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -20,7 +20,7 @@ use async_trait::async_trait; use codec::Encode; use cumulus_client_pov_recovery::RecoveryKind; use cumulus_primitives_core::{ - relay_chain::{BlockId, BlockNumber, CoreState}, + relay_chain::{vstaging::CoreState, BlockId, BlockNumber}, CumulusDigestItem, InboundDownwardMessage, InboundHrmpMessage, }; use cumulus_relay_chain_interface::{ diff --git a/cumulus/client/network/src/lib.rs b/cumulus/client/network/src/lib.rs index 01ad15bed4da..3b9c0fc81ece 100644 --- a/cumulus/client/network/src/lib.rs +++ b/cumulus/client/network/src/lib.rs @@ -32,8 +32,8 @@ use polkadot_node_primitives::{CollationSecondedSignal, Statement}; use polkadot_node_subsystem::messages::RuntimeApiRequest; use polkadot_parachain_primitives::primitives::HeadData; use polkadot_primitives::{ - CandidateReceipt, CompactStatement, Hash as PHash, Id as ParaId, OccupiedCoreAssumption, - SigningContext, UncheckedSigned, + vstaging::CandidateReceiptV2 as CandidateReceipt, CompactStatement, Hash as PHash, + Id as ParaId, OccupiedCoreAssumption, SigningContext, UncheckedSigned, }; use codec::{Decode, DecodeAll, Encode}; @@ -79,7 +79,7 @@ impl Decode for BlockAnnounceData { let relay_parent = match PHash::decode(input) { Ok(p) => p, // For being backwards compatible, we support missing relay-chain parent. - Err(_) => receipt.descriptor.relay_parent, + Err(_) => receipt.descriptor.relay_parent(), }; Ok(Self { receipt, statement, relay_parent }) @@ -108,7 +108,7 @@ impl BlockAnnounceData { return Err(Validation::Failure { disconnect: true }) } - if HeadData(encoded_header).hash() != self.receipt.descriptor.para_head { + if HeadData(encoded_header).hash() != self.receipt.descriptor.para_head() { tracing::debug!( target: LOG_TARGET, "Receipt para head hash doesn't match the hash of the header in the block announcement", @@ -302,7 +302,7 @@ where } .map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?; - Ok(candidate_receipts.into_iter().map(|cr| cr.descriptor.para_head)) + Ok(candidate_receipts.into_iter().map(|cr| cr.descriptor.para_head())) } /// Handle a block announcement with empty data (no statement) attached to it. @@ -399,7 +399,7 @@ where return Ok(e) } - let relay_parent = block_announce_data.receipt.descriptor.relay_parent; + let relay_parent = block_announce_data.receipt.descriptor.relay_parent(); relay_chain_interface .wait_for_block(relay_parent) diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index 4b3473645210..009f922008b1 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -26,10 +26,11 @@ use futures::{executor::block_on, poll, task::Poll, FutureExt, Stream, StreamExt use parking_lot::Mutex; use polkadot_node_primitives::{SignedFullStatement, Statement}; use polkadot_primitives::{ + vstaging::{CommittedCandidateReceiptV2, CoreState}, BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorPair, - CommittedCandidateReceipt, CoreState, Hash as PHash, HeadData, InboundDownwardMessage, - InboundHrmpMessage, OccupiedCoreAssumption, PersistedValidationData, SessionIndex, - SigningContext, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, Hash as PHash, HeadData, InboundDownwardMessage, InboundHrmpMessage, + OccupiedCoreAssumption, PersistedValidationData, SessionIndex, SigningContext, + ValidationCodeHash, ValidatorId, }; use polkadot_test_client::{ Client as PClient, ClientBlockImportExt, DefaultTestClientBuilderExt, FullBackend as PBackend, @@ -166,7 +167,7 @@ impl RelayChainInterface for DummyRelayChainInterface { &self, _: PHash, _: ParaId, - ) -> RelayChainResult> { + ) -> RelayChainResult> { if self.data.lock().runtime_version >= RuntimeApiRequest::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT { @@ -174,7 +175,7 @@ impl RelayChainInterface for DummyRelayChainInterface { } if self.data.lock().has_pending_availability { - Ok(Some(dummy_candidate())) + Ok(Some(dummy_candidate().into())) } else { Ok(None) } @@ -184,7 +185,7 @@ impl RelayChainInterface for DummyRelayChainInterface { &self, _: PHash, _: ParaId, - ) -> RelayChainResult> { + ) -> RelayChainResult> { if self.data.lock().runtime_version < RuntimeApiRequest::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT { @@ -192,7 +193,7 @@ impl RelayChainInterface for DummyRelayChainInterface { } if self.data.lock().has_pending_availability { - Ok(vec![dummy_candidate()]) + Ok(vec![dummy_candidate().into()]) } else { Ok(vec![]) } @@ -412,7 +413,7 @@ async fn make_gossip_message_and_header( validation_code_hash: ValidationCodeHash::from(PHash::random()), }, }; - let statement = Statement::Seconded(candidate_receipt); + let statement = Statement::Seconded(candidate_receipt.into()); let signed = SignedFullStatement::sign( &keystore, statement, @@ -525,7 +526,7 @@ fn legacy_block_announce_data_handling() { let block_data = BlockAnnounceData::decode(&mut &data[..]).expect("Decoding works from legacy works"); - assert_eq!(receipt.descriptor.relay_parent, block_data.relay_parent); + assert_eq!(receipt.descriptor.relay_parent(), block_data.relay_parent); let data = block_data.encode(); LegacyBlockAnnounceData::decode(&mut &data[..]).expect("Decoding works"); @@ -600,7 +601,8 @@ async fn check_statement_seconded() { erasure_root: PHash::random(), signature: sp_core::sr25519::Signature::default().into(), validation_code_hash: ValidationCodeHash::from(PHash::random()), - }, + } + .into(), }, statement: signed_statement.convert_payload().into(), relay_parent, diff --git a/cumulus/client/pov-recovery/src/lib.rs b/cumulus/client/pov-recovery/src/lib.rs index 043cba12d193..87349aef0c93 100644 --- a/cumulus/client/pov-recovery/src/lib.rs +++ b/cumulus/client/pov-recovery/src/lib.rs @@ -56,7 +56,11 @@ use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT}; use polkadot_node_subsystem::messages::{AvailabilityRecoveryMessage, RuntimeApiRequest}; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{ - CandidateReceipt, CommittedCandidateReceipt, Id as ParaId, SessionIndex, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + Id as ParaId, SessionIndex, }; use cumulus_primitives_core::ParachainBlockData; diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index 94dec32485cc..d528a92a52a8 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -18,7 +18,7 @@ use super::*; use assert_matches::assert_matches; use codec::{Decode, Encode}; use cumulus_primitives_core::relay_chain::{ - BlockId, CandidateCommitments, CandidateDescriptor, CoreIndex, CoreState, + vstaging::CoreState, BlockId, CandidateCommitments, CandidateDescriptor, CoreIndex, }; use cumulus_relay_chain_interface::{ InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, PHash, PHeader, @@ -532,7 +532,8 @@ fn make_candidate_chain(candidate_number_range: Range) -> Vec Result>, sp_api::ApiError> { + ) -> Result< + Vec>, + sp_api::ApiError, + > { Ok(self.rpc_client.parachain_host_availability_cores(at).await?) } @@ -212,8 +215,11 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidate_pending_availability( &self, at: Hash, - para_id: ParaId, - ) -> Result>, sp_api::ApiError> { + para_id: cumulus_primitives_core::ParaId, + ) -> Result< + Option>, + sp_api::ApiError, + > { Ok(self .rpc_client .parachain_host_candidate_pending_availability(at, para_id) @@ -223,7 +229,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidate_events( &self, at: Hash, - ) -> Result>, sp_api::ApiError> { + ) -> Result>, sp_api::ApiError> { Ok(self.rpc_client.parachain_host_candidate_events(at).await?) } @@ -266,7 +272,8 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn on_chain_votes( &self, at: Hash, - ) -> Result>, sp_api::ApiError> { + ) -> Result>, sp_api::ApiError> + { Ok(self.rpc_client.parachain_host_on_chain_votes(at).await?) } @@ -437,8 +444,11 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidates_pending_availability( &self, at: Hash, - para_id: ParaId, - ) -> Result>, sp_api::ApiError> { + para_id: cumulus_primitives_core::ParaId, + ) -> Result< + Vec>, + sp_api::ApiError, + > { Ok(self .rpc_client .parachain_host_candidates_pending_availability(at, para_id) diff --git a/cumulus/client/relay-chain-rpc-interface/src/lib.rs b/cumulus/client/relay-chain-rpc-interface/src/lib.rs index f53cdeffea94..0e2f6c054c40 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/lib.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/lib.rs @@ -18,8 +18,9 @@ use async_trait::async_trait; use core::time::Duration; use cumulus_primitives_core::{ relay_chain::{ - CommittedCandidateReceipt, Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, Hash as RelayHash, + Header as RelayHeader, InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, + ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index d8e5abaddc6b..d7785d92c73a 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -32,13 +32,18 @@ use codec::{Decode, Encode}; use cumulus_primitives_core::{ relay_chain::{ - async_backing::{AsyncBackingParams, BackingState}, - slashing, ApprovalVotingParams, BlockNumber, CandidateCommitments, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, - ExecutorParams, GroupRotationInfo, Hash as RelayHash, Header as RelayHeader, - InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + async_backing::AsyncBackingParams, + slashing, + vstaging::{ + async_backing::BackingState, CandidateEvent, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash as RelayHash, Header as RelayHeader, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PvfCheckStatement, SessionIndex, + SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 4b0a5f7248ab..855b6b0e86eb 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -28,3 +28,4 @@ polkadot-primitives-test-helpers = { workspace = true } assert_matches = { workspace = true } rstest = { workspace = true } sp-keyring = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index 50adbddea413..f04f69cbd380 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -48,9 +48,10 @@ use polkadot_node_subsystem_util::{ request_validators, runtime::fetch_claim_queue, }; use polkadot_primitives::{ - collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, - CollatorPair, CoreIndex, CoreState, Hash, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, ScheduledCore, ValidationCodeHash, + collator_signature_payload, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, + CandidateCommitments, CandidateDescriptor, CollatorPair, CoreIndex, Hash, Id as ParaId, + OccupiedCoreAssumption, PersistedValidationData, ScheduledCore, ValidationCodeHash, }; use sp_core::crypto::Pair; use std::sync::Arc; @@ -607,7 +608,8 @@ async fn construct_and_distribute_receipt( erasure_root, para_head: commitments.head_data.hash(), validation_code_hash, - }, + } + .into(), }; gum::debug!( diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 7f76988bb035..78b35fde0ea2 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -30,12 +30,12 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - async_backing::{BackingState, CandidatePendingAvailability}, + vstaging::async_backing::{BackingState, CandidatePendingAvailability}, AsyncBackingParams, BlockNumber, CollatorPair, HeadData, PersistedValidationData, ScheduledCore, ValidationCode, }; use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator, make_candidate, + dummy_candidate_descriptor_v2, dummy_hash, dummy_head_data, dummy_validator, make_candidate, }; use rstest::rstest; use sp_keyring::sr25519::Keyring as Sr25519Keyring; @@ -504,22 +504,22 @@ fn sends_distribute_collation_message(#[case] runtime_version: u32) { // descriptor has a valid signature, then just copy in the generated signature // and check the rest of the fields for equality. assert!(CollatorPair::verify( - &descriptor.signature, + &descriptor.signature().unwrap(), &collator_signature_payload( - &descriptor.relay_parent, - &descriptor.para_id, - &descriptor.persisted_validation_data_hash, - &descriptor.pov_hash, - &descriptor.validation_code_hash, + &descriptor.relay_parent(), + &descriptor.para_id(), + &descriptor.persisted_validation_data_hash(), + &descriptor.pov_hash(), + &descriptor.validation_code_hash(), ) .as_ref(), - &descriptor.collator, + &descriptor.collator().unwrap(), )); let expect_descriptor = { let mut expect_descriptor = expect_descriptor; - expect_descriptor.signature = descriptor.signature.clone(); - expect_descriptor.erasure_root = descriptor.erasure_root; - expect_descriptor + expect_descriptor.signature = descriptor.signature().clone().unwrap(); + expect_descriptor.erasure_root = descriptor.erasure_root(); + expect_descriptor.into() }; assert_eq!(descriptor, expect_descriptor); }, @@ -658,7 +658,7 @@ fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) { .. }) => { let CandidateReceipt { descriptor, .. } = candidate_receipt; - assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash); + assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash()); }, _ => panic!("received wrong message type"), } @@ -752,9 +752,9 @@ fn submit_collation_leads_to_distribution() { }) => { let CandidateReceipt { descriptor, .. } = candidate_receipt; assert_eq!(parent_head_data_hash, parent_head.hash()); - assert_eq!(descriptor.persisted_validation_data_hash, expected_pvd.hash()); - assert_eq!(descriptor.para_head, dummy_head_data().hash()); - assert_eq!(descriptor.validation_code_hash, validation_code_hash); + assert_eq!(descriptor.persisted_validation_data_hash(), expected_pvd.hash()); + assert_eq!(descriptor.para_head(), dummy_head_data().hash()); + assert_eq!(descriptor.validation_code_hash(), validation_code_hash); } ); @@ -772,16 +772,17 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run let para_id = ParaId::from(5); // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. - let cores: Vec = vec![CoreState::Occupied(polkadot_primitives::OccupiedCore { - next_up_on_available: Some(ScheduledCore { para_id, collator: None }), - occupied_since: 1, - time_out_at: 10, - next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), - availability: Default::default(), // doesn't matter - group_responsible: polkadot_primitives::GroupIndex(0), - candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), - })]; + let cores: Vec = + vec![CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), + occupied_since: 1, + time_out_at: 10, + next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), + availability: Default::default(), // doesn't matter + group_responsible: polkadot_primitives::GroupIndex(0), + candidate_hash: Default::default(), + candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), + })]; let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]).into(); test_harness(|mut virtual_overseer| async move { @@ -882,7 +883,7 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti let cores = (0..3) .into_iter() .map(|idx| { - CoreState::Occupied(polkadot_primitives::OccupiedCore { + CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { next_up_on_available: Some(ScheduledCore { para_id, collator: None }), occupied_since: 0, time_out_at: 10, @@ -890,7 +891,7 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti availability: Default::default(), // doesn't matter group_responsible: polkadot_primitives::GroupIndex(idx as u32), candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), }) }) .collect::>(); @@ -1008,16 +1009,17 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( let para_id = ParaId::from(5); // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. - let cores: Vec = vec![CoreState::Occupied(polkadot_primitives::OccupiedCore { - next_up_on_available: Some(ScheduledCore { para_id, collator: None }), - occupied_since: 1, - time_out_at: 10, - next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), - availability: Default::default(), // doesn't matter - group_responsible: polkadot_primitives::GroupIndex(0), - candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), - })]; + let cores: Vec = + vec![CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), + occupied_since: 1, + time_out_at: 10, + next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), + availability: Default::default(), // doesn't matter + group_responsible: polkadot_primitives::GroupIndex(0), + candidate_hash: Default::default(), + candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), + })]; let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]).into(); test_harness(|mut virtual_overseer| async move { @@ -1248,9 +1250,9 @@ mod helpers { .. }) => { assert_eq!(parent_head_data_hash, parent_head.hash()); - assert_eq!(candidate_receipt.descriptor().persisted_validation_data_hash, pvd.hash()); - assert_eq!(candidate_receipt.descriptor().para_head, dummy_head_data().hash()); - assert_eq!(candidate_receipt.descriptor().validation_code_hash, validation_code_hash); + assert_eq!(candidate_receipt.descriptor().persisted_validation_data_hash(), pvd.hash()); + assert_eq!(candidate_receipt.descriptor().para_head(), dummy_head_data().hash()); + assert_eq!(candidate_receipt.descriptor().validation_code_hash(), validation_code_hash); } ); } diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index 2c3db866566c..f9754d2babc9 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -53,6 +53,7 @@ kvdb-memorydb = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } log = { workspace = true, default-features = true } sp-tracing = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-subsystem-bench = { workspace = true } diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index 3774edc69981..3b7262a46826 100644 --- a/polkadot/node/core/approval-voting/src/approval_checking.rs +++ b/polkadot/node/core/approval-voting/src/approval_checking.rs @@ -509,13 +509,13 @@ mod tests { use crate::{approval_db, BTreeMap}; use bitvec::{bitvec, order::Lsb0 as BitOrderLsb0, vec::BitVec}; use polkadot_primitives::GroupIndex; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; #[test] fn pending_is_not_approved() { let candidate = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: BitVec::default(), @@ -550,7 +550,7 @@ mod tests { fn exact_takes_only_assignments_up_to() { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 10], @@ -624,7 +624,7 @@ mod tests { fn one_honest_node_always_approves() { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 10], @@ -1097,7 +1097,7 @@ mod tests { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 3], diff --git a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs index 53e9db64f636..87a1d20b92f5 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs @@ -25,8 +25,8 @@ use codec::{Decode, Encode}; use polkadot_node_primitives::approval::v1::{AssignmentCert, DelayTranche}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, - ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CoreIndex, + GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; use std::collections::BTreeMap; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs index cd9256a5d47e..63c6cbf40b89 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -21,8 +21,8 @@ use polkadot_node_primitives::approval::{v1::DelayTranche, v2::AssignmentCertV2} use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs index 06a3cc1e306b..866702f861c1 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs @@ -26,7 +26,8 @@ use crate::{ ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateHash, CoreIndex, GroupIndex, Hash, }; use polkadot_node_subsystem_util::database::Database; @@ -35,7 +36,8 @@ use sp_consensus_slots::Slot; use std::{collections::HashMap, sync::Arc}; use polkadot_primitives_test_helpers::{ - dummy_candidate_receipt, dummy_candidate_receipt_bad_sig, dummy_hash, + dummy_candidate_receipt_bad_sig, dummy_candidate_receipt_v2, + dummy_candidate_receipt_v2_bad_sig, dummy_hash, }; const DATA_COL: u32 = 0; @@ -72,10 +74,10 @@ fn make_block_entry( } fn make_candidate(para_id: ParaId, relay_parent: Hash) -> CandidateReceipt { - let mut c = dummy_candidate_receipt(dummy_hash()); + let mut c = dummy_candidate_receipt_v2(dummy_hash()); - c.descriptor.para_id = para_id; - c.descriptor.relay_parent = relay_parent; + c.descriptor.set_para_id(para_id); + c.descriptor.set_relay_parent(relay_parent); c } @@ -95,7 +97,7 @@ fn read_write() { make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]); let candidate_entry = CandidateEntry { - candidate: dummy_candidate_receipt_bad_sig(dummy_hash(), None), + candidate: dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None), session: 5, block_assignments: vec![( hash_a, diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs index 7118fb6770fd..bc34f88af80a 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs @@ -25,8 +25,8 @@ use polkadot_node_subsystem::SubsystemResult; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_overseer::SubsystemError; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs index d2a1d7d400b1..372dd49803cb 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs @@ -25,7 +25,8 @@ use crate::{ ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateHash, CoreIndex, GroupIndex, Hash, }; use polkadot_node_subsystem_util::database::Database; @@ -34,7 +35,7 @@ use sp_consensus_slots::Slot; use std::{collections::HashMap, sync::Arc}; use polkadot_primitives_test_helpers::{ - dummy_candidate_receipt, dummy_candidate_receipt_bad_sig, dummy_hash, + dummy_candidate_receipt_v2, dummy_candidate_receipt_v2_bad_sig, dummy_hash, }; const DATA_COL: u32 = 0; @@ -72,12 +73,12 @@ fn make_block_entry( } fn make_candidate(para_id: ParaId, relay_parent: Hash) -> CandidateReceipt { - let mut c = dummy_candidate_receipt(dummy_hash()); + let mut c = dummy_candidate_receipt_v2(dummy_hash()); - c.descriptor.para_id = para_id; - c.descriptor.relay_parent = relay_parent; + c.descriptor.set_para_id(para_id); + c.descriptor.set_relay_parent(relay_parent); - c + c.into() } #[test] @@ -86,7 +87,7 @@ fn read_write() { let hash_a = Hash::repeat_byte(1); let hash_b = Hash::repeat_byte(2); - let candidate_hash = dummy_candidate_receipt_bad_sig(dummy_hash(), None).hash(); + let candidate_hash = dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None).hash(); let range = StoredBlockRange(10, 20); let at_height = vec![hash_a, hash_b]; @@ -95,7 +96,7 @@ fn read_write() { make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]); let candidate_entry = CandidateEntry { - candidate: dummy_candidate_receipt_bad_sig(dummy_hash(), None), + candidate: dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None), session: 5, block_assignments: vec![( hash_a, diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index e50a2f911489..be7b3103ab13 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -45,8 +45,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{determine_new_blocks, runtime::RuntimeInfo}; use polkadot_overseer::SubsystemSender; use polkadot_primitives::{ - node_features, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, - CoreIndex, GroupIndex, Hash, Header, SessionIndex, + node_features, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateHash, ConsensusLog, CoreIndex, GroupIndex, Hash, Header, SessionIndex, }; use sc_keystore::LocalKeystore; use sp_consensus_slots::Slot; @@ -618,10 +619,10 @@ pub(crate) mod tests { use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::{ - node_features::FeatureIndex, ExecutorParams, Id as ParaId, IndexedVec, NodeFeatures, - SessionInfo, ValidatorId, ValidatorIndex, + node_features::FeatureIndex, vstaging::MutateDescriptorV2, ExecutorParams, Id as ParaId, + IndexedVec, NodeFeatures, SessionInfo, ValidatorId, ValidatorIndex, }; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; use schnellru::{ByLength, LruMap}; pub(crate) use sp_consensus_babe::{ digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest}, @@ -764,9 +765,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -917,9 +918,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1056,9 +1057,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1150,9 +1151,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1340,9 +1341,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 0cb977c58021..2176cc7675be 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -52,9 +52,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - ApprovalVoteMultipleCandidates, ApprovalVotingParams, BlockNumber, CandidateHash, - CandidateIndex, CandidateReceipt, CoreIndex, ExecutorParams, GroupIndex, Hash, SessionIndex, - SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, ApprovalVoteMultipleCandidates, + ApprovalVotingParams, BlockNumber, CandidateHash, CandidateIndex, CoreIndex, ExecutorParams, + GroupIndex, Hash, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, + ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -2824,7 +2825,7 @@ where target: LOG_TARGET, validator_index = approval.validator.0, candidate_hash = ?approved_candidate_hash, - para_id = ?candidate_entry.candidate_receipt().descriptor.para_id, + para_id = ?candidate_entry.candidate_receipt().descriptor.para_id(), "Importing approval vote", ); @@ -2923,7 +2924,7 @@ where let block_hash = block_entry.block_hash(); let block_number = block_entry.block_number(); let session_index = block_entry.session(); - let para_id = candidate_entry.candidate_receipt().descriptor().para_id; + let para_id = candidate_entry.candidate_receipt().descriptor().para_id(); let tick_now = state.clock.tick_now(); let (is_approved, status) = if let Some((approval_entry, status)) = state @@ -3221,7 +3222,7 @@ async fn process_wakeup>( gum::trace!( target: LOG_TARGET, ?candidate_hash, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), block_hash = ?relay_block, "Launching approval work.", ); @@ -3352,7 +3353,7 @@ async fn launch_approval< } let candidate_hash = candidate.hash(); - let para_id = candidate.descriptor.para_id; + let para_id = candidate.descriptor.para_id(); gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Recovering data."); let timer = metrics.time_recover_and_approve(); @@ -3370,7 +3371,7 @@ async fn launch_approval< .send_message(RuntimeApiMessage::Request( block_hash, RuntimeApiRequest::ValidationCodeByHash( - candidate.descriptor.validation_code_hash, + candidate.descriptor.validation_code_hash(), code_tx, ), )) @@ -3393,7 +3394,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Data unavailable for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); // do nothing. we'll just be a no-show and that'll cause others to rise up. metrics_guard.take().on_approval_unavailable(); @@ -3404,7 +3405,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Channel closed while recovering data for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); // do nothing. we'll just be a no-show and that'll cause others to rise up. metrics_guard.take().on_approval_unavailable(); @@ -3415,7 +3416,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Data recovery invalid for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); issue_local_invalid_statement( &mut sender, @@ -3438,7 +3439,7 @@ async fn launch_approval< gum::warn!( target: LOG_TARGET, "Validation code unavailable for block {:?} in the state of block {:?} (a recent descendant)", - candidate.descriptor.relay_parent, + candidate.descriptor.relay_parent(), block_hash, ); diff --git a/polkadot/node/core/approval-voting/src/ops.rs b/polkadot/node/core/approval-voting/src/ops.rs index 2a8fdba5aa36..f105580009fa 100644 --- a/polkadot/node/core/approval-voting/src/ops.rs +++ b/polkadot/node/core/approval-voting/src/ops.rs @@ -20,7 +20,9 @@ use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use bitvec::order::Lsb0 as BitOrderLsb0; -use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, GroupIndex, Hash}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, GroupIndex, Hash, +}; use std::collections::{hash_map::Entry, BTreeMap, HashMap}; diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index 16e231aa1a2d..d891af01c3ab 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -26,8 +26,8 @@ use polkadot_node_primitives::approval::{ v2::{AssignmentCertV2, CandidateBitfield}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index db5ffd441c0d..099ab419dfbf 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -42,8 +42,9 @@ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_overseer::SpawnGlue; use polkadot_primitives::{ - ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, - Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, + vstaging::{CandidateEvent, MutateDescriptorV2}, + ApprovalVote, CandidateCommitments, CoreIndex, DisputeStatement, GroupIndex, Header, + Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, ValidatorSignature, }; use std::{cmp::max, time::Duration}; @@ -69,7 +70,9 @@ use super::{ }, }; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_candidate_receipt_bad_sig}; +use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt_v2, dummy_candidate_receipt_v2_bad_sig, +}; const SLOT_DURATION_MILLIS: u64 = 5000; @@ -638,8 +641,8 @@ where } fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { - let mut r = dummy_candidate_receipt_bad_sig(*hash, Some(Default::default())); - r.descriptor.para_id = para_id; + let mut r = dummy_candidate_receipt_v2_bad_sig(*hash, Some(Default::default())); + r.descriptor.set_para_id(para_id); r } @@ -1282,7 +1285,7 @@ fn subsystem_rejects_approval_if_no_block_entry() { let block_hash = Hash::repeat_byte(0x01); let candidate_index = 0; let validator = ValidatorIndex(0); - let candidate_hash = dummy_candidate_receipt(block_hash).hash(); + let candidate_hash = dummy_candidate_receipt_v2(block_hash).hash(); let session_index = 1; let rx = import_approval( @@ -1324,9 +1327,9 @@ fn subsystem_rejects_approval_before_assignment() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -1390,15 +1393,17 @@ fn subsystem_accepts_duplicate_assignment() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt - }; + } + .into(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt - }; + } + .into(); let candidate_index1 = 0; let candidate_index2 = 1; @@ -1572,9 +1577,9 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -1643,9 +1648,9 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -2402,13 +2407,13 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_hash1 = candidate_receipt1.hash(); @@ -2566,18 +2571,18 @@ fn inclusion_events_can_be_unordered_by_core_index() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt0 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(0_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(0_u32)); receipt }; let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_index0 = 0; @@ -2710,8 +2715,8 @@ fn approved_ancestor_test( .iter() .enumerate() .map(|(i, hash)| { - let mut candidate_receipt = dummy_candidate_receipt(*hash); - candidate_receipt.descriptor.para_id = i.into(); + let mut candidate_receipt = dummy_candidate_receipt_v2(*hash); + candidate_receipt.descriptor.set_para_id(i.into()); candidate_receipt }) .collect(); @@ -2882,7 +2887,7 @@ fn subsystem_validate_approvals_cache() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); @@ -3012,13 +3017,13 @@ fn subsystem_doesnt_distribute_duplicate_compact_assignments() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_index1 = 0; @@ -3263,7 +3268,7 @@ where ); let block_hash = Hash::repeat_byte(0x01); - let candidate_receipt = dummy_candidate_receipt(block_hash); + let candidate_receipt = dummy_candidate_receipt_v2(block_hash); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); let candidate_index = 0; @@ -3965,8 +3970,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { let candidate_commitments = CandidateCommitments::default(); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -3974,8 +3979,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { let candidate_hash1 = candidate_receipt1.hash(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4266,8 +4271,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let candidate_commitments = CandidateCommitments::default(); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4275,8 +4280,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let candidate_hash1 = candidate_receipt1.hash(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4420,7 +4425,7 @@ async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); @@ -4549,7 +4554,7 @@ fn subsystem_relaunches_approval_work_on_restart() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let slot = Slot::from(1); clock.inner.lock().set_tick(slot_to_tick(slot + 2)); @@ -4805,7 +4810,7 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let slot = Slot::from(1); @@ -4815,7 +4820,7 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { fork_block_hash, slot, sync_oracle_handle, - candidate_receipt, + candidate_receipt.into(), ) .await; chain_builder.build(&mut virtual_overseer).await; diff --git a/polkadot/node/core/av-store/src/lib.rs b/polkadot/node/core/av-store/src/lib.rs index 9473040e8f5e..9da2973773a0 100644 --- a/polkadot/node/core/av-store/src/lib.rs +++ b/polkadot/node/core/av-store/src/lib.rs @@ -47,8 +47,8 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util as util; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, Hash, - Header, NodeFeatures, ValidatorIndex, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateHash, ChunkIndex, CoreIndex, Hash, Header, NodeFeatures, ValidatorIndex, }; use util::availability_chunks::availability_chunk_indices; diff --git a/polkadot/node/core/av-store/src/tests.rs b/polkadot/node/core/av-store/src/tests.rs index 958917a3104f..80043e56976b 100644 --- a/polkadot/node/core/av-store/src/tests.rs +++ b/polkadot/node/core/av-store/src/tests.rs @@ -31,8 +31,8 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::{database::Database, TimeoutExt}; use polkadot_primitives::{ - node_features, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, HeadData, Header, - PersistedValidationData, ValidatorId, + node_features, vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CoreIndex, + GroupIndex, HeadData, Header, PersistedValidationData, ValidatorId, }; use polkadot_primitives_test_helpers::TestCandidateBuilder; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/node/core/backing/Cargo.toml b/polkadot/node/core/backing/Cargo.toml index bd56a3ad693b..cd1acf9daa93 100644 --- a/polkadot/node/core/backing/Cargo.toml +++ b/polkadot/node/core/backing/Cargo.toml @@ -36,3 +36,4 @@ assert_matches = { workspace = true } rstest = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } diff --git a/polkadot/node/core/backing/src/error.rs b/polkadot/node/core/backing/src/error.rs index 568f71402644..e09d8425f78a 100644 --- a/polkadot/node/core/backing/src/error.rs +++ b/polkadot/node/core/backing/src/error.rs @@ -24,7 +24,7 @@ use polkadot_node_subsystem::{ RuntimeApiError, SubsystemError, }; use polkadot_node_subsystem_util::{runtime, Error as UtilError}; -use polkadot_primitives::{BackedCandidate, ValidationCodeHash}; +use polkadot_primitives::{vstaging::BackedCandidate, ValidationCodeHash}; use crate::{ParaId, LOG_TARGET}; diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 4463fb34b510..b5362d32ad88 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -108,10 +108,14 @@ use polkadot_node_subsystem_util::{ }; use polkadot_parachain_primitives::primitives::IsSystem; use polkadot_primitives::{ - node_features::FeatureIndex, BackedCandidate, CandidateCommitments, CandidateHash, - CandidateReceipt, CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, PersistedValidationData, - SessionIndex, SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + CandidateCommitments, CandidateHash, CoreIndex, ExecutorParams, GroupIndex, GroupRotationInfo, + Hash, Id as ParaId, IndexedVec, NodeFeatures, PersistedValidationData, SessionIndex, + SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, }; use polkadot_statement_table::{ @@ -627,7 +631,7 @@ async fn request_candidate_validation( executor_params: ExecutorParams, ) -> Result { let (tx, rx) = oneshot::channel(); - let is_system = candidate_receipt.descriptor.para_id.is_system(); + let is_system = candidate_receipt.descriptor.para_id().is_system(); sender .send_message(CandidateValidationMessage::ValidateFromExhaustive { @@ -692,7 +696,7 @@ async fn validate_and_make_available( } = params; let validation_code = { - let validation_code_hash = candidate.descriptor().validation_code_hash; + let validation_code_hash = candidate.descriptor().validation_code_hash(); let (tx, rx) = oneshot::channel(); sender .send_message(RuntimeApiMessage::Request( @@ -725,7 +729,7 @@ async fn validate_and_make_available( &mut sender, relay_parent, from_validator, - candidate.descriptor.para_id, + candidate.descriptor.para_id(), candidate_hash, pov_hash, ) @@ -772,7 +776,7 @@ async fn validate_and_make_available( pov.clone(), candidate.hash(), validation_data.clone(), - candidate.descriptor.erasure_root, + candidate.descriptor.erasure_root(), core_index, node_features, ) @@ -1054,7 +1058,7 @@ fn core_index_from_statement( } if let StatementWithPVD::Seconded(candidate, _pvd) = statement.payload() { - let candidate_para_id = candidate.descriptor.para_id; + let candidate_para_id = candidate.descriptor.para_id(); let mut assigned_paras = claim_queue.iter_claims_for_core(&core_index); if !assigned_paras.any(|id| id == &candidate_para_id) { @@ -1445,14 +1449,14 @@ async fn handle_validated_candidate_command( let candidate_hash = candidate.hash(); gum::debug!( target: LOG_TARGET, - relay_parent = ?candidate.descriptor().relay_parent, + relay_parent = ?candidate.descriptor().relay_parent(), ?candidate_hash, "Attempted to second candidate but was rejected by prospective parachains", ); // Ensure the collator is reported. ctx.send_message(CollatorProtocolMessage::Invalid( - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate, )) .await; @@ -1487,7 +1491,7 @@ async fn handle_validated_candidate_command( Some(d) => d, }; - leaf_data.add_seconded_candidate(candidate.descriptor().para_id); + leaf_data.add_seconded_candidate(candidate.descriptor().para_id()); } rp_state.issued_statements.insert(candidate_hash); @@ -1636,7 +1640,7 @@ async fn import_statement( let (tx, rx) = oneshot::channel(); ctx.send_message(ProspectiveParachainsMessage::IntroduceSecondedCandidate( IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate.clone(), persisted_validation_data: pvd.clone(), }, @@ -1665,7 +1669,7 @@ async fn import_statement( persisted_validation_data: pvd.clone(), // This is set after importing when seconding locally. seconded_locally: false, - relay_parent: candidate.descriptor().relay_parent, + relay_parent: candidate.descriptor.relay_parent(), }, ); } @@ -1709,7 +1713,7 @@ async fn post_import_statement_actions( &rp_state.table_context, rp_state.inject_core_index, ) { - let para_id = backed.candidate().descriptor.para_id; + let para_id = backed.candidate().descriptor.para_id(); gum::debug!( target: LOG_TARGET, candidate_hash = ?candidate_hash, @@ -1967,7 +1971,7 @@ async fn maybe_validate_and_import( .get_candidate(&candidate_hash) .ok_or(Error::CandidateNotFound)? .to_plain(), - pov_hash: receipt.descriptor.pov_hash, + pov_hash: receipt.descriptor.pov_hash(), from_validator: statement.validator_index(), backing: Vec::new(), }; @@ -2068,9 +2072,9 @@ async fn handle_second_message( let _timer = metrics.time_process_second(); let candidate_hash = candidate.hash(); - let relay_parent = candidate.descriptor().relay_parent; + let relay_parent = candidate.descriptor().relay_parent(); - if candidate.descriptor().persisted_validation_data_hash != persisted_validation_data.hash() { + if candidate.descriptor().persisted_validation_data_hash() != persisted_validation_data.hash() { gum::warn!( target: LOG_TARGET, ?candidate_hash, @@ -2104,12 +2108,12 @@ async fn handle_second_message( let assigned_paras = rp_state.assigned_core.and_then(|core| rp_state.claim_queue.0.get(&core)); // Sanity check that candidate is from our assignment. - if !matches!(assigned_paras, Some(paras) if paras.contains(&candidate.descriptor().para_id)) { + if !matches!(assigned_paras, Some(paras) if paras.contains(&candidate.descriptor().para_id())) { gum::debug!( target: LOG_TARGET, our_assignment_core = ?rp_state.assigned_core, our_assignment_paras = ?assigned_paras, - collation = ?candidate.descriptor().para_id, + collation = ?candidate.descriptor().para_id(), "Subsystem asked to second for para outside of our assignment", ); return Ok(()); @@ -2119,7 +2123,7 @@ async fn handle_second_message( target: LOG_TARGET, our_assignment_core = ?rp_state.assigned_core, our_assignment_paras = ?assigned_paras, - collation = ?candidate.descriptor().para_id, + collation = ?candidate.descriptor().para_id(), "Current assignments vs collation", ); diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index d9c1fc9499e5..dbb974a634fe 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -22,19 +22,19 @@ use polkadot_node_primitives::{BlockData, InvalidCandidate, SignedFullStatement, use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ - AllMessages, CollatorProtocolMessage, RuntimeApiMessage, RuntimeApiRequest, + AllMessages, CollatorProtocolMessage, PvfExecKind, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed, }, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, TimeoutExt, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - node_features, CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, - ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, + node_features, vstaging::MutateDescriptorV2, CandidateDescriptor, GroupRotationInfo, HeadData, + PersistedValidationData, ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, }; use polkadot_primitives_test_helpers::{ dummy_candidate_receipt_bad_sig, dummy_collator, dummy_collator_signature, - dummy_committed_candidate_receipt, dummy_hash, validator_pubkeys, + dummy_committed_candidate_receipt_v2, dummy_hash, validator_pubkeys, }; use polkadot_statement_table::v2::Misbehavior; use rstest::rstest; @@ -236,7 +236,8 @@ impl TestCandidateBuilder { para_head: self.head_data.hash(), validation_code_hash: ValidationCode(self.validation_code).hash(), persisted_validation_data_hash: self.persisted_validation_data_hash, - }, + } + .into(), commitments: CandidateCommitments { head_data: self.head_data, upward_messages: Default::default(), @@ -433,7 +434,7 @@ async fn assert_validate_from_exhaustive( }, ) if validation_data == *assert_pvd && validation_code == *assert_validation_code && - *pov == *assert_pov && &candidate_receipt.descriptor == assert_candidate.descriptor() && + *pov == *assert_pov && candidate_receipt.descriptor == assert_candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == assert_candidate.commitments.hash() => { @@ -650,7 +651,7 @@ fn backing_works(#[case] elastic_scaling_mvp: bool) { }, ) if validation_data == pvd_ab && validation_code == validation_code_ab && - *pov == pov_ab && &candidate_receipt.descriptor == candidate_a.descriptor() && + *pov == pov_ab && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == candidate_a_commitments_hash => { @@ -1121,7 +1122,7 @@ fn extract_core_index_from_statement_works() { .flatten() .expect("should be signed"); - candidate.descriptor.para_id = test_state.chain_ids[1]; + candidate.descriptor.set_para_id(test_state.chain_ids[1]); let signed_statement_3 = SignedFullStatementWithPVD::sign( &test_state.keystore, @@ -1286,7 +1287,7 @@ fn backing_works_while_validation_ongoing() { }, ) if validation_data == pvd_abc && validation_code == validation_code_abc && - *pov == pov_abc && &candidate_receipt.descriptor == candidate_a.descriptor() && + *pov == pov_abc && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { @@ -1453,7 +1454,7 @@ fn backing_misbehavior_works() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate_a.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { @@ -1620,7 +1621,7 @@ fn backing_dont_second_invalid() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_block_a && &candidate_receipt.descriptor == candidate_a.descriptor() && + *pov == pov_block_a && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1660,7 +1661,7 @@ fn backing_dont_second_invalid() { }, ) if validation_data == pvd_b && validation_code == validation_code_b && - *pov == pov_block_b && &candidate_receipt.descriptor == candidate_b.descriptor() && + *pov == pov_block_b && candidate_receipt.descriptor == candidate_b.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_b.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1787,7 +1788,7 @@ fn backing_second_after_first_fails_works() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1931,7 +1932,7 @@ fn backing_works_after_failed_validation() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1999,7 +2000,7 @@ fn candidate_backing_reorders_votes() { }; let attested = TableAttestedCandidate { - candidate: dummy_committed_candidate_receipt(dummy_hash()), + candidate: dummy_committed_candidate_receipt_v2(dummy_hash()), validity_votes: vec![ (ValidatorIndex(5), fake_attestation(5)), (ValidatorIndex(3), fake_attestation(3)), @@ -2210,7 +2211,7 @@ fn retry_works() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash ); @@ -2752,7 +2753,7 @@ fn validator_ignores_statements_from_disabled_validators() { } ) if validation_data == pvd && validation_code == expected_validation_code && - *pov == expected_pov && &candidate_receipt.descriptor == candidate.descriptor() && + *pov == expected_pov && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_commitments_hash == candidate_receipt.commitments_hash => { diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index 57b2fabd43b0..caddd2408057 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -20,7 +20,7 @@ use polkadot_node_subsystem::{ messages::{ChainApiMessage, HypotheticalMembership}, ActivatedLeaf, TimeoutExt, }; -use polkadot_primitives::{AsyncBackingParams, BlockNumber, Header, OccupiedCore}; +use polkadot_primitives::{vstaging::OccupiedCore, AsyncBackingParams, BlockNumber, Header}; use super::*; @@ -275,7 +275,7 @@ async fn assert_validate_seconded_candidate( }) if &validation_data == assert_pvd && &validation_code == assert_validation_code && &*pov == assert_pov && - &candidate_receipt.descriptor == candidate.descriptor() && + candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -890,7 +890,7 @@ fn prospective_parachains_reject_candidate() { AllMessages::CollatorProtocol(CollatorProtocolMessage::Invalid( relay_parent, candidate_receipt, - )) if candidate_receipt.descriptor() == candidate.descriptor() && + )) if candidate_receipt.descriptor == candidate.descriptor && candidate_receipt.commitments_hash == candidate.commitments.hash() && relay_parent == leaf_a_parent ); @@ -1011,7 +1011,7 @@ fn second_multiple_candidates_per_relay_parent() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate.descriptor().relay_parent, + candidate.descriptor.relay_parent(), &candidate, &pov, &pvd, @@ -1064,13 +1064,13 @@ fn second_multiple_candidates_per_relay_parent() { parent_hash, _signed_statement, ) - ) if parent_hash == candidate.descriptor().relay_parent => {} + ) if parent_hash == candidate.descriptor.relay_parent() => {} ); assert_matches!( virtual_overseer.recv().await, AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded(hash, statement)) => { - assert_eq!(candidate.descriptor().relay_parent, hash); + assert_eq!(candidate.descriptor.relay_parent(), hash); assert_matches!(statement.payload(), Statement::Seconded(_)); } ); @@ -1179,7 +1179,7 @@ fn backing_works() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate_a.descriptor().relay_parent, + candidate_a.descriptor.relay_parent(), &candidate_a, &pov, &pvd, @@ -1544,7 +1544,7 @@ fn seconding_sanity_check_occupy_same_depth() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate.descriptor().relay_parent, + candidate.descriptor.relay_parent(), &candidate, &pov, &pvd, @@ -1599,13 +1599,13 @@ fn seconding_sanity_check_occupy_same_depth() { parent_hash, _signed_statement, ) - ) if parent_hash == candidate.descriptor().relay_parent => {} + ) if parent_hash == candidate.descriptor.relay_parent() => {} ); assert_matches!( virtual_overseer.recv().await, AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded(hash, statement)) => { - assert_eq!(candidate.descriptor().relay_parent, hash); + assert_eq!(candidate.descriptor.relay_parent(), hash); assert_matches!(statement.payload(), Statement::Seconded(_)); } ); @@ -1637,7 +1637,7 @@ fn occupied_core_assignment() { time_out_at: 200_u32, next_up_on_time_out: None, availability: Default::default(), - candidate_descriptor, + candidate_descriptor: candidate_descriptor.into(), candidate_hash: Default::default(), }); diff --git a/polkadot/node/core/bitfield-signing/src/lib.rs b/polkadot/node/core/bitfield-signing/src/lib.rs index 474de1c66abd..7c67853503f6 100644 --- a/polkadot/node/core/bitfield-signing/src/lib.rs +++ b/polkadot/node/core/bitfield-signing/src/lib.rs @@ -34,7 +34,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ self as util, request_availability_cores, runtime::recv_runtime, Validator, }; -use polkadot_primitives::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex}; +use polkadot_primitives::{vstaging::CoreState, AvailabilityBitfield, Hash, ValidatorIndex}; use sp_keystore::{Error as KeystoreError, KeystorePtr}; use std::{collections::HashMap, time::Duration}; use wasm_timer::{Delay, Instant}; diff --git a/polkadot/node/core/bitfield-signing/src/tests.rs b/polkadot/node/core/bitfield-signing/src/tests.rs index c08018375cf3..9123414844a6 100644 --- a/polkadot/node/core/bitfield-signing/src/tests.rs +++ b/polkadot/node/core/bitfield-signing/src/tests.rs @@ -17,8 +17,8 @@ use super::*; use futures::{executor::block_on, pin_mut, StreamExt}; use polkadot_node_subsystem::messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}; -use polkadot_primitives::{CandidateHash, OccupiedCore}; -use polkadot_primitives_test_helpers::dummy_candidate_descriptor; +use polkadot_primitives::{vstaging::OccupiedCore, CandidateHash}; +use polkadot_primitives_test_helpers::dummy_candidate_descriptor_v2; fn occupied_core(para_id: u32, candidate_hash: CandidateHash) -> CoreState { CoreState::Occupied(OccupiedCore { @@ -29,7 +29,7 @@ fn occupied_core(para_id: u32, candidate_hash: CandidateHash) -> CoreState { next_up_on_time_out: None, availability: Default::default(), candidate_hash, - candidate_descriptor: dummy_candidate_descriptor(Hash::zero()), + candidate_descriptor: dummy_candidate_descriptor_v2(Hash::zero()), }) } diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index e875be9b5df9..a48669c24825 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -45,8 +45,11 @@ use polkadot_primitives::{ DEFAULT_APPROVAL_EXECUTION_TIMEOUT, DEFAULT_BACKING_EXECUTION_TIMEOUT, DEFAULT_LENIENT_PREPARATION_TIMEOUT, DEFAULT_PRECHECK_PREPARATION_TIMEOUT, }, - AuthorityDiscoveryId, CandidateCommitments, CandidateDescriptor, CandidateEvent, - CandidateReceipt, ExecutorParams, Hash, PersistedValidationData, + vstaging::{ + CandidateDescriptorV2 as CandidateDescriptor, CandidateEvent, + CandidateReceiptV2 as CandidateReceipt, + }, + AuthorityDiscoveryId, CandidateCommitments, ExecutorParams, Hash, PersistedValidationData, PvfExecKind as RuntimePvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; @@ -437,7 +440,7 @@ where .into_iter() .filter_map(|e| match e { CandidateEvent::CandidateBacked(receipt, ..) => { - let h = receipt.descriptor.validation_code_hash; + let h = receipt.descriptor.validation_code_hash(); if already_prepared.contains(&h) { None } else { @@ -646,7 +649,7 @@ async fn validate_candidate_exhaustive( let _timer = metrics.time_validate_candidate_exhaustive(); let validation_code_hash = validation_code.hash(); - let para_id = candidate_receipt.descriptor.para_id; + let para_id = candidate_receipt.descriptor.para_id(); gum::debug!( target: LOG_TARGET, ?validation_code_hash, @@ -747,7 +750,7 @@ async fn validate_candidate_exhaustive( Err(ValidationFailed(e.to_string())) }, Ok(res) => - if res.head_data.hash() != candidate_receipt.descriptor.para_head { + if res.head_data.hash() != candidate_receipt.descriptor.para_head() { gum::info!(target: LOG_TARGET, ?para_id, "Invalid candidate (para_head)"); Ok(ValidationResult::Invalid(InvalidCandidate::ParaHeadHashMismatch)) } else { @@ -992,11 +995,11 @@ fn perform_basic_checks( return Err(InvalidCandidate::ParamsTooLarge(encoded_pov_size as u64)) } - if pov_hash != candidate.pov_hash { + if pov_hash != candidate.pov_hash() { return Err(InvalidCandidate::PoVHashMismatch) } - if *validation_code_hash != candidate.validation_code_hash { + if *validation_code_hash != candidate.validation_code_hash() { return Err(InvalidCandidate::CodeHashMismatch) } diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 2f7baf4abb61..997a347631a0 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -26,8 +26,8 @@ use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; use polkadot_primitives::{ - CoreIndex, GroupIndex, HeadData, Id as ParaId, OccupiedCoreAssumption, SessionInfo, - UpwardMessage, ValidatorId, + vstaging::CandidateDescriptorV2, CandidateDescriptor, CoreIndex, GroupIndex, HeadData, + Id as ParaId, OccupiedCoreAssumption, SessionInfo, UpwardMessage, ValidatorId, }; use polkadot_primitives_test_helpers::{ dummy_collator, dummy_collator_signature, dummy_hash, make_valid_candidate_descriptor, @@ -106,7 +106,8 @@ fn correctly_checks_included_assumption() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -180,7 +181,8 @@ fn correctly_checks_timed_out_assumption() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -252,7 +254,8 @@ fn check_is_bad_request_if_no_validation_data() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -308,7 +311,8 @@ fn check_is_bad_request_if_no_validation_code() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -376,7 +380,8 @@ fn check_does_not_match() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -478,7 +483,8 @@ fn candidate_validation_ok_is_ok() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -546,7 +552,8 @@ fn candidate_validation_bad_return_is_invalid() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -580,7 +587,7 @@ fn perform_basic_checks_on_valid_candidate( validation_code: &ValidationCode, validation_data: &PersistedValidationData, head_data_hash: Hash, -) -> CandidateDescriptor { +) -> CandidateDescriptorV2 { let descriptor = make_valid_candidate_descriptor( ParaId::from(1_u32), dummy_hash(), @@ -590,7 +597,8 @@ fn perform_basic_checks_on_valid_candidate( head_data_hash, head_data_hash, Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -784,7 +792,8 @@ fn candidate_validation_retry_on_error_helper( dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -824,7 +833,8 @@ fn candidate_validation_timeout_is_internal_error() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -869,9 +879,11 @@ fn candidate_validation_commitment_hash_mismatch_is_invalid() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ), + ) + .into(), commitments_hash: Hash::zero(), - }; + } + .into(); // This will result in different commitments for this candidate. let validation_result = WasmValidationResult { @@ -915,7 +927,8 @@ fn candidate_validation_code_mismatch_is_invalid() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -970,7 +983,8 @@ fn compressed_code_works() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let validation_result = WasmValidationResult { head_data, @@ -1239,7 +1253,8 @@ fn dummy_candidate_backed( signature: dummy_collator_signature(), para_head: zeros, validation_code_hash, - }; + } + .into(); CandidateEvent::CandidateBacked( CandidateReceipt { descriptor, commitments_hash: zeros }, diff --git a/polkadot/node/core/dispute-coordinator/Cargo.toml b/polkadot/node/core/dispute-coordinator/Cargo.toml index eb4600b235b9..344b66af1933 100644 --- a/polkadot/node/core/dispute-coordinator/Cargo.toml +++ b/polkadot/node/core/dispute-coordinator/Cargo.toml @@ -37,6 +37,7 @@ polkadot-primitives-test-helpers = { workspace = true } futures-timer = { workspace = true } sp-application-crypto = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } [features] # If not enabled, the dispute coordinator will do nothing. diff --git a/polkadot/node/core/dispute-coordinator/src/db/v1.rs b/polkadot/node/core/dispute-coordinator/src/db/v1.rs index 0101791550ee..962dfcbbcfac 100644 --- a/polkadot/node/core/dispute-coordinator/src/db/v1.rs +++ b/polkadot/node/core/dispute-coordinator/src/db/v1.rs @@ -25,8 +25,9 @@ use polkadot_node_primitives::DisputeStatus; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, Hash, InvalidDisputeStatementKind, SessionIndex, - ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, Hash, + InvalidDisputeStatementKind, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, + ValidatorSignature, }; use std::sync::Arc; @@ -377,7 +378,9 @@ mod tests { use super::*; use polkadot_node_primitives::DISPUTE_WINDOW; use polkadot_primitives::{Hash, Id as ParaId}; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt, dummy_candidate_receipt_v2, dummy_hash, + }; fn make_db() -> DbBackend { let db = kvdb_memorydb::create(1); @@ -403,7 +406,7 @@ mod tests { session, candidate_hash, CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }, @@ -495,7 +498,7 @@ mod tests { 1, CandidateHash(Hash::repeat_byte(1)), CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }, @@ -508,7 +511,7 @@ mod tests { let mut receipt = dummy_candidate_receipt(dummy_hash()); receipt.descriptor.para_id = ParaId::from(5_u32); - receipt + receipt.into() }, valid: Vec::new(), invalid: Vec::new(), @@ -532,7 +535,7 @@ mod tests { .unwrap() .candidate_receipt .descriptor - .para_id, + .para_id(), ParaId::from(5), ); @@ -556,7 +559,7 @@ mod tests { .unwrap() .candidate_receipt .descriptor - .para_id, + .para_id(), ParaId::from(5), ); } @@ -571,13 +574,13 @@ mod tests { 1, CandidateHash(Hash::repeat_byte(1)), CandidateVotes { - candidate_receipt: dummy_candidate_receipt(Hash::random()), + candidate_receipt: dummy_candidate_receipt_v2(Hash::random()), valid: Vec::new(), invalid: Vec::new(), }, ); - let receipt = dummy_candidate_receipt(dummy_hash()); + let receipt = dummy_candidate_receipt_v2(dummy_hash()); overlay_db.write_candidate_votes( 1, @@ -621,7 +624,7 @@ mod tests { let very_recent = current_session - 1; let blank_candidate_votes = || CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }; diff --git a/polkadot/node/core/dispute-coordinator/src/import.rs b/polkadot/node/core/dispute-coordinator/src/import.rs index d3a4625f0d24..4263dda54b9b 100644 --- a/polkadot/node/core/dispute-coordinator/src/import.rs +++ b/polkadot/node/core/dispute-coordinator/src/import.rs @@ -34,9 +34,9 @@ use polkadot_node_primitives::{ use polkadot_node_subsystem::overseer; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, DisputeStatement, ExecutorParams, Hash, IndexedVec, - SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, - ValidatorPair, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, DisputeStatement, + ExecutorParams, Hash, IndexedVec, SessionIndex, SessionInfo, ValidDisputeStatementKind, + ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 9cf9047b7273..7fc22d5904c5 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -44,9 +44,10 @@ use polkadot_node_subsystem_util::runtime::{ self, key_ownership_proof, submit_report_dispute_lost, RuntimeInfo, }; use polkadot_primitives::{ - slashing, BlockNumber, CandidateHash, CandidateReceipt, CompactStatement, DisputeStatement, - DisputeStatementSet, Hash, ScrapedOnChainVotes, SessionIndex, ValidDisputeStatementKind, - ValidatorId, ValidatorIndex, + slashing, + vstaging::{CandidateReceiptV2 as CandidateReceipt, ScrapedOnChainVotes}, + BlockNumber, CandidateHash, CompactStatement, DisputeStatement, DisputeStatementSet, Hash, + SessionIndex, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, }; use schnellru::{LruMap, UnlimitedCompact}; @@ -607,7 +608,7 @@ impl Initialized { // the new active leaf as if we received them via gossip. for (candidate_receipt, backers) in backing_validators_per_candidate { // Obtain the session info, for sake of `ValidatorId`s - let relay_parent = candidate_receipt.descriptor.relay_parent; + let relay_parent = candidate_receipt.descriptor.relay_parent(); let session_info = match self .runtime_info .get_session_info_by_index(ctx.sender(), relay_parent, session) @@ -958,9 +959,9 @@ impl Initialized { let votes_in_db = overlay_db.load_candidate_votes(session, &candidate_hash)?; let relay_parent = match &candidate_receipt { MaybeCandidateReceipt::Provides(candidate_receipt) => - candidate_receipt.descriptor().relay_parent, + candidate_receipt.descriptor().relay_parent(), MaybeCandidateReceipt::AssumeBackingVotePresent(candidate_hash) => match &votes_in_db { - Some(votes) => votes.candidate_receipt.descriptor().relay_parent, + Some(votes) => votes.candidate_receipt.descriptor().relay_parent(), None => { gum::warn!( target: LOG_TARGET, @@ -1451,7 +1452,7 @@ impl Initialized { ctx, &mut self.runtime_info, session, - candidate_receipt.descriptor.relay_parent, + candidate_receipt.descriptor.relay_parent(), self.offchain_disabled_validators.iter(session), ) .await diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index 84408eb96305..3078ada5d53f 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -46,7 +46,7 @@ use polkadot_node_subsystem_util::{ runtime::{Config as RuntimeInfoConfig, RuntimeInfo}, }; use polkadot_primitives::{ - DisputeStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidatorIndex, + vstaging::ScrapedOnChainVotes, DisputeStatement, SessionIndex, SessionInfo, ValidatorIndex, }; use crate::{ diff --git a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs index 2220f65e20a7..770c44f7d609 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs @@ -31,7 +31,10 @@ use polkadot_node_subsystem::{ overseer, ActiveLeavesUpdate, RecoveryError, }; use polkadot_node_subsystem_util::runtime::get_validation_code_by_hash; -use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, Hash, SessionIndex}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, Hash, + SessionIndex, +}; use crate::LOG_TARGET; @@ -348,7 +351,7 @@ async fn participate( let validation_code = match get_validation_code_by_hash( &mut sender, block_hash, - req.candidate_receipt().descriptor.validation_code_hash, + req.candidate_receipt().descriptor.validation_code_hash(), ) .await { @@ -357,7 +360,7 @@ async fn participate( gum::warn!( target: LOG_TARGET, "Validation code unavailable for code hash {:?} in the state of block {:?}", - req.candidate_receipt().descriptor.validation_code_hash, + req.candidate_receipt().descriptor.validation_code_hash(), block_hash, ); diff --git a/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs index d9e86def168c..4d317d38590a 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs @@ -22,7 +22,8 @@ use std::{ use futures::channel::oneshot; use polkadot_node_subsystem::{messages::ChainApiMessage, overseer}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, ExecutorParams, Hash, SessionIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, ExecutorParams, + Hash, SessionIndex, }; use crate::{ @@ -405,7 +406,7 @@ impl CandidateComparator { candidate: &CandidateReceipt, ) -> FatalResult { let candidate_hash = candidate.hash(); - let n = get_block_number(sender, candidate.descriptor().relay_parent).await?; + let n = get_block_number(sender, candidate.descriptor().relay_parent()).await?; if n.is_none() { gum::warn!( diff --git a/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs index 9176d00b2f5c..a25387a7eb5a 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs @@ -17,13 +17,13 @@ use crate::{metrics::Metrics, ParticipationPriority}; use assert_matches::assert_matches; use polkadot_primitives::{BlockNumber, Hash}; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; +use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; use super::{CandidateComparator, ParticipationRequest, QueueError, Queues}; /// Make a `ParticipationRequest` based on the given commitments hash. fn make_participation_request(hash: Hash) -> ParticipationRequest { - let mut receipt = dummy_candidate_receipt(dummy_hash()); + let mut receipt = dummy_candidate_receipt_v2(dummy_hash()); // make it differ: receipt.commitments_hash = hash; let request_timer = Metrics::default().time_participation_pipeline(); diff --git a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs index a6ab6f16df05..23f7984965b3 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs @@ -68,7 +68,8 @@ async fn participate_with_commitments_hash( let mut receipt = dummy_candidate_receipt_bad_sig(dummy_hash(), dummy_hash()); receipt.commitments_hash = commitments_hash; receipt - }; + } + .into(); let session = 1; let request_timer = participation.metrics.time_participation_pipeline(); diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs index 4c45d9dcc220..9aaad9d1c528 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs @@ -28,8 +28,9 @@ use polkadot_node_subsystem_util::runtime::{ self, get_candidate_events, get_on_chain_votes, get_unapplied_slashes, }; use polkadot_primitives::{ - slashing::PendingSlashes, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, Hash, - ScrapedOnChainVotes, SessionIndex, + slashing::PendingSlashes, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt, ScrapedOnChainVotes}, + BlockNumber, CandidateHash, Hash, SessionIndex, }; use crate::{ diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs index ed2400387ef7..fe04193014c6 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs @@ -36,8 +36,9 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_node_subsystem_util::{reexports::SubsystemContext, TimeoutExt}; use polkadot_primitives::{ - BlakeTwo256, BlockNumber, CandidateDescriptor, CandidateEvent, CandidateReceipt, CoreIndex, - GroupIndex, Hash, HashT, HeadData, Id as ParaId, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlakeTwo256, BlockNumber, CandidateDescriptor, CoreIndex, GroupIndex, Hash, HashT, HeadData, + Id as ParaId, }; use polkadot_primitives_test_helpers::{dummy_collator, dummy_collator_signature, dummy_hash}; @@ -135,7 +136,8 @@ fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { signature: dummy_collator_signature(), para_head: zeros, validation_code_hash: zeros.into(), - }; + } + .into(); CandidateReceipt { descriptor, commitments_hash: zeros } } diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index 48762a1d80be..9383f71804ed 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -60,13 +60,18 @@ use polkadot_node_subsystem_test_helpers::{ make_buffered_subsystem_context, mock::new_leaf, TestSubsystemContextHandle, }; use polkadot_primitives::{ - ApprovalVote, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CandidateReceipt, CoreIndex, DisputeStatement, ExecutorParams, GroupIndex, Hash, HeadData, - Header, IndexedVec, MultiDisputeStatementSet, NodeFeatures, ScrapedOnChainVotes, SessionIndex, - SessionInfo, SigningContext, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, - ValidatorSignature, + vstaging::{ + CandidateEvent, CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2, + ScrapedOnChainVotes, + }, + ApprovalVote, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, DisputeStatement, + ExecutorParams, GroupIndex, Hash, HeadData, Header, IndexedVec, MultiDisputeStatementSet, + NodeFeatures, SessionIndex, SessionInfo, SigningContext, ValidDisputeStatementKind, + ValidatorId, ValidatorIndex, ValidatorSignature, +}; +use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt_v2_bad_sig, dummy_digest, dummy_hash, }; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt_bad_sig, dummy_digest, dummy_hash}; use crate::{ backend::Backend, @@ -648,11 +653,11 @@ fn make_valid_candidate_receipt() -> CandidateReceipt { } fn make_invalid_candidate_receipt() -> CandidateReceipt { - dummy_candidate_receipt_bad_sig(Default::default(), Some(Default::default())) + dummy_candidate_receipt_v2_bad_sig(Default::default(), Some(Default::default())) } fn make_another_valid_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { - let mut candidate_receipt = dummy_candidate_receipt_bad_sig(relay_parent, dummy_hash()); + let mut candidate_receipt = dummy_candidate_receipt_v2_bad_sig(relay_parent, dummy_hash()); candidate_receipt.commitments_hash = CandidateCommitments::default().hash(); candidate_receipt } @@ -3858,14 +3863,15 @@ fn participation_requests_reprioritized_for_newly_included() { for repetition in 1..=3u8 { // Building candidate receipts let mut candidate_receipt = make_valid_candidate_receipt(); - candidate_receipt.descriptor.pov_hash = Hash::from( + candidate_receipt.descriptor.set_pov_hash(Hash::from( [repetition; 32], // Altering this receipt so its hash will be changed - ); + )); // Set consecutive parents (starting from zero). They will order the candidates for // participation. let parent_block_num: BlockNumber = repetition as BlockNumber - 1; - candidate_receipt.descriptor.relay_parent = - *test_state.block_num_to_header.get(&parent_block_num).unwrap(); + candidate_receipt.descriptor.set_relay_parent( + *test_state.block_num_to_header.get(&parent_block_num).unwrap(), + ); receipts.push(candidate_receipt.clone()); } diff --git a/polkadot/node/core/parachains-inherent/src/lib.rs b/polkadot/node/core/parachains-inherent/src/lib.rs index 1de3cab32bed..5f3092f6a881 100644 --- a/polkadot/node/core/parachains-inherent/src/lib.rs +++ b/polkadot/node/core/parachains-inherent/src/lib.rs @@ -29,7 +29,7 @@ use futures::{select, FutureExt}; use polkadot_node_subsystem::{ errors::SubsystemError, messages::ProvisionerMessage, overseer::Handle, }; -use polkadot_primitives::{Block, Hash, InherentData as ParachainsInherentData}; +use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block, Hash}; use std::{sync::Arc, time}; pub(crate) const LOG_TARGET: &str = "parachain::parachains-inherent"; diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 705014e67a05..5629e4ef7fbe 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -23,6 +23,7 @@ polkadot-node-subsystem-util = { workspace = true, default-features = true } assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } rand = { workspace = true } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs index b060897d4391..265d1498ee96 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -136,8 +136,9 @@ use polkadot_node_subsystem_util::inclusion_emulator::{ ProspectiveCandidate, RelayChainBlockInfo, }; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, - PersistedValidationData, ValidationCodeHash, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, BlockNumber, + CandidateCommitments, CandidateHash, Hash, HeadData, PersistedValidationData, + ValidationCodeHash, }; use thiserror::Error; @@ -371,7 +372,8 @@ impl CandidateEntry { persisted_validation_data: PersistedValidationData, state: CandidateState, ) -> Result { - if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { + if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash() + { return Err(CandidateEntryError::PersistedValidationDataMismatch) } @@ -386,13 +388,13 @@ impl CandidateEntry { candidate_hash, parent_head_data_hash, output_head_data_hash, - relay_parent: candidate.descriptor.relay_parent, + relay_parent: candidate.descriptor.relay_parent(), state, candidate: Arc::new(ProspectiveCandidate { commitments: candidate.commitments, persisted_validation_data, - pov_hash: candidate.descriptor.pov_hash, - validation_code_hash: candidate.descriptor.validation_code_hash, + pov_hash: candidate.descriptor.pov_hash(), + validation_code_hash: candidate.descriptor.validation_code_hash(), }), }) } @@ -407,8 +409,8 @@ impl HypotheticalOrConcreteCandidate for CandidateEntry { Some(&self.candidate.persisted_validation_data) } - fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { - Some(&self.candidate.validation_code_hash) + fn validation_code_hash(&self) -> Option { + Some(self.candidate.validation_code_hash) } fn parent_head_data_hash(&self) -> Hash { @@ -1090,7 +1092,7 @@ impl FragmentChain { &relay_parent, &constraints, commitments, - validation_code_hash, + &validation_code_hash, pvd, ) .map_err(Error::CheckAgainstConstraints)?; diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs index 3332cbeb03cb..9708b0871c2a 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -18,7 +18,8 @@ use super::*; use assert_matches::assert_matches; use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, Id as ParaId, + vstaging::MutateDescriptorV2, BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, + Id as ParaId, }; use polkadot_primitives_test_helpers as test_helpers; use rand::{seq::SliceRandom, thread_rng}; @@ -73,7 +74,8 @@ fn make_committed_candidate( signature: test_helpers::dummy_collator_signature(), para_head: para_head.hash(), validation_code_hash: Hash::repeat_byte(42).into(), - }, + } + .into(), commitments: CandidateCommitments { upward_messages: Default::default(), horizontal_messages: Default::default(), @@ -283,7 +285,7 @@ fn candidate_storage_methods() { candidate.commitments.head_data = HeadData(vec![1; 10]); let mut pvd = pvd.clone(); pvd.parent_head = HeadData(vec![1; 10]); - candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + candidate.descriptor.set_persisted_validation_data_hash(pvd.hash()); assert_matches!( CandidateEntry::new_seconded(candidate_hash, candidate, pvd), Err(CandidateEntryError::ZeroLengthCycle) @@ -291,7 +293,7 @@ fn candidate_storage_methods() { } assert!(!storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); + assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head()), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); // Add a valid candidate. @@ -305,9 +307,9 @@ fn candidate_storage_methods() { storage.add_candidate_entry(candidate_entry.clone()).unwrap(); assert!(storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); assert_eq!( - storage.head_data_by_hash(&candidate.descriptor.para_head).unwrap(), + storage.head_data_by_hash(&candidate.descriptor.para_head()).unwrap(), &candidate.commitments.head_data ); assert_eq!(storage.head_data_by_hash(&parent_head_hash).unwrap(), &pvd.parent_head); @@ -323,7 +325,7 @@ fn candidate_storage_methods() { .collect::>(), vec![candidate_hash] ); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); // Re-adding a candidate fails. assert_matches!( @@ -339,7 +341,7 @@ fn candidate_storage_methods() { storage.remove_candidate(&candidate_hash); assert!(!storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); + assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head()), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); storage @@ -354,7 +356,7 @@ fn candidate_storage_methods() { .collect::>(), vec![candidate_hash] ); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); // Now add a second candidate in Seconded state. This will be a fork. let (pvd_2, candidate_2) = make_committed_candidate( diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index b8b5f159e71c..34c1d8823bfc 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -49,9 +49,11 @@ use polkadot_node_subsystem_util::{ runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - async_backing::CandidatePendingAvailability, BlockNumber, CandidateHash, - CommittedCandidateReceipt, CoreState, Hash, HeadData, Header, Id as ParaId, - PersistedValidationData, + vstaging::{ + async_backing::CandidatePendingAvailability, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + BlockNumber, CandidateHash, Hash, HeadData, Header, Id as ParaId, PersistedValidationData, }; use crate::{ @@ -453,12 +455,13 @@ async fn preprocess_candidates_pending_availability( for (i, pending) in pending_availability.into_iter().enumerate() { let Some(relay_parent) = - fetch_block_info(ctx, cache, pending.descriptor.relay_parent).await? + fetch_block_info(ctx, cache, pending.descriptor.relay_parent()).await? else { + let para_id = pending.descriptor.para_id(); gum::debug!( target: LOG_TARGET, ?pending.candidate_hash, - ?pending.descriptor.para_id, + ?para_id, index = ?i, ?expected_count, "Had to stop processing pending candidates early due to missing info.", diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 14a093239e8e..3f1eaa4e41ed 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -25,9 +25,12 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - async_backing::{AsyncBackingParams, BackingState, Constraints, InboundHrmpLimitations}, - CommittedCandidateReceipt, CoreIndex, HeadData, Header, PersistedValidationData, ScheduledCore, - ValidationCodeHash, + async_backing::{AsyncBackingParams, Constraints, InboundHrmpLimitations}, + vstaging::{ + async_backing::BackingState, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + MutateDescriptorV2, + }, + CoreIndex, HeadData, Header, PersistedValidationData, ScheduledCore, ValidationCodeHash, }; use polkadot_primitives_test_helpers::make_candidate; use rstest::rstest; @@ -393,15 +396,15 @@ async fn handle_leaf_activation( ); for pending in pending_availability { - if !used_relay_parents.contains(&pending.descriptor.relay_parent) { + if !used_relay_parents.contains(&pending.descriptor.relay_parent()) { send_block_header( virtual_overseer, - pending.descriptor.relay_parent, + pending.descriptor.relay_parent(), pending.relay_parent_number, ) .await; - used_relay_parents.insert(pending.descriptor.relay_parent); + used_relay_parents.insert(pending.descriptor.relay_parent()); } } } @@ -436,7 +439,7 @@ async fn introduce_seconded_candidate( pvd: PersistedValidationData, ) { let req = IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate, persisted_validation_data: pvd, }; @@ -455,7 +458,7 @@ async fn introduce_seconded_candidate_failed( pvd: PersistedValidationData, ) { let req = IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate, persisted_validation_data: pvd, }; @@ -476,7 +479,7 @@ async fn back_candidate( virtual_overseer .send(overseer::FromOrchestra::Communication { msg: ProspectiveParachainsMessage::CandidateBacked( - candidate.descriptor.para_id, + candidate.descriptor.para_id(), candidate_hash, ), }) @@ -568,7 +571,7 @@ macro_rules! make_and_back_candidate { $test_state.validation_code_hash, ); // Set a field to make this candidate unique. - candidate.descriptor.para_head = Hash::from_low_u64_le($index); + candidate.descriptor.set_para_head(Hash::from_low_u64_le($index)); let candidate_hash = candidate.hash(); introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; @@ -1378,7 +1381,7 @@ fn check_backable_query_single_candidate() { test_state.validation_code_hash, ); // Set a field to make this candidate unique. - candidate_b.descriptor.para_head = Hash::from_low_u64_le(1000); + candidate_b.descriptor.set_para_head(Hash::from_low_u64_le(1000)); let candidate_hash_b = candidate_b.hash(); // Introduce candidates. diff --git a/polkadot/node/core/provisioner/Cargo.toml b/polkadot/node/core/provisioner/Cargo.toml index 5869e494c70f..64a598b420f7 100644 --- a/polkadot/node/core/provisioner/Cargo.toml +++ b/polkadot/node/core/provisioner/Cargo.toml @@ -27,4 +27,6 @@ sp-application-crypto = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } + rstest = { workspace = true } diff --git a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs index ecb7aac78396..8c0d478b67df 100644 --- a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs +++ b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs @@ -427,7 +427,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 80 / 100; let session_idx = 0; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -445,7 +445,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 40 / 100; let session_idx = 1; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -462,7 +462,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 90 / 100; let session_idx = 2; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Confirmed); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -478,7 +478,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 75 / 100; let session_idx = 3; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::ConcludedFor(0)); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -494,7 +494,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 90 / 100; let session_idx = 4; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::ConcludedFor(0)); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -510,7 +510,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 10 / 100; let session_idx = 5; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -527,7 +527,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 10 / 100; let session_idx = 6; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 9a06d9cff0cc..a95df6c5f880 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -41,9 +41,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - node_features::FeatureIndex, BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, - CoreIndex, CoreState, Hash, Id as ParaId, NodeFeatures, OccupiedCoreAssumption, SessionIndex, - SignedAvailabilityBitfield, ValidatorIndex, + node_features::FeatureIndex, + vstaging::{BackedCandidate, CandidateReceiptV2 as CandidateReceipt, CoreState}, + BlockNumber, CandidateHash, CoreIndex, Hash, Id as ParaId, NodeFeatures, + OccupiedCoreAssumption, SessionIndex, SignedAvailabilityBitfield, ValidatorIndex, }; use std::collections::{BTreeMap, HashMap}; @@ -361,10 +362,9 @@ fn note_provisionable_data( gum::trace!( target: LOG_TARGET, ?candidate_hash, - para = ?backed_candidate.descriptor().para_id, + para = ?backed_candidate.descriptor().para_id(), "noted backed candidate", ); - per_relay_parent.backed_candidates.push(backed_candidate); }, // We choose not to punish these forms of misbehavior for the time being. @@ -650,22 +650,22 @@ async fn select_candidate_hashes_from_tracked( // selection criteria if let Some(candidate) = candidates.iter().find(|backed_candidate| { let descriptor = &backed_candidate.descriptor; - descriptor.para_id == scheduled_core.para_id && - descriptor.persisted_validation_data_hash == computed_validation_data_hash + descriptor.para_id() == scheduled_core.para_id && + descriptor.persisted_validation_data_hash() == computed_validation_data_hash }) { let candidate_hash = candidate.hash(); gum::trace!( target: LOG_TARGET, leaf_hash=?relay_parent, ?candidate_hash, - para = ?candidate.descriptor.para_id, + para = ?candidate.descriptor.para_id(), core = core_idx, "Selected candidate receipt", ); selected_candidates.insert( - candidate.descriptor.para_id, - vec![(candidate_hash, candidate.descriptor.relay_parent)], + candidate.descriptor.para_id(), + vec![(candidate_hash, candidate.descriptor.relay_parent())], ); } } diff --git a/polkadot/node/core/provisioner/src/tests.rs b/polkadot/node/core/provisioner/src/tests.rs index b38459302c8f..a09b243f3ab1 100644 --- a/polkadot/node/core/provisioner/src/tests.rs +++ b/polkadot/node/core/provisioner/src/tests.rs @@ -16,14 +16,17 @@ use super::*; use bitvec::bitvec; -use polkadot_primitives::{OccupiedCore, ScheduledCore}; -use polkadot_primitives_test_helpers::{dummy_candidate_descriptor, dummy_hash}; +use polkadot_primitives::{ + vstaging::{MutateDescriptorV2, OccupiedCore}, + ScheduledCore, +}; +use polkadot_primitives_test_helpers::{dummy_candidate_descriptor_v2, dummy_hash}; const MOCK_GROUP_SIZE: usize = 5; pub fn occupied_core(para_id: u32) -> CoreState { - let mut candidate_descriptor = dummy_candidate_descriptor(dummy_hash()); - candidate_descriptor.para_id = para_id.into(); + let mut candidate_descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + candidate_descriptor.set_para_id(para_id.into()); CoreState::Occupied(OccupiedCore { group_responsible: para_id.into(), @@ -32,7 +35,7 @@ pub fn occupied_core(para_id: u32) -> CoreState { time_out_at: 200_u32, next_up_on_time_out: None, availability: bitvec![u8, bitvec::order::Lsb0; 0; 32], - candidate_descriptor, + candidate_descriptor: candidate_descriptor.into(), candidate_hash: Default::default(), }) } @@ -254,9 +257,10 @@ mod select_candidates { use polkadot_node_subsystem_test_helpers::TestSubsystemSender; use polkadot_node_subsystem_util::runtime::ProspectiveParachainsMode; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CommittedCandidateReceipt, PersistedValidationData, + vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateCommitments, PersistedValidationData, }; - use polkadot_primitives_test_helpers::{dummy_candidate_descriptor, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_descriptor_v2, dummy_hash}; use rstest::rstest; use std::ops::Not; use CoreState::{Free, Scheduled}; @@ -266,8 +270,8 @@ mod select_candidates { fn dummy_candidate_template() -> CandidateReceipt { let empty_hash = PersistedValidationData::::default().hash(); - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; + let mut descriptor_template = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor_template.set_persisted_validation_data_hash(empty_hash); CandidateReceipt { descriptor: descriptor_template, commitments_hash: CandidateCommitments::default().hash(), @@ -283,7 +287,7 @@ mod select_candidates { .take(core_count) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate }) .collect(); @@ -559,14 +563,14 @@ mod select_candidates { use RuntimeApiMessage::Request; let mut backed = expected.clone().into_iter().fold(HashMap::new(), |mut acc, candidate| { - acc.entry(candidate.descriptor().para_id).or_insert(vec![]).push(candidate); + acc.entry(candidate.descriptor().para_id()).or_insert(vec![]).push(candidate); acc }); - expected.sort_by_key(|c| c.candidate().descriptor.para_id); + expected.sort_by_key(|c| c.candidate().descriptor.para_id()); let mut candidates_iter = expected .iter() - .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent)); + .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent())); while let Some(from_job) = receiver.next().await { match from_job { @@ -601,7 +605,7 @@ mod select_candidates { candidates .iter() .map(|candidate| { - (candidate.hash(), candidate.descriptor().relay_parent) + (candidate.hash(), candidate.descriptor().relay_parent()) }) .collect(), ) @@ -707,7 +711,7 @@ mod select_candidates { .take(mock_cores.len()) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate }) .cycle() @@ -719,11 +723,11 @@ mod select_candidates { candidate } else if idx < mock_cores.len() * 2 { // for the second repetition of the candidates, give them the wrong hash - candidate.descriptor.persisted_validation_data_hash = Default::default(); + candidate.descriptor.set_persisted_validation_data_hash(Default::default()); candidate } else { // third go-around: right hash, wrong para_id - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate } }) @@ -807,9 +811,9 @@ mod select_candidates { let committed_receipts: Vec<_> = (0..=mock_cores.len()) .map(|i| { - let mut descriptor = dummy_candidate_descriptor(dummy_hash()); - descriptor.para_id = i.into(); - descriptor.persisted_validation_data_hash = empty_hash; + let mut descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor.set_para_id(i.into()); + descriptor.set_persisted_validation_data_hash(empty_hash); CommittedCandidateReceipt { descriptor, commitments: CandidateCommitments { @@ -917,14 +921,14 @@ mod select_candidates { let committed_receipts: Vec<_> = (0..mock_cores.len()) .map(|i| { - let mut descriptor = dummy_candidate_descriptor(dummy_hash()); - descriptor.para_id = if let Scheduled(scheduled_core) = &mock_cores[i] { + let mut descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor.set_para_id(if let Scheduled(scheduled_core) = &mock_cores[i] { scheduled_core.para_id } else { panic!("`mock_cores` is not initialized with `Scheduled`?") - }; - descriptor.persisted_validation_data_hash = empty_hash; - descriptor.pov_hash = Hash::from_low_u64_be(i as u64); + }); + descriptor.set_persisted_validation_data_hash(empty_hash); + descriptor.set_pov_hash(Hash::from_low_u64_be(i as u64)); CommittedCandidateReceipt { descriptor, commitments: CandidateCommitments { @@ -1222,8 +1226,8 @@ mod select_candidates { .take(mock_cores.len() + 1) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); - candidate.descriptor.relay_parent = Hash::repeat_byte(idx as u8); + candidate.descriptor.set_para_id(idx.into()); + candidate.descriptor.set_relay_parent(Hash::repeat_byte(idx as u8)); candidate }) .collect(); diff --git a/polkadot/node/core/runtime-api/src/cache.rs b/polkadot/node/core/runtime-api/src/cache.rs index 05efbc533d02..7246010711e4 100644 --- a/polkadot/node/core/runtime-api/src/cache.rs +++ b/polkadot/node/core/runtime-api/src/cache.rs @@ -20,12 +20,16 @@ use schnellru::{ByLength, LruMap}; use sp_consensus_babe::Epoch; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + async_backing, slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, - ValidationCodeHash, ValidatorId, ValidatorIndex, + PersistedValidationData, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, }; /// For consistency we have the same capacity for all caches. We use 128 as we'll only need that @@ -66,7 +70,7 @@ pub(crate) struct RequestResultCache { key_ownership_proof: LruMap<(Hash, ValidatorId), Option>, minimum_backing_votes: LruMap, disabled_validators: LruMap>, - para_backing_state: LruMap<(Hash, ParaId), Option>, + para_backing_state: LruMap<(Hash, ParaId), Option>, async_backing_params: LruMap, node_features: LruMap, approval_voting_params: LruMap, @@ -499,14 +503,14 @@ impl RequestResultCache { pub(crate) fn para_backing_state( &mut self, key: (Hash, ParaId), - ) -> Option<&Option> { + ) -> Option<&Option> { self.para_backing_state.get(&key).map(|v| &*v) } pub(crate) fn cache_para_backing_state( &mut self, key: (Hash, ParaId), - value: Option, + value: Option, ) { self.para_backing_state.insert(key, value); } @@ -601,7 +605,7 @@ pub(crate) enum RequestResult { SubmitReportDisputeLost(Option<()>), ApprovalVotingParams(Hash, SessionIndex, ApprovalVotingParams), DisabledValidators(Hash, Vec), - ParaBackingState(Hash, ParaId, Option), + ParaBackingState(Hash, ParaId, Option), AsyncBackingParams(Hash, async_backing::AsyncBackingParams), NodeFeatures(SessionIndex, NodeFeatures), ClaimQueue(Hash, BTreeMap>), diff --git a/polkadot/node/core/runtime-api/src/tests.rs b/polkadot/node/core/runtime-api/src/tests.rs index 7c382707264f..d4fa07323886 100644 --- a/polkadot/node/core/runtime-api/src/tests.rs +++ b/polkadot/node/core/runtime-api/src/tests.rs @@ -20,14 +20,20 @@ use polkadot_node_primitives::{BabeAllowedSlots, BabeEpoch, BabeEpochConfigurati use polkadot_node_subsystem::SpawnGlue; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Id as ParaId, + async_backing, slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - Slot, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, Slot, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, +}; +use polkadot_primitives_test_helpers::{ + dummy_committed_candidate_receipt_v2, dummy_validation_code, }; -use polkadot_primitives_test_helpers::{dummy_committed_candidate_receipt, dummy_validation_code}; use sp_api::ApiError; use sp_core::testing::TaskExecutor; use std::{ @@ -279,7 +285,7 @@ impl RuntimeApiSubsystemClient for MockSubsystemClient { &self, _: Hash, _: ParaId, - ) -> Result, ApiError> { + ) -> Result, ApiError> { todo!("Not required for tests") } @@ -699,7 +705,7 @@ fn requests_candidate_pending_availability() { let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = sp_core::testing::TaskExecutor::new(); - let candidate_receipt = dummy_committed_candidate_receipt(relay_parent); + let candidate_receipt = dummy_committed_candidate_receipt_v2(relay_parent); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index 66926f48c5e7..7415e6c79df5 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -24,8 +24,10 @@ use crate::{ use polkadot_node_primitives::{InvalidCandidate, ValidationResult}; use polkadot_primitives::{ - CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData, - PvfExecKind, + vstaging::{ + CandidateDescriptorV2 as CandidateDescriptor, CandidateReceiptV2 as CandidateReceipt, + }, + CandidateCommitments, PersistedValidationData, PvfExecKind, }; use futures::channel::oneshot; @@ -203,7 +205,7 @@ fn create_validation_response( gum::debug!( target: MALUS, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), candidate_hash = ?candidate_receipt.hash(), "ValidationResult: {:?}", &result @@ -308,7 +310,7 @@ where gum::info!( target: MALUS, ?behave_maliciously, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), "😈 Maliciously sending invalid validation result: {:?}.", &validation_result, ); diff --git a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs index 7a95bdaead26..309be9e46d82 100644 --- a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs @@ -42,7 +42,7 @@ use polkadot_cli::{ use polkadot_node_subsystem::SpawnGlue; use polkadot_node_subsystem_types::{ChainApiBackend, OverseerSignal, RuntimeApiSubsystemClient}; use polkadot_node_subsystem_util::request_candidate_events; -use polkadot_primitives::CandidateEvent; +use polkadot_primitives::vstaging::CandidateEvent; use sp_core::traits::SpawnNamed; // Filter wrapping related types. diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index ab2d380fbaf4..2fe08c8a1c49 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -32,7 +32,7 @@ use polkadot_cli::{ }; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; -use polkadot_primitives::{CandidateDescriptor, CandidateReceipt}; +use polkadot_primitives::{vstaging::CandidateReceiptV2, CandidateDescriptor}; use polkadot_node_subsystem_util::request_validators; use sp_core::traits::SpawnNamed; @@ -127,7 +127,7 @@ where let validation_code = { let validation_code_hash = - _candidate.descriptor().validation_code_hash; + _candidate.descriptor().validation_code_hash(); let (tx, rx) = oneshot::channel(); new_sender .send_message(RuntimeApiMessage::Request( @@ -214,7 +214,7 @@ where let collator_pair = CollatorPair::generate().0; let signature_payload = polkadot_primitives::collator_signature_payload( &relay_parent, - &candidate.descriptor().para_id, + &candidate.descriptor().para_id(), &validation_data_hash, &pov_hash, &validation_code_hash, @@ -227,9 +227,9 @@ where &malicious_available_data.validation_data, ); - let malicious_candidate = CandidateReceipt { + let malicious_candidate = CandidateReceiptV2 { descriptor: CandidateDescriptor { - para_id: candidate.descriptor().para_id, + para_id: candidate.descriptor.para_id(), relay_parent, collator: collator_id, persisted_validation_data_hash: validation_data_hash, @@ -238,7 +238,8 @@ where signature: collator_signature, para_head: malicious_commitments.head_data.hash(), validation_code_hash, - }, + } + .into(), commitments_hash: malicious_commitments.hash(), }; let malicious_candidate_hash = malicious_candidate.hash(); diff --git a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs index 5be6f2d223a8..c4654b843c44 100644 --- a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs @@ -35,8 +35,8 @@ use polkadot_node_subsystem::{ overseer, }; use polkadot_primitives::{ - AuthorityDiscoveryId, BlakeTwo256, CandidateHash, ChunkIndex, GroupIndex, Hash, HashT, - OccupiedCore, SessionIndex, + vstaging::OccupiedCore, AuthorityDiscoveryId, BlakeTwo256, CandidateHash, ChunkIndex, + GroupIndex, Hash, HashT, SessionIndex, }; use sc_network::ProtocolName; @@ -170,8 +170,8 @@ impl FetchTaskConfig { candidate_hash: core.candidate_hash, index: session_info.our_index, }, - erasure_root: core.candidate_descriptor.erasure_root, - relay_parent: core.candidate_descriptor.relay_parent, + erasure_root: core.candidate_descriptor.erasure_root(), + relay_parent: core.candidate_descriptor.relay_parent(), metrics, sender, chunk_index, diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index 233825032724..613a514269ee 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -38,7 +38,7 @@ use polkadot_node_subsystem_util::{ availability_chunks::availability_chunk_index, runtime::{get_occupied_cores, RuntimeInfo}, }; -use polkadot_primitives::{CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex}; +use polkadot_primitives::{vstaging::OccupiedCore, CandidateHash, CoreIndex, Hash, SessionIndex}; use super::{FatalError, Metrics, Result, LOG_TARGET}; diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index 021f6da7e2e9..ebcba2a038bc 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -21,7 +21,7 @@ use polkadot_node_network_protocol::request_response::ReqProtocolNames; use polkadot_node_primitives::{BlockData, ErasureChunk, PoV}; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ - BlockNumber, ChunkIndex, CoreState, ExecutorParams, GroupIndex, Hash, Id as ParaId, + vstaging::CoreState, BlockNumber, ChunkIndex, ExecutorParams, GroupIndex, Hash, Id as ParaId, ScheduledCore, SessionIndex, SessionInfo, }; use sp_core::{testing::TaskExecutor, traits::SpawnNamed}; diff --git a/polkadot/node/network/availability-distribution/src/tests/mock.rs b/polkadot/node/network/availability-distribution/src/tests/mock.rs index b41c493a1072..f900cb6e6156 100644 --- a/polkadot/node/network/availability-distribution/src/tests/mock.rs +++ b/polkadot/node/network/availability-distribution/src/tests/mock.rs @@ -23,8 +23,9 @@ use sp_keyring::Sr25519Keyring; use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks}; use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV, Proof}; use polkadot_primitives::{ + vstaging::{CommittedCandidateReceiptV2, OccupiedCore}, CandidateCommitments, CandidateDescriptor, CandidateHash, ChunkIndex, - CommittedCandidateReceipt, GroupIndex, Hash, HeadData, Id as ParaId, IndexedVec, OccupiedCore, + CommittedCandidateReceipt, GroupIndex, Hash, HeadData, Id as ParaId, IndexedVec, PersistedValidationData, SessionInfo, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ @@ -101,7 +102,7 @@ impl OccupiedCoreBuilder { availability: Default::default(), group_responsible: self.group_responsible, candidate_hash: candidate_receipt.hash(), - candidate_descriptor: candidate_receipt.descriptor().clone(), + candidate_descriptor: candidate_receipt.descriptor.clone(), }; (core, (candidate_receipt.hash(), chunk)) } @@ -117,7 +118,7 @@ pub struct TestCandidateBuilder { } impl TestCandidateBuilder { - pub fn build(self) -> CommittedCandidateReceipt { + pub fn build(self) -> CommittedCandidateReceiptV2 { CommittedCandidateReceipt { descriptor: CandidateDescriptor { para_id: self.para_id, @@ -132,6 +133,7 @@ impl TestCandidateBuilder { }, commitments: CandidateCommitments { head_data: self.head_data, ..Default::default() }, } + .into() } } diff --git a/polkadot/node/network/availability-distribution/src/tests/mod.rs b/polkadot/node/network/availability-distribution/src/tests/mod.rs index 078220607c37..d4abd4e32d9b 100644 --- a/polkadot/node/network/availability-distribution/src/tests/mod.rs +++ b/polkadot/node/network/availability-distribution/src/tests/mod.rs @@ -22,7 +22,7 @@ use rstest::rstest; use polkadot_node_network_protocol::request_response::{ IncomingRequest, Protocol, ReqProtocolNames, }; -use polkadot_primitives::{node_features, Block, CoreState, Hash, NodeFeatures}; +use polkadot_primitives::{node_features, vstaging::CoreState, Block, Hash, NodeFeatures}; use sp_keystore::KeystorePtr; use super::*; diff --git a/polkadot/node/network/availability-distribution/src/tests/state.rs b/polkadot/node/network/availability-distribution/src/tests/state.rs index 53d6fd2c530f..c6dd17a344e0 100644 --- a/polkadot/node/network/availability-distribution/src/tests/state.rs +++ b/polkadot/node/network/availability-distribution/src/tests/state.rs @@ -47,7 +47,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - CandidateHash, ChunkIndex, CoreIndex, CoreState, ExecutorParams, GroupIndex, Hash, + vstaging::CoreState, CandidateHash, ChunkIndex, CoreIndex, ExecutorParams, GroupIndex, Hash, Id as ParaId, NodeFeatures, ScheduledCore, SessionInfo, ValidatorIndex, }; use test_helpers::mock::{make_ferdie_keystore, new_leaf}; diff --git a/polkadot/node/network/availability-recovery/src/lib.rs b/polkadot/node/network/availability-recovery/src/lib.rs index 114faa2859c4..eb54d9657d83 100644 --- a/polkadot/node/network/availability-recovery/src/lib.rs +++ b/polkadot/node/network/availability-recovery/src/lib.rs @@ -66,8 +66,8 @@ use polkadot_node_subsystem_util::{ runtime::{ExtendedSessionInfo, RuntimeInfo}, }; use polkadot_primitives::{ - node_features, BlockNumber, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, GroupIndex, - Hash, SessionIndex, ValidatorIndex, + node_features, vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, + ChunkIndex, CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, }; mod error; @@ -540,11 +540,11 @@ async fn handle_recover( threshold: recovery_threshold(n_validators)?, systematic_threshold, candidate_hash, - erasure_root: receipt.descriptor.erasure_root, + erasure_root: receipt.descriptor.erasure_root(), metrics: metrics.clone(), bypass_availability_store, post_recovery_check, - pov_hash: receipt.descriptor.pov_hash, + pov_hash: receipt.descriptor.pov_hash(), req_v1_protocol_name, req_v2_protocol_name, chunk_mapping_enabled, diff --git a/polkadot/node/network/availability-recovery/src/tests.rs b/polkadot/node/network/availability-recovery/src/tests.rs index 4fd9ede40ff6..9a46d5420782 100644 --- a/polkadot/node/network/availability-recovery/src/tests.rs +++ b/polkadot/node/network/availability-recovery/src/tests.rs @@ -41,8 +41,8 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - node_features, AuthorityDiscoveryId, Block, ExecutorParams, Hash, HeadData, IndexedVec, - NodeFeatures, PersistedValidationData, SessionInfo, ValidatorId, + node_features, vstaging::MutateDescriptorV2, AuthorityDiscoveryId, Block, ExecutorParams, Hash, + HeadData, IndexedVec, NodeFeatures, PersistedValidationData, SessionInfo, ValidatorId, }; use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; use sc_network::{IfDisconnected, OutboundFailure, ProtocolName, RequestFailure}; @@ -346,7 +346,7 @@ impl TestState { ) .unwrap(), current, - candidate, + candidate: candidate.into(), session_index, core_index, node_features, @@ -800,12 +800,12 @@ fn availability_is_recovered_from_chunks_if_no_group_provided(#[case] systematic // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, None, Some(test_state.core_index), @@ -929,12 +929,12 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, Some(GroupIndex(1)), Some(test_state.core_index), @@ -1218,7 +1218,7 @@ fn invalid_erasure_coding_leads_to_invalid_error(#[case] systematic_recovery: bo test_state.validators.len(), test_state.core_index, ); - test_state.candidate.descriptor.erasure_root = bad_erasure_root; + test_state.candidate.descriptor.set_erasure_root(bad_erasure_root); let candidate_hash = test_state.candidate.hash(); @@ -1283,7 +1283,7 @@ fn invalid_pov_hash_leads_to_invalid_error() { test_harness(subsystem, |mut virtual_overseer| async move { let pov = PoV { block_data: BlockData(vec![69; 64]) }; - test_state.candidate.descriptor.pov_hash = pov.hash(); + test_state.candidate.descriptor.set_pov_hash(pov.hash()); let candidate_hash = test_state.candidate.hash(); @@ -1420,7 +1420,10 @@ fn recovers_from_only_chunks_if_pov_large( test_state.threshold(), ), (false, true) => { - test_state.candidate.descriptor.pov_hash = test_state.available_data.pov.hash(); + test_state + .candidate + .descriptor + .set_pov_hash(test_state.available_data.pov.hash()); ( AvailabilityRecoverySubsystem::for_collator( None, @@ -1497,12 +1500,12 @@ fn recovers_from_only_chunks_if_pov_large( // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, Some(GroupIndex(1)), Some(test_state.core_index), @@ -1593,7 +1596,10 @@ fn fast_path_backing_group_recovers_if_pov_small( Metrics::new_dummy(), ), (false, true) => { - test_state.candidate.descriptor.pov_hash = test_state.available_data.pov.hash(); + test_state + .candidate + .descriptor + .set_pov_hash(test_state.available_data.pov.hash()); AvailabilityRecoverySubsystem::for_collator( None, request_receiver(&req_protocol_names), @@ -2635,7 +2641,7 @@ fn number_of_request_retries_is_bounded( ); test_state.chunks = map_chunks(chunks, &test_state.node_features, n_validators, test_state.core_index); - test_state.candidate.descriptor.erasure_root = erasure_root; + test_state.candidate.descriptor.set_erasure_root(erasure_root); let (subsystem, retry_limit) = match systematic_recovery { false => ( diff --git a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs index 57e1479a449b..6a570331f710 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs @@ -28,7 +28,9 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::PoV; use polkadot_node_subsystem::messages::ParentHeadData; -use polkadot_primitives::{CandidateHash, CandidateReceipt, Hash, Id as ParaId}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, Hash, Id as ParaId, +}; /// The status of a collation as seen from the collator. pub enum CollationStatus { diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index af9beb535f46..504b0d716043 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -54,8 +54,9 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CandidateReceipt, CollatorPair, CoreIndex, CoreState, - GroupIndex, Hash, HeadData, Id as ParaId, SessionIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, + AuthorityDiscoveryId, CandidateHash, CollatorPair, CoreIndex, GroupIndex, Hash, HeadData, + Id as ParaId, SessionIndex, }; use super::LOG_TARGET; @@ -374,7 +375,7 @@ async fn distribute_collation( result_sender: Option>, core_index: CoreIndex, ) -> Result<()> { - let candidate_relay_parent = receipt.descriptor.relay_parent; + let candidate_relay_parent = receipt.descriptor.relay_parent(); let candidate_hash = receipt.hash(); let per_relay_parent = match state.per_relay_parent.get_mut(&candidate_relay_parent) { @@ -850,12 +851,12 @@ async fn process_msg( core_index, } => { match state.collating_on { - Some(id) if candidate_receipt.descriptor.para_id != id => { + Some(id) if candidate_receipt.descriptor.para_id() != id => { // If the ParaId of a collation requested to be distributed does not match // the one we expect, we ignore the message. gum::warn!( target: LOG_TARGET, - para_id = %candidate_receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id(), collating_on = %id, "DistributeCollation for unexpected para_id", ); @@ -879,7 +880,7 @@ async fn process_msg( None => { gum::warn!( target: LOG_TARGET, - para_id = %candidate_receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id(), "DistributeCollation message while not collating on any", ); }, diff --git a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs index 58d9ebc57726..0b3e9f4b3431 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs @@ -40,8 +40,8 @@ use polkadot_node_subsystem_util::{ metrics::prometheus::prometheus::HistogramTimer, runtime::ProspectiveParachainsMode, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CollatorId, Hash, HeadData, Id as ParaId, - PersistedValidationData, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CollatorId, Hash, HeadData, + Id as ParaId, PersistedValidationData, }; use tokio_util::sync::CancellationToken; @@ -71,18 +71,15 @@ pub struct FetchedCollation { pub para_id: ParaId, /// Candidate hash. pub candidate_hash: CandidateHash, - /// Id of the collator the collation was fetched from. - pub collator_id: CollatorId, } impl From<&CandidateReceipt> for FetchedCollation { fn from(receipt: &CandidateReceipt) -> Self { let descriptor = receipt.descriptor(); Self { - relay_parent: descriptor.relay_parent, - para_id: descriptor.para_id, + relay_parent: descriptor.relay_parent(), + para_id: descriptor.para_id(), candidate_hash: receipt.hash(), - collator_id: descriptor.collator.clone(), } } } @@ -141,7 +138,7 @@ pub fn fetched_collation_sanity_check( persisted_validation_data: &PersistedValidationData, maybe_parent_head_and_hash: Option<(HeadData, Hash)>, ) -> Result<(), SecondingError> { - if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash { + if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash() { Err(SecondingError::PersistedValidationDataMismatch) } else if advertised .prospective_candidate diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index deb6ce03f43e..51e987d59ce8 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -52,8 +52,8 @@ use polkadot_node_subsystem_util::{ runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - CandidateHash, CollatorId, CoreState, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, + vstaging::CoreState, CandidateHash, CollatorId, Hash, HeadData, Id as ParaId, + OccupiedCoreAssumption, PersistedValidationData, }; use crate::error::{Error, FetchError, Result, SecondingError}; @@ -1021,7 +1021,7 @@ async fn second_unblocked_collations( for mut unblocked_collation in unblocked_collations { unblocked_collation.maybe_parent_head_data = Some(head_data.clone()); let peer_id = unblocked_collation.collation_event.pending_collation.peer_id; - let relay_parent = unblocked_collation.candidate_receipt.descriptor.relay_parent; + let relay_parent = unblocked_collation.candidate_receipt.descriptor.relay_parent(); if let Err(err) = kick_off_seconding(ctx, state, unblocked_collation).await { gum::warn!( @@ -1328,7 +1328,7 @@ where collations.retain(|collation| { state .per_relay_parent - .contains_key(&collation.candidate_receipt.descriptor.relay_parent) + .contains_key(&collation.candidate_receipt.descriptor.relay_parent()) }); !collations.is_empty() @@ -1470,7 +1470,7 @@ async fn process_msg( }, }; let output_head_data = receipt.commitments.head_data.clone(); - let output_head_data_hash = receipt.descriptor.para_head; + let output_head_data_hash = receipt.descriptor.para_head(); let fetched_collation = FetchedCollation::from(&receipt.to_plain()); if let Some(CollationEvent { collator_id, pending_collation, .. }) = state.fetched_candidates.remove(&fetched_collation) @@ -1531,8 +1531,8 @@ async fn process_msg( Invalid(parent, candidate_receipt) => { // Remove collations which were blocked from seconding and had this candidate as parent. state.blocked_from_seconding.remove(&BlockedCollationId { - para_id: candidate_receipt.descriptor.para_id, - parent_head_data_hash: candidate_receipt.descriptor.para_head, + para_id: candidate_receipt.descriptor.para_id(), + parent_head_data_hash: candidate_receipt.descriptor.para_head(), }); let fetched_collation = FetchedCollation::from(&candidate_receipt); @@ -1843,8 +1843,8 @@ async fn kick_off_seconding( (CollationVersion::V2, Some(_)) | (CollationVersion::V1, _) => { let pvd = request_persisted_validation_data( ctx.sender(), - candidate_receipt.descriptor().relay_parent, - candidate_receipt.descriptor().para_id, + candidate_receipt.descriptor().relay_parent(), + candidate_receipt.descriptor().para_id(), ) .await?; ( @@ -1874,14 +1874,14 @@ async fn kick_off_seconding( gum::debug!( target: LOG_TARGET, candidate_hash = ?blocked_collation.candidate_receipt.hash(), - relay_parent = ?blocked_collation.candidate_receipt.descriptor.relay_parent, + relay_parent = ?blocked_collation.candidate_receipt.descriptor.relay_parent(), "Collation having parent head data hash {} is blocked from seconding. Waiting on its parent to be validated.", parent_head_data_hash ); state .blocked_from_seconding .entry(BlockedCollationId { - para_id: blocked_collation.candidate_receipt.descriptor.para_id, + para_id: blocked_collation.candidate_receipt.descriptor.para_id(), parent_head_data_hash, }) .or_insert_with(Vec::new) @@ -2025,11 +2025,11 @@ async fn handle_collation_fetch_response( Ok( request_v1::CollationFetchingResponse::Collation(receipt, _) | request_v1::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. }, - ) if receipt.descriptor().para_id != pending_collation.para_id => { + ) if receipt.descriptor().para_id() != pending_collation.para_id => { gum::debug!( target: LOG_TARGET, expected_para_id = ?pending_collation.para_id, - got_para_id = ?receipt.descriptor().para_id, + got_para_id = ?receipt.descriptor().para_id(), peer_id = ?pending_collation.peer_id, "Got wrong para ID for requested collation." ); diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs index 86c8bcb6bdcd..290c4db901d5 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs @@ -42,8 +42,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt}; use polkadot_primitives::{ - CandidateReceipt, CollatorPair, CoreIndex, CoreState, GroupIndex, GroupRotationInfo, HeadData, - OccupiedCore, PersistedValidationData, ScheduledCore, ValidatorId, ValidatorIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, + CollatorPair, CoreIndex, GroupIndex, GroupRotationInfo, HeadData, PersistedValidationData, + ScheduledCore, ValidatorId, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ dummy_candidate_descriptor, dummy_candidate_receipt_bad_sig, dummy_hash, @@ -121,7 +122,7 @@ impl Default for TestState { let mut d = dummy_candidate_descriptor(dummy_hash()); d.para_id = chain_ids[1]; - d + d.into() }, }), ]; @@ -341,7 +342,7 @@ async fn assert_candidate_backing_second( incoming_pov, )) => { assert_eq!(expected_relay_parent, relay_parent); - assert_eq!(expected_para_id, candidate_receipt.descriptor.para_id); + assert_eq!(expected_para_id, candidate_receipt.descriptor.para_id()); assert_eq!(*expected_pov, incoming_pov); assert_eq!(pvd, received_pvd); candidate_receipt @@ -591,8 +592,11 @@ fn act_on_advertisement_v2() { response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -793,8 +797,11 @@ fn fetch_one_collation_at_a_time() { candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash(); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -917,16 +924,22 @@ fn fetches_next_collation() { // First request finishes now: response_channel_non_exclusive .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -1055,8 +1068,11 @@ fn fetch_next_collation_on_invalid_collation() { candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash(); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs index dff98e22e3db..e040163cd905 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs @@ -20,8 +20,8 @@ use super::*; use polkadot_node_subsystem::messages::ChainApiMessage; use polkadot_primitives::{ - AsyncBackingParams, BlockNumber, CandidateCommitments, CommittedCandidateReceipt, Header, - SigningContext, ValidatorId, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AsyncBackingParams, + BlockNumber, CandidateCommitments, Header, SigningContext, ValidatorId, }; use rstest::rstest; @@ -225,7 +225,7 @@ async fn send_seconded_statement( overseer_send( virtual_overseer, - CollatorProtocolMessage::Seconded(candidate.descriptor.relay_parent, stmt), + CollatorProtocolMessage::Seconded(candidate.descriptor.relay_parent(), stmt), ) .await; } @@ -374,7 +374,7 @@ fn v1_advertisement_accepted_and_seconded() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); - + let candidate: CandidateReceipt = candidate.into(); let pov = PoV { block_data: BlockData(vec![1]) }; response_channel @@ -595,6 +595,7 @@ fn second_multiple_candidates_per_relay_parent() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = candidate.hash(); let parent_head_data_hash = Hash::zero(); @@ -750,7 +751,7 @@ fn fetched_collation_sanity_check() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); - + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = CandidateHash(Hash::zero()); let parent_head_data_hash = Hash::zero(); @@ -845,7 +846,6 @@ fn sanity_check_invalid_parent_head_data() { let mut candidate = dummy_candidate_receipt_bad_sig(head_c, Some(Default::default())); candidate.descriptor.para_id = test_state.chain_ids[0]; - let commitments = CandidateCommitments { head_data: HeadData(vec![1, 2, 3]), horizontal_messages: Default::default(), @@ -864,6 +864,7 @@ fn sanity_check_invalid_parent_head_data() { pvd.parent_head = parent_head_data; candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = candidate.hash(); @@ -1068,6 +1069,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { processed_downward_messages: 0, hrmp_watermark: 0, }; + let mut candidate_b: CandidateReceipt = candidate_b.into(); candidate_b.commitments_hash = candidate_b_commitments.hash(); let candidate_b_hash = candidate_b.hash(); @@ -1134,6 +1136,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { relay_parent_storage_root: Default::default(), } .hash(); + let mut candidate_a: CandidateReceipt = candidate_a.into(); let candidate_a_commitments = CandidateCommitments { head_data: HeadData(vec![1]), horizontal_messages: Default::default(), @@ -1144,6 +1147,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { }; candidate_a.commitments_hash = candidate_a_commitments.hash(); + let candidate_a: CandidateReceipt = candidate_a.into(); let candidate_a_hash = candidate_a.hash(); advertise_collation( @@ -1208,7 +1212,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { incoming_pov, )) => { assert_eq!(head_c, relay_parent); - assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id); + assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id()); assert_eq!(PoV { block_data: BlockData(vec![2]) }, incoming_pov); assert_eq!(PersistedValidationData:: { parent_head: HeadData(vec![0]), @@ -1261,7 +1265,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { incoming_pov, )) => { assert_eq!(head_c, relay_parent); - assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id); + assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id()); assert_eq!(PoV { block_data: BlockData(vec![1]) }, incoming_pov); assert_eq!(PersistedValidationData:: { parent_head: HeadData(vec![1]), diff --git a/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs b/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs index 11380b7c072e..c911b4bc4ae6 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs @@ -22,7 +22,7 @@ use polkadot_node_network_protocol::{ PeerId, }; use polkadot_node_primitives::SignedDisputeStatement; -use polkadot_primitives::{CandidateReceipt, ValidatorIndex}; +use polkadot_primitives::{vstaging::CandidateReceiptV2 as CandidateReceipt, ValidatorIndex}; use crate::receiver::{BATCH_COLLECTING_INTERVAL, MIN_KEEP_BATCH_ALIVE_VOTES}; diff --git a/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs index 76c7683d1574..13b42aff1f30 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs @@ -22,7 +22,7 @@ use std::{ use futures::future::pending; use polkadot_node_network_protocol::request_response::DISPUTE_REQUEST_TIMEOUT; -use polkadot_primitives::{CandidateHash, CandidateReceipt}; +use polkadot_primitives::{vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash}; use crate::{ receiver::batches::{batch::TickResult, waiting_queue::PendingWake}, diff --git a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs index 77c1e41aac05..b21965fc7004 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs @@ -334,7 +334,7 @@ where .runtime .get_session_info_by_index( &mut self.sender, - payload.0.candidate_receipt.descriptor.relay_parent, + payload.0.candidate_receipt.descriptor.relay_parent(), payload.0.session_index, ) .await?; diff --git a/polkadot/node/network/dispute-distribution/src/sender/send_task.rs b/polkadot/node/network/dispute-distribution/src/sender/send_task.rs index 54ccd10789d0..f607c9431513 100644 --- a/polkadot/node/network/dispute-distribution/src/sender/send_task.rs +++ b/polkadot/node/network/dispute-distribution/src/sender/send_task.rs @@ -234,7 +234,7 @@ impl SendTask { runtime: &mut RuntimeInfo, active_sessions: &HashMap, ) -> Result> { - let ref_head = self.request.0.candidate_receipt.descriptor.relay_parent; + let ref_head = self.request.0.candidate_receipt.descriptor.relay_parent(); // Retrieve all authorities which participated in the parachain consensus of the session // in which the candidate was backed. let info = runtime diff --git a/polkadot/node/network/dispute-distribution/src/tests/mock.rs b/polkadot/node/network/dispute-distribution/src/tests/mock.rs index baa857e2eb68..52659ae9e002 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mock.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mock.rs @@ -33,10 +33,10 @@ use sp_keystore::{Keystore, KeystorePtr}; use polkadot_node_primitives::{DisputeMessage, SignedDisputeStatement}; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CandidateReceipt, Hash, SessionIndex, SessionInfo, - ValidatorId, ValidatorIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, AuthorityDiscoveryId, CandidateHash, Hash, + SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, }; -use polkadot_primitives_test_helpers::dummy_candidate_descriptor; +use polkadot_primitives_test_helpers::dummy_candidate_descriptor_v2; use crate::LOG_TARGET; @@ -116,7 +116,7 @@ pub static MOCK_NEXT_SESSION_INFO: LazyLock = LazyLock::new(|| Sess pub fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { CandidateReceipt { - descriptor: dummy_candidate_descriptor(relay_parent), + descriptor: dummy_candidate_descriptor_v2(relay_parent), commitments_hash: Hash::random(), } } diff --git a/polkadot/node/network/dispute-distribution/src/tests/mod.rs b/polkadot/node/network/dispute-distribution/src/tests/mod.rs index 60820e62ca2d..5306b22828cc 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mod.rs @@ -57,8 +57,8 @@ use polkadot_node_subsystem_test_helpers::{ subsystem_test_harness, TestSubsystemContextHandle, }; use polkadot_primitives::{ - AuthorityDiscoveryId, Block, CandidateHash, CandidateReceipt, ExecutorParams, Hash, - NodeFeatures, SessionIndex, SessionInfo, + vstaging::CandidateReceiptV2 as CandidateReceipt, AuthorityDiscoveryId, Block, CandidateHash, + ExecutorParams, Hash, NodeFeatures, SessionIndex, SessionInfo, }; use self::mock::{ diff --git a/polkadot/node/network/protocol/src/request_response/v1.rs b/polkadot/node/network/protocol/src/request_response/v1.rs index 80721f1884af..4f28d4cbf2d8 100644 --- a/polkadot/node/network/protocol/src/request_response/v1.rs +++ b/polkadot/node/network/protocol/src/request_response/v1.rs @@ -22,8 +22,11 @@ use polkadot_node_primitives::{ AvailableData, DisputeMessage, ErasureChunk, PoV, Proof, UncheckedDisputeMessage, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, - ValidatorIndex, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + CandidateHash, Hash, HeadData, Id as ParaId, ValidatorIndex, }; use super::{IsRequest, Protocol}; diff --git a/polkadot/node/network/protocol/src/request_response/v2.rs b/polkadot/node/network/protocol/src/request_response/v2.rs index ae65b39cd406..834870e5b908 100644 --- a/polkadot/node/network/protocol/src/request_response/v2.rs +++ b/polkadot/node/network/protocol/src/request_response/v2.rs @@ -20,8 +20,8 @@ use codec::{Decode, Encode}; use polkadot_node_primitives::ErasureChunk; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, Hash, Id as ParaId, PersistedValidationData, - UncheckedSignedStatement, ValidatorIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, + Id as ParaId, PersistedValidationData, UncheckedSignedStatement, ValidatorIndex, }; use super::{v1, IsRequest, Protocol}; diff --git a/polkadot/node/network/statement-distribution/Cargo.toml b/polkadot/node/network/statement-distribution/Cargo.toml index 2a9773ddde4b..08059353033d 100644 --- a/polkadot/node/network/statement-distribution/Cargo.toml +++ b/polkadot/node/network/statement-distribution/Cargo.toml @@ -40,6 +40,7 @@ sp-tracing = { workspace = true, default-features = true } sc-keystore = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } futures-timer = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-primitives-test-helpers = { workspace = true } rand_chacha = { workspace = true, default-features = true } polkadot-subsystem-bench = { workspace = true } diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs index 8270b9809194..bd6d4ebe755c 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs @@ -37,9 +37,10 @@ use polkadot_node_subsystem::{ overseer, ActivatedLeaf, StatementDistributionSenderTrait, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash, - Id as ParaId, IndexedVec, OccupiedCoreAssumption, PersistedValidationData, SignedStatement, - SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, ValidatorSignature, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AuthorityDiscoveryId, + CandidateHash, CompactStatement, Hash, Id as ParaId, IndexedVec, OccupiedCoreAssumption, + PersistedValidationData, SignedStatement, SigningContext, UncheckedSignedStatement, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use futures::{ @@ -1641,7 +1642,7 @@ async fn handle_incoming_message<'a, Context>( // In case of `Valid` we should have it cached prior, therefore this performs // no Runtime API calls and always returns `Ok(Some(_))`. let pvd = if let Statement::Seconded(receipt) = statement.payload() { - let para_id = receipt.descriptor.para_id; + let para_id = receipt.descriptor.para_id(); // Either call the Runtime API or check that validation data is cached. let result = active_head .fetch_persisted_validation_data(ctx.sender(), relay_parent, para_id) diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs index c0346adfe101..69bcbac76b70 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs @@ -29,7 +29,9 @@ use polkadot_node_network_protocol::{ PeerId, UnifiedReputationChange, }; use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_primitives::{CandidateHash, CommittedCandidateReceipt, Hash}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, +}; use crate::{ legacy_v1::{COST_WRONG_HASH, LOG_TARGET}, diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs index 8d1683759a03..03e1dc059989 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs @@ -29,7 +29,9 @@ use polkadot_node_network_protocol::{ }, PeerId, UnifiedReputationChange as Rep, }; -use polkadot_primitives::{CandidateHash, CommittedCandidateReceipt, Hash}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, +}; use crate::LOG_TARGET; diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs index 5e00fb96d74f..d2fd016ec2f1 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs @@ -47,7 +47,8 @@ use polkadot_primitives::{ SessionInfo, ValidationCode, }; use polkadot_primitives_test_helpers::{ - dummy_committed_candidate_receipt, dummy_hash, AlwaysZeroRng, + dummy_committed_candidate_receipt, dummy_committed_candidate_receipt_v2, dummy_hash, + AlwaysZeroRng, }; use sc_keystore::LocalKeystore; use sc_network::ProtocolName; @@ -140,7 +141,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note A let a_seconded_val_0 = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_a.clone()), + Statement::Seconded(candidate_a.into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -167,7 +168,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note B let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_b.clone()), + Statement::Seconded(candidate_b.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -184,7 +185,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note C (beyond 2 - ignored) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_c.clone()), + Statement::Seconded(candidate_c.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -202,7 +203,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note B (new validator) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_b.clone()), + Statement::Seconded(candidate_b.into()), &signing_context, ValidatorIndex(1), &bob_public.into(), @@ -219,7 +220,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note C (new validator) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_c.clone()), + Statement::Seconded(candidate_c.into()), &signing_context, ValidatorIndex(1), &bob_public.into(), @@ -470,7 +471,7 @@ fn peer_view_update_sends_messages() { let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -612,7 +613,7 @@ fn circulated_statement_goes_to_all_peers_with_view() { let mut c = dummy_committed_candidate_receipt(dummy_hash()); c.descriptor.relay_parent = hash_b; c.descriptor.para_id = ParaId::from(1_u32); - c + c.into() }; let peer_a = PeerId::random(); @@ -746,7 +747,7 @@ fn receiving_from_one_sends_to_another_and_to_candidate_backing() { let mut c = dummy_committed_candidate_receipt(dummy_hash()); c.descriptor.relay_parent = hash_a; c.descriptor.para_id = PARA_ID; - c + c.into() }; let peer_a = PeerId::random(); @@ -1199,7 +1200,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -1337,7 +1338,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( let bad_candidate = { let mut bad = candidate.clone(); bad.descriptor.para_id = 0xeadbeaf.into(); - bad + bad.into() }; let response = StatementFetchingResponse::Statement(bad_candidate); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); @@ -1391,7 +1392,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( assert_eq!(req.candidate_hash, metadata.candidate_hash); // On retry, we should have reverse order: assert_eq!(outgoing.peer, Recipient::Peer(peer_c)); - let response = StatementFetchingResponse::Statement(candidate.clone()); + let response = StatementFetchingResponse::Statement(candidate.clone().into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -1517,7 +1518,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( req_cfg.inbound_queue.as_mut().unwrap().send(req).await.unwrap(); let StatementFetchingResponse::Statement(committed) = Decode::decode(&mut response_rx.await.unwrap().result.unwrap().as_ref()).unwrap(); - assert_eq!(committed, candidate); + assert_eq!(committed, candidate.into()); handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; @@ -1744,7 +1745,7 @@ fn delay_reputation_changes() { SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -1884,7 +1885,7 @@ fn delay_reputation_changes() { bad.descriptor.para_id = 0xeadbeaf.into(); bad }; - let response = StatementFetchingResponse::Statement(bad_candidate); + let response = StatementFetchingResponse::Statement(bad_candidate.into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -1928,7 +1929,7 @@ fn delay_reputation_changes() { assert_eq!(req.candidate_hash, metadata.candidate_hash); // On retry, we should have reverse order: assert_eq!(outgoing.peer, Recipient::Peer(peer_c)); - let response = StatementFetchingResponse::Statement(candidate.clone()); + let response = StatementFetchingResponse::Statement(candidate.clone().into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -2288,7 +2289,7 @@ fn share_prioritizes_backing_group() { SignedFullStatementWithPVD::sign( &keystore, - Statement::Seconded(candidate.clone()).supply_pvd(pvd), + Statement::Seconded(candidate.clone().into()).supply_pvd(pvd), &signing_context, ValidatorIndex(4), &ferdie_public.into(), @@ -2352,7 +2353,7 @@ fn share_prioritizes_backing_group() { req_cfg.inbound_queue.as_mut().unwrap().send(req).await.unwrap(); let StatementFetchingResponse::Statement(committed) = Decode::decode(&mut response_rx.await.unwrap().result.unwrap().as_ref()).unwrap(); - assert_eq!(committed, candidate); + assert_eq!(committed, candidate.into()); handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; @@ -2514,7 +2515,7 @@ fn peer_cant_flood_with_large_statements() { SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -2595,7 +2596,7 @@ fn handle_multiple_seconded_statements() { let relay_parent_hash = Hash::repeat_byte(1); let pvd = dummy_pvd(); - let candidate = dummy_committed_candidate_receipt(relay_parent_hash); + let candidate = dummy_committed_candidate_receipt_v2(relay_parent_hash); let candidate_hash = candidate.hash(); // We want to ensure that our peers are not lucky diff --git a/polkadot/node/network/statement-distribution/src/v2/candidates.rs b/polkadot/node/network/statement-distribution/src/v2/candidates.rs index a4f2455c2840..1a37d2ea086a 100644 --- a/polkadot/node/network/statement-distribution/src/v2/candidates.rs +++ b/polkadot/node/network/statement-distribution/src/v2/candidates.rs @@ -28,8 +28,8 @@ use polkadot_node_network_protocol::PeerId; use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, GroupIndex, Hash, Id as ParaId, - PersistedValidationData, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, GroupIndex, + Hash, Id as ParaId, PersistedValidationData, }; use std::{ @@ -154,8 +154,8 @@ impl Candidates { assigned_group: GroupIndex, ) -> Option { let parent_hash = persisted_validation_data.parent_head.hash(); - let relay_parent = candidate_receipt.descriptor().relay_parent; - let para_id = candidate_receipt.descriptor().para_id; + let relay_parent = candidate_receipt.descriptor.relay_parent(); + let para_id = candidate_receipt.descriptor.para_id(); let prev_state = self.candidates.insert( candidate_hash, @@ -530,12 +530,12 @@ pub struct ConfirmedCandidate { impl ConfirmedCandidate { /// Get the relay-parent of the candidate. pub fn relay_parent(&self) -> Hash { - self.receipt.descriptor().relay_parent + self.receipt.descriptor.relay_parent() } /// Get the para-id of the candidate. pub fn para_id(&self) -> ParaId { - self.receipt.descriptor().para_id + self.receipt.descriptor.para_id() } /// Get the underlying candidate receipt. diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index f9c2d0ddbae8..c79ae3953ad9 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -50,9 +50,9 @@ use polkadot_node_subsystem_util::{ }, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, CoreState, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, IndexedVec, SessionIndex, SessionInfo, SignedStatement, - SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, + vstaging::CoreState, AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, + GroupIndex, GroupRotationInfo, Hash, Id as ParaId, IndexedVec, SessionIndex, SessionInfo, + SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, }; use sp_keystore::KeystorePtr; @@ -1201,7 +1201,7 @@ pub(crate) async fn share_local_statement( // have the candidate. Sanity: check the para-id is valid. let expected = match statement.payload() { FullStatementWithPVD::Seconded(ref c, _) => - Some((c.descriptor().para_id, c.descriptor().relay_parent)), + Some((c.descriptor.para_id(), c.descriptor.relay_parent())), FullStatementWithPVD::Valid(hash) => state.candidates.get_confirmed(&hash).map(|c| (c.para_id(), c.relay_parent())), }; @@ -2277,13 +2277,13 @@ async fn fragment_chain_update_inner( } = hypo { let confirmed_candidate = state.candidates.get_confirmed(&candidate_hash); - let prs = state.per_relay_parent.get_mut(&receipt.descriptor().relay_parent); + let prs = state.per_relay_parent.get_mut(&receipt.descriptor.relay_parent()); if let (Some(confirmed), Some(prs)) = (confirmed_candidate, prs) { let per_session = state.per_session.get(&prs.session); let group_index = confirmed.group_index(); // Sanity check if group_index is valid for this para at relay parent. - let Some(expected_groups) = prs.groups_per_para.get(&receipt.descriptor().para_id) + let Some(expected_groups) = prs.groups_per_para.get(&receipt.descriptor.para_id()) else { continue }; @@ -2296,7 +2296,7 @@ async fn fragment_chain_update_inner( ctx, candidate_hash, confirmed.group_index(), - &receipt.descriptor().relay_parent, + &receipt.descriptor.relay_parent(), prs, confirmed, per_session, @@ -2888,7 +2888,7 @@ pub(crate) async fn handle_backed_candidate_message( ctx, state, confirmed.para_id(), - confirmed.candidate_receipt().descriptor().para_head, + confirmed.candidate_receipt().descriptor.para_head(), ) .await; } diff --git a/polkadot/node/network/statement-distribution/src/v2/requests.rs b/polkadot/node/network/statement-distribution/src/v2/requests.rs index b8ed34d26c8a..74f29710956f 100644 --- a/polkadot/node/network/statement-distribution/src/v2/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/requests.rs @@ -47,9 +47,9 @@ use polkadot_node_network_protocol::{ PeerId, UnifiedReputationChange as Rep, }; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement, GroupIndex, Hash, Id as ParaId, - PersistedValidationData, SessionIndex, SignedStatement, SigningContext, ValidatorId, - ValidatorIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, + CompactStatement, GroupIndex, Hash, Id as ParaId, PersistedValidationData, SessionIndex, + SignedStatement, SigningContext, ValidatorId, ValidatorIndex, }; use futures::{future::BoxFuture, prelude::*, stream::FuturesUnordered}; @@ -696,18 +696,18 @@ fn validate_complete_response( // sanity-check candidate response. // note: roughly ascending cost of operations { - if response.candidate_receipt.descriptor.relay_parent != identifier.relay_parent { + if response.candidate_receipt.descriptor.relay_parent() != identifier.relay_parent { return invalid_candidate_output() } - if response.candidate_receipt.descriptor.persisted_validation_data_hash != + if response.candidate_receipt.descriptor.persisted_validation_data_hash() != response.persisted_validation_data.hash() { return invalid_candidate_output() } if !allowed_para_lookup( - response.candidate_receipt.descriptor.para_id, + response.candidate_receipt.descriptor.para_id(), identifier.group_index, ) { return invalid_candidate_output() @@ -1019,6 +1019,7 @@ mod tests { candidate_receipt.descriptor.persisted_validation_data_hash = persisted_validation_data.hash(); let candidate = candidate_receipt.hash(); + let candidate_receipt: CommittedCandidateReceipt = candidate_receipt.into(); let requested_peer_1 = PeerId::random(); let requested_peer_2 = PeerId::random(); @@ -1074,7 +1075,7 @@ mod tests { requested_peer: requested_peer_1, props: request_properties.clone(), response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1114,7 +1115,7 @@ mod tests { requested_peer: requested_peer_2, props: request_properties, response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1197,7 +1198,7 @@ mod tests { requested_peer, props: request_properties, response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1236,6 +1237,7 @@ mod tests { candidate_receipt.descriptor.persisted_validation_data_hash = persisted_validation_data.hash(); let candidate = candidate_receipt.hash(); + let candidate_receipt: CommittedCandidateReceipt = candidate_receipt.into(); let requested_peer = PeerId::random(); let identifier = request_manager @@ -1417,7 +1419,7 @@ mod tests { requested_peer: requested_peer_1, props: request_properties.clone(), response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt_1.clone(), + candidate_receipt: candidate_receipt_1.clone().into(), persisted_validation_data: persisted_validation_data_1.clone(), statements, }), diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs index 119dc832d13a..9f2c36ad1018 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs @@ -33,9 +33,9 @@ use polkadot_node_subsystem::messages::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - AssignmentPair, AsyncBackingParams, Block, BlockNumber, CommittedCandidateReceipt, CoreState, - GroupRotationInfo, HeadData, Header, IndexedVec, PersistedValidationData, ScheduledCore, - SessionIndex, SessionInfo, ValidatorPair, + vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState}, + AssignmentPair, AsyncBackingParams, Block, BlockNumber, GroupRotationInfo, HeadData, Header, + IndexedVec, PersistedValidationData, ScheduledCore, SessionIndex, SessionInfo, ValidatorPair, }; use sc_keystore::LocalKeystore; use sc_network::ProtocolName; diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs index 807e7405ff1b..e1b2af733b47 100644 --- a/polkadot/node/overseer/examples/minimal-example.rs +++ b/polkadot/node/overseer/examples/minimal-example.rs @@ -31,7 +31,9 @@ use polkadot_overseer::{ gen::{FromOrchestra, SpawnedSubsystem}, HeadSupportsParachains, SubsystemError, }; -use polkadot_primitives::{CandidateReceipt, Hash, PersistedValidationData}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, Hash, PersistedValidationData, +}; use polkadot_primitives_test_helpers::{ dummy_candidate_descriptor, dummy_hash, dummy_validation_code, }; @@ -71,7 +73,7 @@ impl Subsystem1 { let (tx, _) = oneshot::channel(); let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: Hash::zero(), }; diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs index 46864a482e2a..c3c47335cd3e 100644 --- a/polkadot/node/overseer/src/tests.rs +++ b/polkadot/node/overseer/src/tests.rs @@ -28,11 +28,12 @@ use polkadot_node_subsystem_types::messages::{ NetworkBridgeEvent, PvfExecKind, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CollatorPair, Id as ParaId, InvalidDisputeStatementKind, - PersistedValidationData, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, + vstaging::CandidateReceiptV2, CandidateHash, CollatorPair, Id as ParaId, + InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, ValidDisputeStatementKind, + ValidatorIndex, }; use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor, dummy_candidate_receipt, dummy_hash, dummy_validation_code, + dummy_candidate_descriptor, dummy_candidate_receipt_v2, dummy_hash, dummy_validation_code, }; use crate::{ @@ -98,8 +99,8 @@ where let mut c: usize = 0; loop { if c < 10 { - let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + let candidate_receipt = CandidateReceiptV2 { + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: dummy_hash(), }; @@ -799,8 +800,8 @@ where fn test_candidate_validation_msg() -> CandidateValidationMessage { let (response_sender, _) = oneshot::channel(); let pov = Arc::new(PoV { block_data: BlockData(Vec::new()) }); - let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + let candidate_receipt = CandidateReceiptV2 { + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: Hash::zero(), }; @@ -859,7 +860,7 @@ fn test_statement_distribution_msg() -> StatementDistributionMessage { fn test_availability_recovery_msg() -> AvailabilityRecoveryMessage { let (sender, _) = oneshot::channel(); AvailabilityRecoveryMessage::RecoverAvailableData( - dummy_candidate_receipt(dummy_hash()), + dummy_candidate_receipt_v2(dummy_hash()), Default::default(), None, None, @@ -918,7 +919,7 @@ fn test_dispute_coordinator_msg() -> DisputeCoordinatorMessage { fn test_dispute_distribution_msg() -> DisputeDistributionMessage { let dummy_dispute_message = UncheckedDisputeMessage { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), session_index: 0, invalid_vote: InvalidDisputeVote { validator_index: ValidatorIndex(0), diff --git a/polkadot/node/primitives/src/disputes/message.rs b/polkadot/node/primitives/src/disputes/message.rs index f9dec073bf50..d32ed4dadb6e 100644 --- a/polkadot/node/primitives/src/disputes/message.rs +++ b/polkadot/node/primitives/src/disputes/message.rs @@ -25,7 +25,8 @@ use codec::{Decode, Encode}; use super::{InvalidDisputeVote, SignedDisputeStatement, ValidDisputeVote}; use polkadot_primitives::{ - CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo, ValidatorIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo, + ValidatorIndex, }; /// A dispute initiating/participating message that have been built from signed diff --git a/polkadot/node/primitives/src/disputes/mod.rs b/polkadot/node/primitives/src/disputes/mod.rs index 0f08b4733654..71e2f0b16be3 100644 --- a/polkadot/node/primitives/src/disputes/mod.rs +++ b/polkadot/node/primitives/src/disputes/mod.rs @@ -25,9 +25,9 @@ use sp_application_crypto::AppCrypto; use sp_keystore::{Error as KeystoreError, KeystorePtr}; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CompactStatement, DisputeStatement, EncodeAs, - InvalidDisputeStatementKind, SessionIndex, SigningContext, UncheckedSigned, - ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CompactStatement, + DisputeStatement, EncodeAs, InvalidDisputeStatementKind, SessionIndex, SigningContext, + UncheckedSigned, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, }; /// `DisputeMessage` and related types. diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index a525b2bc9776..e2e7aa92b11c 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -30,10 +30,10 @@ use futures::Future; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use polkadot_primitives::{ - BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, ChunkIndex, CollatorPair, - CommittedCandidateReceipt, CompactStatement, CoreIndex, EncodeAs, Hash, HashT, HeadData, - Id as ParaId, PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode, - ValidationCodeHash, MAX_CODE_SIZE, MAX_POV_SIZE, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, BlakeTwo256, BlockNumber, + CandidateCommitments, CandidateHash, ChunkIndex, CollatorPair, CompactStatement, CoreIndex, + EncodeAs, Hash, HashT, HeadData, Id as ParaId, PersistedValidationData, SessionIndex, Signed, + UncheckedSigned, ValidationCode, ValidationCodeHash, MAX_CODE_SIZE, MAX_POV_SIZE, }; pub use sp_consensus_babe::{ AllowedSlots as BabeAllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch, diff --git a/polkadot/node/service/src/parachains_db/upgrade.rs b/polkadot/node/service/src/parachains_db/upgrade.rs index 808acf04b4e7..52b010f0b5d0 100644 --- a/polkadot/node/service/src/parachains_db/upgrade.rs +++ b/polkadot/node/service/src/parachains_db/upgrade.rs @@ -463,7 +463,7 @@ mod tests { v3::migration_helpers::{v1_to_latest_sanity_check, v2_fill_test_data}, }; use polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter; - use polkadot_primitives_test_helpers::dummy_candidate_receipt; + use polkadot_primitives_test_helpers::dummy_candidate_receipt_v2; #[test] fn test_paritydb_migrate_0_to_1() { @@ -617,7 +617,7 @@ mod tests { assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32); let db = DbAdapter::new(db, columns::v3::ORDERED_COL); // Fill the approval voting column with test data. - v1_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt) + v1_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2) .unwrap() }; @@ -648,7 +648,7 @@ mod tests { assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32); let db = DbAdapter::new(db, columns::v3::ORDERED_COL); // Fill the approval voting column with test data. - v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt) + v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2) .unwrap() }; diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 293df9f6e6d5..8633818e775d 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -28,7 +28,7 @@ polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-node-network-protocol = { workspace = true, default-features = true } polkadot-availability-recovery = { features = ["subsystem-benchmarks"], workspace = true, default-features = true } polkadot-availability-distribution = { workspace = true, default-features = true } diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index a3a475ac6b98..24cd734c6ae5 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -29,8 +29,8 @@ use polkadot_node_subsystem_types::messages::{ }; use polkadot_overseer::AllMessages; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateReceipt, CoreIndex, GroupIndex, Hash, Header, - Id as ParaId, Slot, ValidatorIndex, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CoreIndex, GroupIndex, Hash, Header, Id as ParaId, Slot, ValidatorIndex, }; use polkadot_primitives_test_helpers::dummy_candidate_receipt_bad_sig; use rand::{seq::SliceRandom, SeedableRng}; @@ -189,7 +189,7 @@ pub fn make_header(parent_hash: Hash, slot: Slot, number: u32) -> Header { fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { let mut r = dummy_candidate_receipt_bad_sig(*hash, Some(Default::default())); r.descriptor.para_id = para_id; - r + r.into() } /// Helper function to create a list of candidates that are included in the block diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index 807afb0438c9..79de6e72fc88 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -40,8 +40,8 @@ use polkadot_node_primitives::approval::{ v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_primitives::{ - ApprovalVoteMultipleCandidates, CandidateEvent, CandidateHash, CandidateIndex, CoreIndex, Hash, - SessionInfo, Slot, ValidatorId, ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, + vstaging::CandidateEvent, ApprovalVoteMultipleCandidates, CandidateHash, CandidateIndex, + CoreIndex, Hash, SessionInfo, Slot, ValidatorId, ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, }; use rand::{seq::SliceRandom, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 29ebc4a419ae..1b20960a3f8a 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -66,8 +66,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; use polkadot_overseer::Handle as OverseerHandleReal; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateIndex, CandidateReceipt, Hash, Header, Slot, ValidatorId, - ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateIndex, Hash, Header, Slot, ValidatorId, ValidatorIndex, + ASSIGNMENT_KEY_TYPE_ID, }; use prometheus::Registry; use sc_keystore::LocalKeystore; diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs index a99f013195fa..8ac9796acb62 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -391,7 +391,7 @@ pub async fn benchmark_availability_write( candidate_hash: backed_candidate.hash(), n_validators: config.n_validators as u32, available_data, - expected_erasure_root: backed_candidate.descriptor().erasure_root, + expected_erasure_root: backed_candidate.descriptor().erasure_root(), tx, core_index: CoreIndex(core_index as u32), node_features: node_features_with_chunk_mapping_enabled(), diff --git a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs index 173b23f6b76e..511795970e6c 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs @@ -34,8 +34,9 @@ use polkadot_node_subsystem_test_helpers::{ use polkadot_node_subsystem_util::availability_chunks::availability_chunk_indices; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, - Hash, HeadData, Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + AvailabilityBitfield, BlockNumber, CandidateHash, ChunkIndex, CoreIndex, Hash, HeadData, + Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, }; use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; use sp_core::H256; @@ -148,7 +149,10 @@ impl TestState { test_state.chunks.push(new_chunks); test_state.available_data.push(new_available_data); test_state.pov_size_to_candidate.insert(pov_size, index); - test_state.candidate_receipt_templates.push(candidate_receipt); + test_state.candidate_receipt_templates.push(CandidateReceipt { + descriptor: candidate_receipt.descriptor.into(), + commitments_hash: candidate_receipt.commitments_hash, + }); } test_state.block_infos = (1..=config.num_blocks) @@ -189,7 +193,9 @@ impl TestState { test_state.candidate_receipt_templates[candidate_index].clone(); // Make it unique. - candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + candidate_receipt + .descriptor + .set_relay_parent(Hash::from_low_u64_be(index as u64)); // Store the new candidate in the state test_state.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index 61523de1f1b5..6c54d14448a1 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -26,9 +26,10 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ - node_features, ApprovalVotingParams, AsyncBackingParams, CandidateEvent, CandidateReceipt, - CoreState, GroupIndex, GroupRotationInfo, IndexedVec, NodeFeatures, OccupiedCore, - ScheduledCore, SessionIndex, SessionInfo, ValidationCode, ValidatorIndex, + node_features, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, + ApprovalVotingParams, AsyncBackingParams, GroupIndex, GroupRotationInfo, IndexedVec, + NodeFeatures, ScheduledCore, SessionIndex, SessionInfo, ValidationCode, ValidatorIndex, }; use sp_consensus_babe::Epoch as BabeEpoch; use sp_core::H256; diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 88b5e8b76b62..2d2e9434b767 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -41,12 +41,15 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, Header, Id, PersistedValidationData, SessionInfo, SignedStatement, SigningContext, - UncheckedSigned, ValidatorIndex, ValidatorPair, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2, + }, + BlockNumber, CandidateHash, CompactStatement, Hash, Header, Id, PersistedValidationData, + SessionInfo, SignedStatement, SigningContext, UncheckedSigned, ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{ - dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, + dummy_committed_candidate_receipt_v2, dummy_hash, dummy_head_data, dummy_pvd, }; use sc_network::{config::IncomingRequest, ProtocolName}; use sp_core::{Pair, H256}; @@ -125,8 +128,8 @@ impl TestState { let candidate_index = *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); let mut receipt = receipt_templates[candidate_index].clone(); - receipt.descriptor.para_id = Id::new(core_idx as u32 + 1); - receipt.descriptor.relay_parent = block_info.hash; + receipt.descriptor.set_para_id(Id::new(core_idx as u32 + 1)); + receipt.descriptor.set_relay_parent(block_info.hash); state.candidate_receipts.entry(block_info.hash).or_default().push( CandidateReceipt { @@ -240,7 +243,7 @@ fn generate_receipt_templates( pov_size_to_candidate .iter() .map(|(&pov_size, &index)| { - let mut receipt = dummy_committed_candidate_receipt(dummy_hash()); + let mut receipt = dummy_committed_candidate_receipt_v2(dummy_hash()); let (_, erasure_root) = derive_erasure_chunks_with_proofs_and_root( n_validators, &AvailableData { @@ -249,8 +252,8 @@ fn generate_receipt_templates( }, |_, _| {}, ); - receipt.descriptor.persisted_validation_data_hash = pvd.hash(); - receipt.descriptor.erasure_root = erasure_root; + receipt.descriptor.set_persisted_validation_data_hash(pvd.hash()); + receipt.descriptor.set_erasure_root(erasure_root); receipt }) .collect() diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 0017adb45568..ba1ba5755be0 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -43,15 +43,18 @@ use polkadot_node_primitives::{ ValidationResult, }; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, - BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CandidateIndex, - CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, - ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, Header as BlockHeader, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, - NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - PvfExecKind as RuntimePvfExecKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield, - SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + async_backing, slashing, vstaging, + vstaging::{ + BackedCandidate, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CandidateIndex, CollatorId, CoreIndex, DisputeState, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, HeadData, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, MultiDisputeStatementSet, NodeFeatures, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, PvfExecKind as RuntimePvfExecKind, SessionIndex, + SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -708,7 +711,7 @@ pub enum RuntimeApiRequest { CandidatePendingAvailability(ParaId, RuntimeApiSender>), /// Get all events concerning candidates (backing, inclusion, time-out) in the parent of /// the block in whose state this request is executed. - CandidateEvents(RuntimeApiSender>), + CandidateEvents(RuntimeApiSender>), /// Get the execution environment parameter set by session index SessionExecutorParams(SessionIndex, RuntimeApiSender>), /// Get the session info for the given session, if stored. @@ -724,7 +727,7 @@ pub enum RuntimeApiRequest { /// Get information about the BABE epoch the block was included in. CurrentBabeEpoch(RuntimeApiSender), /// Get all disputes in relation to a relay parent. - FetchOnChainVotes(RuntimeApiSender>), + FetchOnChainVotes(RuntimeApiSender>), /// Submits a PVF pre-checking statement into the transaction pool. SubmitPvfCheckStatement(PvfCheckStatement, ValidatorSignature, RuntimeApiSender<()>), /// Returns code hashes of PVFs that require pre-checking by validators in the active set. @@ -759,7 +762,7 @@ pub enum RuntimeApiRequest { /// Returns all disabled validators at a given block height. DisabledValidators(RuntimeApiSender>), /// Get the backing state of the given para. - ParaBackingState(ParaId, RuntimeApiSender>), + ParaBackingState(ParaId, RuntimeApiSender>), /// Get candidate's acceptance limitations for asynchronous backing for a relay parent. /// /// If it's not supported by the Runtime, the async backing is said to be disabled. @@ -1256,7 +1259,7 @@ impl HypotheticalCandidate { /// Get the `ParaId` of the hypothetical candidate. pub fn candidate_para(&self) -> ParaId { match *self { - HypotheticalCandidate::Complete { ref receipt, .. } => receipt.descriptor().para_id, + HypotheticalCandidate::Complete { ref receipt, .. } => receipt.descriptor.para_id(), HypotheticalCandidate::Incomplete { candidate_para, .. } => candidate_para, } } @@ -1275,7 +1278,7 @@ impl HypotheticalCandidate { pub fn relay_parent(&self) -> Hash { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - receipt.descriptor().relay_parent, + receipt.descriptor.relay_parent(), HypotheticalCandidate::Incomplete { candidate_relay_parent, .. } => candidate_relay_parent, } @@ -1285,7 +1288,7 @@ impl HypotheticalCandidate { pub fn output_head_data_hash(&self) -> Option { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - Some(receipt.descriptor.para_head), + Some(receipt.descriptor.para_head()), HypotheticalCandidate::Incomplete { .. } => None, } } @@ -1308,10 +1311,10 @@ impl HypotheticalCandidate { } /// Get the validation code hash, if the candidate is complete. - pub fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + pub fn validation_code_hash(&self) -> Option { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - Some(&receipt.descriptor.validation_code_hash), + Some(receipt.descriptor.validation_code_hash()), HypotheticalCandidate::Incomplete { .. } => None, } } diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index a8af8b7996f9..4b96009f44bf 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -16,12 +16,18 @@ use async_trait::async_trait; use polkadot_primitives::{ - async_backing, runtime_api::ParachainHost, slashing, ApprovalVotingParams, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Header, Id, - InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + async_backing, + runtime_api::ParachainHost, + slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, Block, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Header, Id, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sc_client_api::{AuxStore, HeaderBackend}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; @@ -311,7 +317,7 @@ pub trait RuntimeApiSubsystemClient { &self, at: Hash, para_id: Id, - ) -> Result, ApiError>; + ) -> Result, ApiError>; // === v8 === @@ -380,10 +386,7 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client - .runtime_api() - .availability_cores(at) - .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) + self.client.runtime_api().availability_cores(at) } async fn persisted_validation_data( @@ -436,10 +439,7 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client - .runtime_api() - .candidate_pending_availability(at, para_id) - .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) + self.client.runtime_api().candidate_pending_availability(at, para_id) } async fn candidates_pending_availability( @@ -447,19 +447,11 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client - .runtime_api() - .candidates_pending_availability(at, para_id) - .map(|candidates| { - candidates.into_iter().map(|candidate| candidate.into()).collect::>() - }) + self.client.runtime_api().candidates_pending_availability(at, para_id) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client - .runtime_api() - .candidate_events(at) - .map(|events| events.into_iter().map(|event| event.into()).collect::>()) + self.client.runtime_api().candidate_events(at) } async fn dmq_contents( @@ -490,10 +482,7 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client - .runtime_api() - .on_chain_votes(at) - .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) + self.client.runtime_api().on_chain_votes(at) } async fn session_executor_params( @@ -604,13 +593,8 @@ where &self, at: Hash, para_id: Id, - ) -> Result, ApiError> { - self.client - .runtime_api() - .para_backing_state(at, para_id) - .map(|maybe_backing_state| { - maybe_backing_state.map(|backing_state| backing_state.into()) - }) + ) -> Result, ApiError> { + self.client.runtime_api().para_backing_state(at, para_id) } async fn async_backing_params( diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index 0c3b40743495..a2a6095b765c 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -770,7 +770,7 @@ pub trait HypotheticalOrConcreteCandidate { /// Return a reference to the persisted validation data, if present. fn persisted_validation_data(&self) -> Option<&PersistedValidationData>; /// Return a reference to the validation code hash, if present. - fn validation_code_hash(&self) -> Option<&ValidationCodeHash>; + fn validation_code_hash(&self) -> Option; /// Return the parent head hash. fn parent_head_data_hash(&self) -> Hash; /// Return the output head hash, if present. @@ -790,7 +790,7 @@ impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { self.persisted_validation_data() } - fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + fn validation_code_hash(&self) -> Option { self.validation_code_hash() } diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 4bab4e80fe50..3bed18558941 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -41,12 +41,15 @@ use codec::Encode; use futures::channel::{mpsc, oneshot}; use polkadot_primitives::{ - async_backing::BackingState, slashing, AsyncBackingParams, AuthorityDiscoveryId, - CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, EncodeAs, - ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, - SigningContext, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + slashing, + vstaging::{ + async_backing::BackingState, CandidateEvent, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, ScrapedOnChainVotes, + }, + AsyncBackingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, EncodeAs, ExecutorParams, + GroupIndex, GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption, + PersistedValidationData, SessionIndex, SessionInfo, Signed, SigningContext, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; pub use rand; use runtime::get_disabled_validators_with_fallback; diff --git a/polkadot/node/subsystem-util/src/runtime/mod.rs b/polkadot/node/subsystem-util/src/runtime/mod.rs index 2f9d3ed7b4f4..d84951ae1366 100644 --- a/polkadot/node/subsystem-util/src/runtime/mod.rs +++ b/polkadot/node/subsystem-util/src/runtime/mod.rs @@ -30,11 +30,13 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::UnpinHandle; use polkadot_primitives::{ - node_features::FeatureIndex, slashing, AsyncBackingParams, CandidateEvent, CandidateHash, - CoreIndex, CoreState, EncodeAs, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, - Id as ParaId, IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, - SessionInfo, Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, LEGACY_MIN_BACKING_VOTES, + node_features::FeatureIndex, + slashing, + vstaging::{CandidateEvent, CoreState, OccupiedCore, ScrapedOnChainVotes}, + AsyncBackingParams, CandidateHash, CoreIndex, EncodeAs, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, SessionIndex, SessionInfo, + Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, LEGACY_MIN_BACKING_VOTES, }; use std::collections::{BTreeMap, VecDeque}; diff --git a/polkadot/parachain/test-parachains/adder/collator/src/lib.rs b/polkadot/parachain/test-parachains/adder/collator/src/lib.rs index daeb8bc915dd..a2fb623331a0 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/lib.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/lib.rs @@ -236,7 +236,7 @@ impl Collator { if let Ok(res) = recv.await { if !matches!( res.statement.payload(), - Statement::Seconded(s) if s.descriptor.pov_hash == compressed_pov.hash(), + Statement::Seconded(s) if s.descriptor.pov_hash() == compressed_pov.hash(), ) { log::error!( "Seconded statement should match our collation: {:?}", diff --git a/polkadot/parachain/test-parachains/undying/collator/src/lib.rs b/polkadot/parachain/test-parachains/undying/collator/src/lib.rs index 920099f4499d..448c181ae062 100644 --- a/polkadot/parachain/test-parachains/undying/collator/src/lib.rs +++ b/polkadot/parachain/test-parachains/undying/collator/src/lib.rs @@ -282,7 +282,7 @@ impl Collator { if let Ok(res) = recv.await { if !matches!( res.statement.payload(), - Statement::Seconded(s) if s.descriptor.pov_hash == compressed_pov.hash(), + Statement::Seconded(s) if s.descriptor.pov_hash() == compressed_pov.hash(), ) { log::error!( "Seconded statement should match our collation: {:?}", diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index cca327df42c9..fdcb9fe8fb7e 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -484,7 +484,7 @@ pub fn collator_signature_payload>( payload } -fn check_collator_signature>( +pub(crate) fn check_collator_signature>( relay_parent: &H, para_id: &Id, persisted_validation_data_hash: &Hash, diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index bc687f7e2fbe..265fcd899d74 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -107,14 +107,42 @@ impl From> for CandidateDescriptor { } } -#[cfg(any(feature = "runtime-benchmarks", feature = "test"))] -impl From> for CandidateDescriptorV2 { +fn clone_into_array(slice: &[T]) -> A +where + A: Default + AsMut<[T]>, + T: Clone, +{ + let mut a = A::default(); + >::as_mut(&mut a).clone_from_slice(slice); + a +} + +impl From> for CandidateDescriptorV2 { fn from(value: CandidateDescriptor) -> Self { - Decode::decode(&mut value.encode().as_slice()).unwrap() + let collator = value.collator.as_slice(); + + Self { + para_id: value.para_id, + relay_parent: value.relay_parent, + // Use first byte of the `collator` field. + version: InternalVersion(collator[0]), + // Use next 2 bytes of the `collator` field. + core_index: u16::from_ne_bytes(clone_into_array(&collator[1..=2])), + // Use next 4 bytes of the `collator` field. + session_index: SessionIndex::from_ne_bytes(clone_into_array(&collator[3..=6])), + // Use remaing 25 bytes of the `collator` field. + reserved1: clone_into_array(&collator[7..]), + persisted_validation_data_hash: value.persisted_validation_data_hash, + pov_hash: value.pov_hash, + erasure_root: value.erasure_root, + reserved2: value.signature.into_inner().0, + para_head: value.para_head, + validation_code_hash: value.validation_code_hash, + } } } -impl CandidateDescriptorV2 { +impl> CandidateDescriptorV2 { /// Constructor pub fn new( para_id: Id, @@ -143,17 +171,74 @@ impl CandidateDescriptorV2 { } } - /// Set the PoV size in the descriptor. Only for tests. - #[cfg(feature = "test")] - pub fn set_pov_hash(&mut self, pov_hash: Hash) { + /// Check the signature of the collator within this descriptor. + pub fn check_collator_signature(&self) -> Result<(), ()> { + // Return `Ok` if collator signature is not included (v2+ descriptor). + let Some(collator) = self.collator() else { return Ok(()) }; + + let Some(signature) = self.signature() else { return Ok(()) }; + + super::v8::check_collator_signature( + &self.relay_parent, + &self.para_id, + &self.persisted_validation_data_hash, + &self.pov_hash, + &self.validation_code_hash, + &collator, + &signature, + ) + } +} + +/// A trait to allow changing the descriptor field values in tests. +#[cfg(feature = "test")] + +pub trait MutateDescriptorV2 { + /// Set the relay parent of the descriptor. + fn set_relay_parent(&mut self, relay_parent: H); + /// Set the `ParaId` of the descriptor. + fn set_para_id(&mut self, para_id: Id); + /// Set the PoV hash of the descriptor. + fn set_pov_hash(&mut self, pov_hash: Hash); + /// Set the version field of the descriptor. + fn set_version(&mut self, version: InternalVersion); + /// Set the PVD of the descriptor. + fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash); + /// Set the erasure root of the descriptor. + fn set_erasure_root(&mut self, erasure_root: Hash); + /// Set the para head of the descriptor. + fn set_para_head(&mut self, para_head: Hash); +} + +#[cfg(feature = "test")] +impl MutateDescriptorV2 for CandidateDescriptorV2 { + fn set_para_id(&mut self, para_id: Id) { + self.para_id = para_id; + } + + fn set_relay_parent(&mut self, relay_parent: H) { + self.relay_parent = relay_parent; + } + + fn set_pov_hash(&mut self, pov_hash: Hash) { self.pov_hash = pov_hash; } - /// Set the version in the descriptor. Only for tests. - #[cfg(feature = "test")] - pub fn set_version(&mut self, version: InternalVersion) { + fn set_version(&mut self, version: InternalVersion) { self.version = version; } + + fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) { + self.persisted_validation_data_hash = persisted_validation_data_hash; + } + + fn set_erasure_root(&mut self, erasure_root: Hash) { + self.erasure_root = erasure_root; + } + + fn set_para_head(&mut self, para_head: Hash) { + self.para_head = para_head; + } } /// A candidate-receipt at version 2. @@ -233,6 +318,24 @@ impl CandidateReceiptV2 { } } +impl From> for CandidateReceiptV2 { + fn from(value: super::v8::CandidateReceipt) -> Self { + CandidateReceiptV2 { + descriptor: value.descriptor.into(), + commitments_hash: value.commitments_hash, + } + } +} + +impl From> for CommittedCandidateReceiptV2 { + fn from(value: super::v8::CommittedCandidateReceipt) -> Self { + CommittedCandidateReceiptV2 { + descriptor: value.descriptor.into(), + commitments: value.commitments, + } + } +} + impl CommittedCandidateReceiptV2 { /// Transforms this into a plain `CandidateReceipt`. pub fn to_plain(&self) -> CandidateReceiptV2 { @@ -368,7 +471,7 @@ pub enum CandidateReceiptError { macro_rules! impl_getter { ($field:ident, $type:ident) => { - /// Returns the value of $field field. + /// Returns the value of `$field` field. pub fn $field(&self) -> $type { self.$field } @@ -703,6 +806,13 @@ pub struct OccupiedCore { pub candidate_descriptor: CandidateDescriptorV2, } +impl OccupiedCore { + /// Get the Para currently occupying this core. + pub fn para_id(&self) -> Id { + self.candidate_descriptor.para_id + } +} + /// The state of a particular availability core. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] @@ -724,6 +834,28 @@ pub enum CoreState { Free, } +impl CoreState { + /// Returns the scheduled `ParaId` for the core or `None` if nothing is scheduled. + /// + /// This function is deprecated. `ClaimQueue` should be used to obtain the scheduled `ParaId`s + /// for each core. + #[deprecated( + note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead." + )] + pub fn para_id(&self) -> Option { + match self { + Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id), + Self::Scheduled(core) => Some(core.para_id), + Self::Free => None, + } + } + + /// Is this core state `Self::Occupied`? + pub fn is_occupied(&self) -> bool { + matches!(self, Self::Occupied(_)) + } +} + impl From> for super::v8::OccupiedCore { fn from(value: OccupiedCore) -> Self { Self { @@ -841,6 +973,25 @@ mod tests { assert_eq!(old_ccr.hash(), new_ccr.hash()); } + #[test] + fn test_from_v1_descriptor() { + let mut old_ccr = dummy_old_committed_candidate_receipt().to_plain(); + old_ccr.descriptor.collator = dummy_collator_id(); + old_ccr.descriptor.signature = dummy_collator_signature(); + + let mut new_ccr = dummy_committed_candidate_receipt_v2().to_plain(); + + // Override descriptor from old candidate receipt. + new_ccr.descriptor = old_ccr.descriptor.clone().into(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V1); + assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap()); + assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap()); + } + #[test] fn invalid_version_descriptor() { let mut new_ccr = dummy_committed_candidate_receipt_v2(); diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index b0f78717dd97..c2eccafef788 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -44,7 +44,7 @@ pub fn dummy_candidate_receipt>(relay_parent: H) -> CandidateRece } /// Creates a v2 candidate receipt with filler data. -pub fn dummy_candidate_receipt_v2>(relay_parent: H) -> CandidateReceiptV2 { +pub fn dummy_candidate_receipt_v2 + Copy>(relay_parent: H) -> CandidateReceiptV2 { CandidateReceiptV2:: { commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(), descriptor: dummy_candidate_descriptor_v2(relay_parent), @@ -62,7 +62,7 @@ pub fn dummy_committed_candidate_receipt>( } /// Creates a v2 committed candidate receipt with filler data. -pub fn dummy_committed_candidate_receipt_v2>( +pub fn dummy_committed_candidate_receipt_v2 + Copy>( relay_parent: H, ) -> CommittedCandidateReceiptV2 { CommittedCandidateReceiptV2 { @@ -88,6 +88,23 @@ pub fn dummy_candidate_receipt_bad_sig( } } +/// Create a candidate receipt with a bogus signature and filler data. Optionally set the commitment +/// hash with the `commitments` arg. +pub fn dummy_candidate_receipt_v2_bad_sig( + relay_parent: Hash, + commitments: impl Into>, +) -> CandidateReceiptV2 { + let commitments_hash = if let Some(commitments) = commitments.into() { + commitments + } else { + dummy_candidate_commitments(dummy_head_data()).hash() + }; + CandidateReceiptV2:: { + commitments_hash, + descriptor: dummy_candidate_descriptor_bad_sig(relay_parent).into(), + } +} + /// Create candidate commitments with filler data. pub fn dummy_candidate_commitments(head_data: impl Into>) -> CandidateCommitments { CandidateCommitments { @@ -144,7 +161,9 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD } /// Create a v2 candidate descriptor with filler data. -pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { +pub fn dummy_candidate_descriptor_v2 + Copy>( + relay_parent: H, +) -> CandidateDescriptorV2 { let invalid = Hash::zero(); let descriptor = make_valid_candidate_descriptor_v2( 1.into(), @@ -208,7 +227,7 @@ pub fn make_candidate( parent_head: HeadData, head_data: HeadData, validation_code_hash: ValidationCodeHash, -) -> (CommittedCandidateReceipt, PersistedValidationData) { +) -> (CommittedCandidateReceiptV2, PersistedValidationData) { let pvd = dummy_pvd(parent_head, relay_parent_number); let commitments = CandidateCommitments { head_data, @@ -225,7 +244,8 @@ pub fn make_candidate( candidate.descriptor.para_id = para_id; candidate.descriptor.persisted_validation_data_hash = pvd.hash(); candidate.descriptor.validation_code_hash = validation_code_hash; - let candidate = CommittedCandidateReceipt { descriptor: candidate.descriptor, commitments }; + let candidate = + CommittedCandidateReceiptV2 { descriptor: candidate.descriptor.into(), commitments }; (candidate, pvd) } @@ -269,7 +289,7 @@ pub fn make_valid_candidate_descriptor>( } /// Create a v2 candidate descriptor. -pub fn make_valid_candidate_descriptor_v2>( +pub fn make_valid_candidate_descriptor_v2 + Copy>( para_id: ParaId, relay_parent: H, core_index: CoreIndex, @@ -335,11 +355,11 @@ impl std::default::Default for TestCandidateBuilder { impl TestCandidateBuilder { /// Build a `CandidateReceipt`. - pub fn build(self) -> CandidateReceipt { + pub fn build(self) -> CandidateReceiptV2 { let mut descriptor = dummy_candidate_descriptor(self.relay_parent); descriptor.para_id = self.para_id; descriptor.pov_hash = self.pov_hash; - CandidateReceipt { descriptor, commitments_hash: self.commitments_hash } + CandidateReceipt { descriptor, commitments_hash: self.commitments_hash }.into() } } diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 188ba4995d83..8513d2dad91d 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -39,7 +39,7 @@ use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, + vstaging::MutateDescriptorV2, BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 2c65298baf01..eef26b83368f 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -61,7 +61,9 @@ mod enter { use frame_support::assert_ok; use frame_system::limits; use polkadot_primitives::{ - vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion}, + vstaging::{ + CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion, MutateDescriptorV2, + }, AvailabilityBitfield, CandidateDescriptor, UncheckedSigned, }; use sp_runtime::Perbill; diff --git a/polkadot/statement-table/src/lib.rs b/polkadot/statement-table/src/lib.rs index 469c877eafc9..68febf76feb3 100644 --- a/polkadot/statement-table/src/lib.rs +++ b/polkadot/statement-table/src/lib.rs @@ -35,8 +35,8 @@ pub use generic::{Config, Context, Table}; pub mod v2 { use crate::generic; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, - CoreIndex, ValidatorIndex, ValidatorSignature, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, + CompactStatement as PrimitiveStatement, CoreIndex, ValidatorIndex, ValidatorSignature, }; /// Statements about candidates on the network. diff --git a/prdoc/pr_5679.prdoc b/prdoc/pr_5679.prdoc new file mode 100644 index 000000000000..59c36ecb933d --- /dev/null +++ b/prdoc/pr_5679.prdoc @@ -0,0 +1,80 @@ +title: Switch to new `CandidateReceipt` primitives +doc: +- audience: + - Node Dev + - Runtime Dev + description: | + This change is just plumbing work and updates all crate interfaces to use the new primitives. + It doesn't alter any functionality and is required before implementing RFC103 on the + node side. +crates: +- name: polkadot-primitives + bump: major +- name: polkadot-runtime-parachains + bump: patch +- name: rococo-runtime + bump: patch +- name: westend-runtime + bump: patch +- name: cumulus-relay-chain-inprocess-interface + bump: major +- name: polkadot-service + bump: patch +- name: polkadot-node-subsystem-types + bump: major +- name: polkadot + bump: patch +- name: cumulus-client-network + bump: major +- name: cumulus-client-pov-recovery + bump: major +- name: cumulus-relay-chain-interface + bump: major +- name: cumulus-relay-chain-minimal-node + bump: major +- name: cumulus-relay-chain-rpc-interface + bump: major +- name: polkadot-node-collation-generation + bump: major +- name: polkadot-node-core-approval-voting + bump: major +- name: polkadot-node-core-av-store + bump: major +- name: polkadot-node-core-backing + bump: major +- name: polkadot-node-core-bitfield-signing + bump: major +- name: polkadot-node-core-candidate-validation + bump: major +- name: polkadot-node-core-dispute-coordinator + bump: major +- name: polkadot-node-core-parachains-inherent + bump: major +- name: polkadot-node-core-prospective-parachains + bump: major +- name: polkadot-node-core-provisioner + bump: major +- name: polkadot-node-core-runtime-api + bump: major +- name: polkadot-availability-distribution + bump: major +- name: polkadot-availability-recovery + bump: major +- name: polkadot-collator-protocol + bump: major +- name: polkadot-dispute-distribution + bump: major +- name: polkadot-node-network-protocol + bump: major +- name: polkadot-statement-distribution + bump: major +- name: polkadot-node-primitives + bump: major +- name: polkadot-node-subsystem-util + bump: major +- name: polkadot-statement-table + bump: major +- name: polkadot-overseer + bump: patch +- name: cumulus-client-consensus-common + bump: major From 0e4bcceb04158919ef7bdb942e8460b3988ffda0 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Mon, 28 Oct 2024 09:44:31 +0200 Subject: [PATCH 46/58] Fix a flaky zombienet test - 0004-coretime-smoke-test (#6236) In the test log I noticed that the batch transaction which configures the coretime chain fails. However when rerunning the transaction manually - it worked. Then I noticed that the coretime chain is initially registered via zombienet and then re-registered via `0004-configure-relay.js`. Because of this there is a period of time when it's not producing blocks and `0004-configure-broker.js` fails to setup the coretime chain. My theory is that the transaction has failed because the coretime chain is stalled during the re-registration. Fixes https://github.com/paritytech/polkadot-sdk/issues/6226 --- polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl b/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl index cfb1ce7d9821..b3a3b46ed780 100644 --- a/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl +++ b/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl @@ -8,6 +8,9 @@ coretime-collator: is up # configure relay chain alice: js-script ./0004-configure-relay.js with "" return is 0 within 600 secs +# Coretime chain should be producing blocks when the extrinsic is sent +alice: parachain 1005 block height is at least 10 within 120 seconds + # configure broker chain coretime-collator: js-script ./0004-configure-broker.js with "" return is 0 within 600 secs From 2d038329738549909764dd2ab8a58cd9527ccf3f Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 28 Oct 2024 11:41:30 +0200 Subject: [PATCH 47/58] fix experimental-ump-signals tests (#6214) Resolves https://github.com/paritytech/polkadot-sdk/issues/6200 Also sets the feature on the rococo-parachain. Will be useful for zombienet testing --- cumulus/pallets/parachain-system/src/tests.rs | 127 ++++++++++++++++-- .../testing/rococo-parachain/Cargo.toml | 2 +- prdoc/pr_6214.prdoc | 5 + 3 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 prdoc/pr_6214.prdoc diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 23223627ebca..2b65dd6a9216 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -25,6 +25,8 @@ use frame_support::{assert_ok, parameter_types, weights::Weight}; use frame_system::RawOrigin; use hex_literal::hex; use rand::Rng; +#[cfg(feature = "experimental-ump-signals")] +use relay_chain::vstaging::{UMPSignal, UMP_SEPARATOR}; use relay_chain::HrmpChannelId; use sp_core::H256; @@ -583,7 +585,25 @@ fn send_upward_message_num_per_candidate() { }, || { let v = UpwardMessages::::get(); - assert_eq!(v, vec![b"Mr F was here".to_vec()]); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + v, + vec![ + b"Mr F was here".to_vec(), + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(1), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert_eq!(v, vec![b"Mr F was here".to_vec()]); + } }, ) .add_with_post_test( @@ -594,7 +614,25 @@ fn send_upward_message_num_per_candidate() { }, || { let v = UpwardMessages::::get(); - assert_eq!(v, vec![b"message 2".to_vec()]); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + v, + vec![ + b"message 2".to_vec(), + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(2), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert_eq!(v, vec![b"message 2".to_vec()]); + } }, ); } @@ -620,7 +658,24 @@ fn send_upward_message_relay_bottleneck() { || { // The message won't be sent because there is already one message in queue. let v = UpwardMessages::::get(); - assert!(v.is_empty()); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + v, + vec![ + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(1), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert!(v.is_empty()); + } }, ) .add_with_post_test( @@ -628,7 +683,25 @@ fn send_upward_message_relay_bottleneck() { || { /* do nothing within block */ }, || { let v = UpwardMessages::::get(); - assert_eq!(v, vec![vec![0u8; 8]]); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + v, + vec![ + vec![0u8; 8], + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(2), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert_eq!(v, vec![vec![0u8; 8]]); + } }, ); } @@ -1172,7 +1245,25 @@ fn ump_fee_factor_increases_and_decreases() { || { // Factor decreases in `on_finalize`, but only if we are below the threshold let messages = UpwardMessages::::get(); - assert_eq!(messages, vec![b"Test".to_vec()]); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + messages, + vec![ + b"Test".to_vec(), + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(1), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert_eq!(messages, vec![b"Test".to_vec()]); + } assert_eq!( UpwardDeliveryFeeFactor::::get(), FixedU128::from_rational(105, 100) @@ -1186,10 +1277,28 @@ fn ump_fee_factor_increases_and_decreases() { }, || { let messages = UpwardMessages::::get(); - assert_eq!( - messages, - vec![b"This message will be enough to increase the fee factor".to_vec(),] - ); + #[cfg(feature = "experimental-ump-signals")] + { + assert_eq!( + messages, + vec![ + b"This message will be enough to increase the fee factor".to_vec(), + UMP_SEPARATOR, + UMPSignal::SelectCore( + CoreSelector(2), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET) + ) + .encode() + ] + ); + } + #[cfg(not(feature = "experimental-ump-signals"))] + { + assert_eq!( + messages, + vec![b"This message will be enough to increase the fee factor".to_vec()] + ); + } // Now the delivery fee factor is decreased, since we are below the threshold assert_eq!(UpwardDeliveryFeeFactor::::get(), FixedU128::from_u32(1)); }, diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index bbc1185db0d8..b0581c8d43ff 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -49,7 +49,7 @@ polkadot-runtime-common = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } pallet-message-queue = { workspace = true } -cumulus-pallet-parachain-system = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true, features = ["experimental-ump-signals"] } cumulus-pallet-xcm = { workspace = true } cumulus-pallet-xcmp-queue = { workspace = true } cumulus-ping = { workspace = true } diff --git a/prdoc/pr_6214.prdoc b/prdoc/pr_6214.prdoc new file mode 100644 index 000000000000..c991df986307 --- /dev/null +++ b/prdoc/pr_6214.prdoc @@ -0,0 +1,5 @@ +crates: + - name: cumulus-pallet-parachain-system + bump: none + - name: rococo-parachain-runtime + bump: none From 7d04b0b834888047472d10bd2ec59a44a768c6f9 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 28 Oct 2024 13:34:19 +0200 Subject: [PATCH 48/58] remove parachains_assigner code (#6171) Resolves https://github.com/paritytech/polkadot-sdk/issues/5970 Removes the code of the legacy parachains assigner, which was used prior to coretime. Now that all networks are upgraded to use the coretime assigner, we can remove it. --- .../parachains/src/assigner_parachains.rs | 67 ----------- .../src/assigner_parachains/mock_helpers.rs | 84 ------------- .../src/assigner_parachains/tests.rs | 111 ------------------ polkadot/runtime/parachains/src/lib.rs | 1 - polkadot/runtime/parachains/src/mock.rs | 5 +- prdoc/pr_6171.prdoc | 7 ++ 6 files changed, 8 insertions(+), 267 deletions(-) delete mode 100644 polkadot/runtime/parachains/src/assigner_parachains.rs delete mode 100644 polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs delete mode 100644 polkadot/runtime/parachains/src/assigner_parachains/tests.rs create mode 100644 prdoc/pr_6171.prdoc diff --git a/polkadot/runtime/parachains/src/assigner_parachains.rs b/polkadot/runtime/parachains/src/assigner_parachains.rs deleted file mode 100644 index 53edae5c32fc..000000000000 --- a/polkadot/runtime/parachains/src/assigner_parachains.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 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 . - -//! The bulk (parachain slot auction) blockspace assignment provider. -//! This provider is tightly coupled with the configuration and paras modules. - -#[cfg(test)] -mod mock_helpers; -#[cfg(test)] -mod tests; - -use frame_system::pallet_prelude::BlockNumberFor; -use polkadot_primitives::CoreIndex; - -use crate::{ - configuration, paras, - scheduler::common::{Assignment, AssignmentProvider}, -}; - -pub use pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + configuration::Config + paras::Config {} -} - -impl AssignmentProvider> for Pallet { - fn pop_assignment_for_core(core_idx: CoreIndex) -> Option { - paras::Parachains::::get() - .get(core_idx.0 as usize) - .copied() - .map(Assignment::Bulk) - } - - fn report_processed(_: Assignment) {} - - /// Bulk assignment has no need to push the assignment back on a session change, - /// this is a no-op in the case of a bulk assignment slot. - fn push_back_assignment(_: Assignment) {} - - #[cfg(any(feature = "runtime-benchmarks", test))] - fn get_mock_assignment(_: CoreIndex, para_id: polkadot_primitives::Id) -> Assignment { - Assignment::Bulk(para_id) - } - - fn assignment_duplicated(_: &Assignment) {} -} diff --git a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs deleted file mode 100644 index d984fd9232c3..000000000000 --- a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 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 . - -//! Helper functions for tests - -use crate::{ - mock::MockGenesisConfig, - paras::{ParaGenesisArgs, ParaKind}, -}; - -use polkadot_primitives::{Balance, HeadData, ValidationCode}; -use sp_runtime::Perbill; - -fn default_genesis_config() -> MockGenesisConfig { - MockGenesisConfig { - configuration: crate::configuration::GenesisConfig { - config: crate::configuration::HostConfiguration { ..Default::default() }, - }, - ..Default::default() - } -} - -#[derive(Debug)] -pub struct GenesisConfigBuilder { - pub on_demand_cores: u32, - pub on_demand_base_fee: Balance, - pub on_demand_fee_variability: Perbill, - pub on_demand_max_queue_size: u32, - pub on_demand_target_queue_utilization: Perbill, - pub onboarded_on_demand_chains: Vec, -} - -impl Default for GenesisConfigBuilder { - fn default() -> Self { - Self { - on_demand_cores: 10, - on_demand_base_fee: 10_000, - on_demand_fee_variability: Perbill::from_percent(1), - on_demand_max_queue_size: 100, - on_demand_target_queue_utilization: Perbill::from_percent(25), - onboarded_on_demand_chains: vec![], - } - } -} - -impl GenesisConfigBuilder { - pub(super) fn build(self) -> MockGenesisConfig { - let mut genesis = default_genesis_config(); - let config = &mut genesis.configuration.config; - config.scheduler_params.num_cores = self.on_demand_cores; - config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; - config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; - config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.scheduler_params.on_demand_target_queue_utilization = - self.on_demand_target_queue_utilization; - - let paras = &mut genesis.paras.paras; - for para_id in self.onboarded_on_demand_chains { - paras.push(( - para_id, - ParaGenesisArgs { - genesis_head: HeadData::from(vec![0u8]), - validation_code: ValidationCode::from(vec![0u8]), - para_kind: ParaKind::Parathread, - }, - )) - } - - genesis - } -} diff --git a/polkadot/runtime/parachains/src/assigner_parachains/tests.rs b/polkadot/runtime/parachains/src/assigner_parachains/tests.rs deleted file mode 100644 index 6e8e185bb48d..000000000000 --- a/polkadot/runtime/parachains/src/assigner_parachains/tests.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 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 . - -use super::*; -use crate::{ - assigner_parachains::mock_helpers::GenesisConfigBuilder, - initializer::SessionChangeNotification, - mock::{ - new_test_ext, ParachainsAssigner, Paras, ParasShared, RuntimeOrigin, Scheduler, System, - }, - paras::{ParaGenesisArgs, ParaKind}, -}; -use frame_support::{assert_ok, pallet_prelude::*}; -use polkadot_primitives::{BlockNumber, Id as ParaId, SessionIndex, ValidationCode}; - -fn schedule_blank_para(id: ParaId, parakind: ParaKind) { - let validation_code: ValidationCode = vec![1, 2, 3].into(); - assert_ok!(Paras::schedule_para_initialize( - id, - ParaGenesisArgs { - genesis_head: Vec::new().into(), - validation_code: validation_code.clone(), - para_kind: parakind, - } - )); - - assert_ok!(Paras::add_trusted_validation_code(RuntimeOrigin::root(), validation_code)); -} - -fn run_to_block( - to: BlockNumber, - new_session: impl Fn(BlockNumber) -> Option>, -) { - while System::block_number() < to { - let b = System::block_number(); - - Scheduler::initializer_finalize(); - Paras::initializer_finalize(b); - - if let Some(notification) = new_session(b + 1) { - let mut notification_with_session_index = notification; - // We will make every session change trigger an action queue. Normally this may require - // 2 or more session changes. - if notification_with_session_index.session_index == SessionIndex::default() { - notification_with_session_index.session_index = ParasShared::scheduled_session(); - } - Paras::initializer_on_new_session(¬ification_with_session_index); - Scheduler::initializer_on_new_session(¬ification_with_session_index); - } - - System::on_finalize(b); - - System::on_initialize(b + 1); - System::set_block_number(b + 1); - - Paras::initializer_initialize(b + 1); - Scheduler::initializer_initialize(b + 1); - - // In the real runtime this is expected to be called by the `InclusionInherent` pallet. - Scheduler::advance_claim_queue(&Default::default()); - } -} - -// This and the scheduler test schedule_schedules_including_just_freed together -// ensure that next_up_on_available and next_up_on_time_out will always be -// filled with scheduler claims for lease holding parachains. (Removes the need -// for two other scheduler tests) -#[test] -fn parachains_assigner_pop_assignment_is_always_some() { - let core_index = CoreIndex(0); - let para_id = ParaId::from(10); - let expected_assignment = Assignment::Bulk(para_id); - - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - // Register the para_id as a lease holding parachain - schedule_blank_para(para_id, ParaKind::Parachain); - - assert!(!Paras::is_parachain(para_id)); - run_to_block(10, |n| if n == 10 { Some(Default::default()) } else { None }); - assert!(Paras::is_parachain(para_id)); - - for _ in 0..20 { - assert!( - ParachainsAssigner::pop_assignment_for_core(core_index) == - Some(expected_assignment.clone()) - ); - } - - run_to_block(20, |n| if n == 20 { Some(Default::default()) } else { None }); - - for _ in 0..20 { - assert!( - ParachainsAssigner::pop_assignment_for_core(core_index) == - Some(expected_assignment.clone()) - ); - } - }); -} diff --git a/polkadot/runtime/parachains/src/lib.rs b/polkadot/runtime/parachains/src/lib.rs index f1162e1cc215..828c0b9bcef2 100644 --- a/polkadot/runtime/parachains/src/lib.rs +++ b/polkadot/runtime/parachains/src/lib.rs @@ -24,7 +24,6 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod assigner_coretime; -pub mod assigner_parachains; pub mod configuration; pub mod coretime; pub mod disputes; diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index c23918708b21..9ef3922f0f8c 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -17,7 +17,7 @@ //! Mocks for all the traits. use crate::{ - assigner_coretime, assigner_parachains, configuration, coretime, disputes, dmp, hrmp, + assigner_coretime, configuration, coretime, disputes, dmp, hrmp, inclusion::{self, AggregateMessageOrigin, UmpQueueId}, initializer, on_demand, origin, paras, paras::ParaKind, @@ -76,7 +76,6 @@ frame_support::construct_runtime!( ParaInherent: paras_inherent, Scheduler: scheduler, MockAssigner: mock_assigner, - ParachainsAssigner: assigner_parachains, OnDemand: on_demand, CoretimeAssigner: assigner_coretime, Coretime: coretime, @@ -399,8 +398,6 @@ impl pallet_message_queue::Config for Test { type IdleMaxServiceWeight = (); } -impl assigner_parachains::Config for Test {} - parameter_types! { pub const OnDemandTrafficDefaultValue: FixedU128 = FixedU128::from_u32(1); // Production chains should keep this numbar around twice the diff --git a/prdoc/pr_6171.prdoc b/prdoc/pr_6171.prdoc new file mode 100644 index 000000000000..36246350cf8a --- /dev/null +++ b/prdoc/pr_6171.prdoc @@ -0,0 +1,7 @@ +title: 'remove parachains_assigner' +doc: + - audience: Runtime Dev + description: "Remove the code of the parachains_assigner pallet, since coretime was released on all production networks." +crates: +- name: polkadot-runtime-parachains + bump: major From 5f95c0b6765776b645640033b74b0f7ea113fe7d Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 28 Oct 2024 14:31:00 +0100 Subject: [PATCH 49/58] [pallet-revive] Add Ethereum JSON-RPC server (#6147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redo of https://github.com/paritytech/polkadot-sdk/pull/5953 --------- Co-authored-by: Alexander Theißen Co-authored-by: GitHub Action --- Cargo.lock | 170 +++- Cargo.toml | 2 + .../assets/asset-hub-westend/src/lib.rs | 13 +- prdoc/pr_6147.prdoc | 17 + substrate/bin/node/cli/src/chain_spec.rs | 4 - substrate/bin/node/runtime/src/lib.rs | 14 +- .../revive/fixtures/contracts/rpc_demo.rs | 36 + substrate/frame/revive/rpc/Cargo.toml | 83 ++ substrate/frame/revive/rpc/examples/README.md | 99 +++ .../frame/revive/rpc/examples/js/.gitignore | 24 + .../frame/revive/rpc/examples/js/bun.lockb | Bin 0 -> 23039 bytes .../frame/revive/rpc/examples/js/index.html | 29 + .../frame/revive/rpc/examples/js/package.json | 18 + .../frame/revive/rpc/examples/js/src/main.ts | 141 ++++ .../revive/rpc/examples/js/src/script.ts | 49 ++ .../revive/rpc/examples/js/tsconfig.json | 23 + .../revive/rpc/examples/rpc_demo.polkavm | 1 + .../frame/revive/rpc/examples/rust/deploy.rs | 74 ++ .../revive/rpc/examples/rust/extrinsic.rs | 54 ++ .../rpc/examples/rust/remark-extrinsic.rs | 43 + .../rpc/examples/rust/rpc-playground.rs | 41 + .../revive/rpc/examples/rust/transfer.rs | 55 ++ .../rpc/examples/westend_local_network.toml | 41 + .../frame/revive/rpc/revive_chain.metadata | Bin 0 -> 342591 bytes substrate/frame/revive/rpc/src/cli.rs | 131 +++ substrate/frame/revive/rpc/src/client.rs | 799 ++++++++++++++++++ substrate/frame/revive/rpc/src/example.rs | 96 +++ substrate/frame/revive/rpc/src/lib.rs | 359 ++++++++ substrate/frame/revive/rpc/src/main.rs | 39 + .../frame/revive/rpc/src/rpc_methods_gen.rs | 160 ++++ .../frame/revive/rpc/src/subxt_client.rs | 71 ++ substrate/frame/revive/rpc/src/tests.rs | 103 +++ substrate/frame/revive/src/evm/api/account.rs | 9 + .../frame/revive/src/evm/api/rpc_types_gen.rs | 4 +- substrate/frame/revive/src/evm/api/type_id.rs | 18 + substrate/frame/revive/src/evm/runtime.rs | 2 +- substrate/frame/revive/src/lib.rs | 9 +- umbrella/Cargo.toml | 8 +- umbrella/src/lib.rs | 4 + 39 files changed, 2823 insertions(+), 20 deletions(-) create mode 100644 prdoc/pr_6147.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/rpc_demo.rs create mode 100644 substrate/frame/revive/rpc/Cargo.toml create mode 100644 substrate/frame/revive/rpc/examples/README.md create mode 100644 substrate/frame/revive/rpc/examples/js/.gitignore create mode 100755 substrate/frame/revive/rpc/examples/js/bun.lockb create mode 100644 substrate/frame/revive/rpc/examples/js/index.html create mode 100644 substrate/frame/revive/rpc/examples/js/package.json create mode 100644 substrate/frame/revive/rpc/examples/js/src/main.ts create mode 100644 substrate/frame/revive/rpc/examples/js/src/script.ts create mode 100644 substrate/frame/revive/rpc/examples/js/tsconfig.json create mode 120000 substrate/frame/revive/rpc/examples/rpc_demo.polkavm create mode 100644 substrate/frame/revive/rpc/examples/rust/deploy.rs create mode 100644 substrate/frame/revive/rpc/examples/rust/extrinsic.rs create mode 100644 substrate/frame/revive/rpc/examples/rust/remark-extrinsic.rs create mode 100644 substrate/frame/revive/rpc/examples/rust/rpc-playground.rs create mode 100644 substrate/frame/revive/rpc/examples/rust/transfer.rs create mode 100644 substrate/frame/revive/rpc/examples/westend_local_network.toml create mode 100644 substrate/frame/revive/rpc/revive_chain.metadata create mode 100644 substrate/frame/revive/rpc/src/cli.rs create mode 100644 substrate/frame/revive/rpc/src/client.rs create mode 100644 substrate/frame/revive/rpc/src/example.rs create mode 100644 substrate/frame/revive/rpc/src/lib.rs create mode 100644 substrate/frame/revive/rpc/src/main.rs create mode 100644 substrate/frame/revive/rpc/src/rpc_methods_gen.rs create mode 100644 substrate/frame/revive/rpc/src/subxt_client.rs create mode 100644 substrate/frame/revive/rpc/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 3d5193fe0ee8..dfa70250e30f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,6 +119,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -1150,6 +1165,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd 0.13.0", + "zstd-safe 7.0.0", +] + [[package]] name = "async-executor" version = "1.5.1" @@ -2582,6 +2613,27 @@ dependencies = [ "tuplex", ] +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.5.1" @@ -7420,6 +7472,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + [[package]] name = "httparse" version = "1.8.0" @@ -7900,6 +7958,16 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "iri-string" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -9683,6 +9751,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -12472,6 +12550,41 @@ dependencies = [ "subxt-signer", ] +[[package]] +name = "pallet-revive-eth-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.13", + "env_logger 0.11.3", + "futures", + "hex", + "hex-literal", + "hyper 1.3.1", + "jsonrpsee 0.24.3", + "log", + "pallet-revive", + "pallet-revive-fixtures", + "parity-scale-codec", + "rlp 0.6.1", + "sc-rpc", + "scale-info", + "secp256k1", + "serde_json", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0", + "sp-runtime 31.0.1", + "sp-weights 27.0.0", + "substrate-cli-test-utils", + "subxt", + "subxt-signer", + "thiserror", + "tokio", + "tower", + "tower-http 0.5.2", + "tracing-subscriber 0.3.18", +] + [[package]] name = "pallet-revive-fixtures" version = "0.1.0" @@ -15395,6 +15508,7 @@ dependencies = [ "pallet-referenda", "pallet-remark", "pallet-revive", + "pallet-revive-eth-rpc", "pallet-revive-fixtures", "pallet-revive-mock-network", "pallet-revive-proc-macro", @@ -24217,9 +24331,9 @@ dependencies = [ [[package]] name = "subxt-core" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f41eb2e2eea6ed45649508cc735f92c27f1fcfb15229e75f8270ea73177345" +checksum = "3af3b36405538a36b424d229dc908d1396ceb0994c90825ce928709eac1a159a" dependencies = [ "base58", "blake2 0.10.6", @@ -24745,9 +24859,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] @@ -24774,9 +24888,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", @@ -25162,7 +25276,7 @@ dependencies = [ "futures-util", "http 0.2.9", "http-body 0.4.5", - "http-range-header", + "http-range-header 0.3.1", "mime", "pin-project-lite", "tower-layer", @@ -25176,14 +25290,29 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ + "async-compression", + "base64 0.21.7", "bitflags 2.6.0", "bytes", + "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "http-body-util", + "http-range-header 0.4.1", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", + "tokio", + "tokio-util", + "tower", "tower-layer", "tower-service", + "tracing", + "uuid", ] [[package]] @@ -25600,6 +25729,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -27440,6 +27578,15 @@ dependencies = [ "zstd-safe 6.0.6", ] +[[package]] +name = "zstd" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +dependencies = [ + "zstd-safe 7.0.0", +] + [[package]] name = "zstd-safe" version = "5.0.2+zstd.1.5.2" @@ -27460,6 +27607,15 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.8+zstd.1.5.5" diff --git a/Cargo.toml b/Cargo.toml index dd1fa40e146a..e451529431ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -402,6 +402,7 @@ members = [ "substrate/frame/revive/fixtures", "substrate/frame/revive/mock-network", "substrate/frame/revive/proc-macro", + "substrate/frame/revive/rpc", "substrate/frame/revive/uapi", "substrate/frame/root-offences", "substrate/frame/root-testing", @@ -965,6 +966,7 @@ pallet-recovery = { path = "substrate/frame/recovery", default-features = false pallet-referenda = { path = "substrate/frame/referenda", default-features = false } pallet-remark = { default-features = false, path = "substrate/frame/remark" } pallet-revive = { path = "substrate/frame/revive", default-features = false } +pallet-revive-eth-rpc = { path = "substrate/frame/revive/rpc", default-features = false } pallet-revive-fixtures = { path = "substrate/frame/revive/fixtures", default-features = false } pallet-revive-mock-network = { default-features = false, path = "substrate/frame/revive/mock-network" } pallet-revive-proc-macro = { path = "substrate/frame/revive/proc-macro", default-features = false } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 066a1c2bdfdc..0ed24db97df8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -58,7 +58,7 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; -use pallet_revive::evm::runtime::EthExtra; +use pallet_revive::{evm::runtime::EthExtra, AddressMapper}; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, @@ -2069,8 +2069,17 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi for Runtime + impl pallet_revive::ReviveApi for Runtime { + fn balance(address: H160) -> Balance { + let account = ::AddressMapper::to_account_id(&address); + Balances::usable_balance(account) + } + + fn nonce(address: H160) -> Nonce { + let account = ::AddressMapper::to_account_id(&address); + System::account_nonce(account) + } fn eth_transact( from: H160, dest: Option, diff --git a/prdoc/pr_6147.prdoc b/prdoc/pr_6147.prdoc new file mode 100644 index 000000000000..eef0d0936675 --- /dev/null +++ b/prdoc/pr_6147.prdoc @@ -0,0 +1,17 @@ +title: "[pallet-revive] Ethereum JSON-RPC" + +doc: + - audience: Runtime Dev + description: | + Add a new Ethereum JSON-RPC server that can be used a substrate chain configured with pallet-revive +crates: + - name: pallet-revive + bump: patch + - name: asset-hub-westend-runtime + bump: patch + - name: pallet-revive-eth-rpc + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index 362deacba5f3..0c4a48a19260 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -437,10 +437,6 @@ fn default_endowed_accounts() -> Vec { .chain([ eth_account(subxt_signer::eth::dev::alith()), eth_account(subxt_signer::eth::dev::baltathar()), - eth_account(subxt_signer::eth::dev::charleth()), - eth_account(subxt_signer::eth::dev::dorothy()), - eth_account(subxt_signer::eth::dev::ethan()), - eth_account(subxt_signer::eth::dev::faith()), ]) .collect() } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 89b5bf26ce75..2dbc6ab39e73 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -76,7 +76,7 @@ use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; -use pallet_revive::evm::runtime::EthExtra; +use pallet_revive::{evm::runtime::EthExtra, AddressMapper}; use pallet_session::historical as pallet_session_historical; // Can't use `FungibleAdapter` here until Treasury pallet migrates to fungibles // @@ -3157,8 +3157,18 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi for Runtime + impl pallet_revive::ReviveApi for Runtime { + fn balance(address: H160) -> Balance { + let account = ::AddressMapper::to_account_id(&address); + Balances::usable_balance(account) + } + + fn nonce(address: H160) -> Nonce { + let account = ::AddressMapper::to_account_id(&address); + System::account_nonce(account) + } + fn eth_transact( from: H160, dest: Option, diff --git a/substrate/frame/revive/fixtures/contracts/rpc_demo.rs b/substrate/frame/revive/fixtures/contracts/rpc_demo.rs new file mode 100644 index 000000000000..0d75c6eb8df6 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/rpc_demo.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(128, data: [u8],); + api::deposit_event(&[], data); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(128, data: [u8],); + api::deposit_event(&[], data); +} diff --git a/substrate/frame/revive/rpc/Cargo.toml b/substrate/frame/revive/rpc/Cargo.toml new file mode 100644 index 000000000000..3c05bdeef478 --- /dev/null +++ b/substrate/frame/revive/rpc/Cargo.toml @@ -0,0 +1,83 @@ +[package] +name = "pallet-revive-eth-rpc" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "An Ethereum JSON-RPC server for pallet-revive." + +[[bin]] +name = "eth-rpc" +path = "src/main.rs" + +[[example]] +name = "deploy" +path = "examples/rust/deploy.rs" +required-features = ["example", "riscv"] + +[[example]] +name = "transfer" +path = "examples/rust/transfer.rs" +required-features = ["example", "riscv"] + +[[example]] +name = "rpc-playground" +path = "examples/rust/rpc-playground.rs" +required-features = ["example", "riscv"] + +[[example]] +name = "extrinsic" +path = "examples/rust/extrinsic.rs" +required-features = ["example", "riscv"] + +[[example]] +name = "remark-extrinsic" +path = "examples/rust/remark-extrinsic.rs" +required-features = ["example", "riscv"] + +[dependencies] +clap = { workspace = true, features = ["derive"] } +anyhow = { workspace = true } +futures = { workspace = true, features = ["thread-pool"] } +jsonrpsee = { workspace = true, features = ["full"] } +serde_json = { workspace = true } +thiserror = { workspace = true } +sp-crypto-hashing = { workspace = true } +subxt = { workspace = true, default-features = true, features = [ + "unstable-reconnecting-rpc-client", +] } +tokio = { workspace = true, features = ["full"] } +codec = { workspace = true, features = ["derive"] } +log.workspace = true +tracing-subscriber.workspace = true +pallet-revive = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } +sp-runtime = { workspace = true, default-features = true } +sc-rpc = { workspace = true, default-features = true } + +tower.workspace = true +tower-http = { workspace = true, features = ["full"] } +hyper.workspace = true + +rlp = { workspace = true, optional = true } +subxt-signer = { workspace = true, optional = true, features = [ + "unstable-eth", +] } +hex = { workspace = true, optional = true } +hex-literal = { workspace = true, optional = true } +scale-info = { workspace = true } +secp256k1 = { workspace = true, optional = true, features = ["recovery"] } +env_logger = { workspace = true } + +[features] +dev = [] +example = ["hex", "hex-literal", "rlp", "secp256k1", "subxt-signer"] +riscv = ["pallet-revive/riscv"] + +[dev-dependencies] +hex-literal = { workspace = true } +pallet-revive-fixtures = { workspace = true } +substrate-cli-test-utils = { workspace = true } diff --git a/substrate/frame/revive/rpc/examples/README.md b/substrate/frame/revive/rpc/examples/README.md new file mode 100644 index 000000000000..7c01dc0075ee --- /dev/null +++ b/substrate/frame/revive/rpc/examples/README.md @@ -0,0 +1,99 @@ +## Pre-requisites + + Build `pallet-revive-fixture`, as we need some compiled contracts to exercise the RPC server. + +```bash +cargo build -p pallet-revive-fixtures --features riscv +``` + +## Start the node + +Start the kitchensink node: + +```bash +RUST_LOG="error,evm=debug,sc_rpc_server=info,runtime::revive=debug" cargo run --bin substrate-node -- --dev +``` + +## Start a zombienet network + +Alternatively, you can start a zombienet network with the westend Asset Hub parachain: + +Prerequisites for running a local network: +- download latest [zombienet release](https://github.com/paritytech/zombienet/releases); +- build Polkadot binary by running `cargo build -p polkadot --release --features fast-runtime` command in the + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; +- build Polkadot Parachain binary by running `cargo build -p polkadot-parachain-bin --release` command in the + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; + +```bash +zombienet spawn --provider native westend_local_network.toml +``` + +## Start the RPC server + +This command starts the Ethereum JSON-RPC server, which runs on `localhost:8545` by default: + +```bash +RUST_LOG="info,eth-rpc=debug" cargo run -p pallet-revive-eth-rpc --features dev +``` + +## Rust examples + +Run one of the examples from the `examples` directory to send a transaction to the node: + +```bash +RUST_LOG="info,eth-rpc=debug" cargo run -p pallet-revive-eth-rpc --features example --example deploy +``` + +## JS examples + +Interact with the node using MetaMask & Ether.js, by starting the example web app: + +```bash + +cd substrate/frame/revive/rpc/examples/js +bun install +bun run dev +``` + +Alternatively, you can run the example script directly: + +```bash +cd substrate/frame/revive/rpc/examples/js +bun src/script.ts +``` + +### Configure MetaMask + +You can use the following instructions to setup [MetaMask] with the local chain. + +> **Note**: When you interact with MetaMask and restart the chain, you need to clear the activity tab (Settings > +Advanced > Clear activity tab data), and in some cases lock/unlock MetaMask to reset the nonce. +See [this guide][reset-account] for more info on how to reset the account activity. + +#### Add a new network + +To interact with the local chain, add a new network in [MetaMask]. +See [this guide][add-network] for more info on how to add a custom network. + +Make sure the node and the RPC server are started, and use the following settings to configure the network +(MetaMask > Networks > Add a network manually): + +- Network name: KitchenSink +- RPC URL: +- Chain ID: 420420420 +- Currency Symbol: `DEV` + +#### Import Dev account + +You will need to import the following account, endowed with some balance at genesis, to interact with the chain. +See [this guide][import-account] for more info on how to import an account. + +- Account: `0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac` +- Private Key: `5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133` + +[MetaMask]: https://metamask.io +[add-network]: https://support.metamask.io/networks-and-sidechains/managing-networks/how-to-add-a-custom-network-rpc/#adding-a-network-manually +[import-account]: https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-import-an-account/ +[reset-account]: https://support.metamask.io/managing-my-wallet/resetting-deleting-and-restoring/how-to-clear-your-account-activity-reset-account + diff --git a/substrate/frame/revive/rpc/examples/js/.gitignore b/substrate/frame/revive/rpc/examples/js/.gitignore new file mode 100644 index 000000000000..a547bf36d8d1 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/substrate/frame/revive/rpc/examples/js/bun.lockb b/substrate/frame/revive/rpc/examples/js/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..8bf47d7eb8b82734467165627413c33adede6fd6 GIT binary patch literal 23039 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 literal 0 HcmV?d00001 diff --git a/substrate/frame/revive/rpc/examples/js/index.html b/substrate/frame/revive/rpc/examples/js/index.html new file mode 100644 index 000000000000..72f7026ce5f3 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/index.html @@ -0,0 +1,29 @@ + + + + + + + MetaMask Playground + + + + + + + + + + + + + + + diff --git a/substrate/frame/revive/rpc/examples/js/package.json b/substrate/frame/revive/rpc/examples/js/package.json new file mode 100644 index 000000000000..4d7136606b65 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/package.json @@ -0,0 +1,18 @@ +{ + "name": "demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "ethers": "^6.13.1" + }, + "devDependencies": { + "typescript": "^5.5.3", + "vite": "^5.4.8" + } +} diff --git a/substrate/frame/revive/rpc/examples/js/src/main.ts b/substrate/frame/revive/rpc/examples/js/src/main.ts new file mode 100644 index 000000000000..88b72755aae9 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/src/main.ts @@ -0,0 +1,141 @@ +import { + AddressLike, + BrowserProvider, + Contract, + ContractFactory, + Eip1193Provider, + JsonRpcSigner, + parseEther, +} from "ethers"; + +declare global { + interface Window { + ethereum?: Eip1193Provider; + } +} + +function str_to_bytes(str: string): Uint8Array { + return new TextEncoder().encode(str); +} + +document.addEventListener("DOMContentLoaded", async () => { + if (typeof window.ethereum == "undefined") { + return console.log("MetaMask is not installed"); + } + + console.log("MetaMask is installed!"); + const provider = new BrowserProvider(window.ethereum); + + console.log("Getting signer..."); + let signer: JsonRpcSigner; + try { + signer = await provider.getSigner(); + console.log(`Signer: ${signer.address}`); + } catch (e) { + console.error("Failed to get signer", e); + return; + } + + console.log("Getting block number..."); + try { + const blockNumber = await provider.getBlockNumber(); + console.log(`Block number: ${blockNumber}`); + } catch (e) { + console.error("Failed to get block number", e); + return; + } + + const nonce = await signer.getNonce(); + console.log(`Nonce: ${nonce}`); + + document.getElementById("transferButton")?.addEventListener( + "click", + async () => { + const address = + (document.getElementById("transferInput") as HTMLInputElement).value; + await transfer(address); + }, + ); + + document.getElementById("deployButton")?.addEventListener( + "click", + async () => { + await deploy(); + }, + ); + document.getElementById("deployAndCallButton")?.addEventListener( + "click", + async () => { + const nonce = await signer.getNonce(); + console.log(`deploy with nonce: ${nonce}`); + + const address = await deploy(); + if (address) { + const nonce = await signer.getNonce(); + console.log(`call with nonce: ${nonce}`); + await call(address); + } + }, + ); + document.getElementById("callButton")?.addEventListener("click", async () => { + const address = + (document.getElementById("callInput") as HTMLInputElement).value; + await call(address); + }); + + async function deploy() { + console.log("Deploying contract..."); + + const bytecode = await fetch("rpc_demo.polkavm").then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.arrayBuffer(); + }) + .then((arrayBuffer) => new Uint8Array(arrayBuffer)); + + const contractFactory = new ContractFactory( + [ + "constructor(bytes memory _data)", + ], + bytecode, + signer, + ); + + try { + const args = str_to_bytes("hello"); + const contract = await contractFactory.deploy(args); + await contract.waitForDeployment(); + const address = await contract.getAddress(); + console.log(`Contract deployed: ${address}`); + return address; + } catch (e) { + console.error("Failed to deploy contract", e); + return; + } + } + + async function call(address: string) { + const abi = ["function call(bytes data)"]; + const contract = new Contract(address, abi, signer); + const tx = await contract.call(str_to_bytes("world")); + + console.log("Transaction hash:", tx.hash); + } + + async function transfer(to: AddressLike) { + console.log(`transferring 1 DOT to ${to}...`); + try { + const tx = await signer.sendTransaction({ + to, + value: parseEther("1.0"), + }); + + const receipt = await tx.wait(); + console.log(`Transaction hash: ${receipt?.hash}`); + } catch (e) { + console.error("Failed to send transaction", e); + return; + } + } +}); diff --git a/substrate/frame/revive/rpc/examples/js/src/script.ts b/substrate/frame/revive/rpc/examples/js/src/script.ts new file mode 100644 index 000000000000..96414e34b7ed --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/src/script.ts @@ -0,0 +1,49 @@ +//! Run with bun run script.ts + +import { readFileSync } from "fs"; +import { Contract, ContractFactory, JsonRpcProvider } from "ethers"; + +const provider = new JsonRpcProvider("http://localhost:8545"); +const signer = await provider.getSigner(); +console.log( + `Signer address: ${await signer.getAddress()}, Nonce: ${await signer + .getNonce()}`, +); + +function str_to_bytes(str: string): Uint8Array { + return new TextEncoder().encode(str); +} + +// deploy +async function deploy() { + console.log(`Deploying Contract...`); + + const bytecode = readFileSync("rpc_demo.polkavm"); + const contractFactory = new ContractFactory( + [ + "constructor(bytes memory _data)", + ], + bytecode, + signer, + ); + + const args = str_to_bytes("hello"); + console.log("Deploying contract with args:", args); + const contract = await contractFactory.deploy(args); + await contract.waitForDeployment(); + const address = await contract.getAddress(); + console.log(`Contract deployed: ${address}`); + return address; +} + +async function call(address: string) { + console.log(`Calling Contract at ${address}...`); + + const abi = ["function call(bytes data)"]; + const contract = new Contract(address, abi, signer); + const tx = await contract.call(str_to_bytes("world")); + console.log("Call transaction hash:", tx.hash); +} + +const address = await deploy(); +await call(address); diff --git a/substrate/frame/revive/rpc/examples/js/tsconfig.json b/substrate/frame/revive/rpc/examples/js/tsconfig.json new file mode 100644 index 000000000000..0511b9f0e041 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/js/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/substrate/frame/revive/rpc/examples/rpc_demo.polkavm b/substrate/frame/revive/rpc/examples/rpc_demo.polkavm new file mode 120000 index 000000000000..63925dfcc544 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rpc_demo.polkavm @@ -0,0 +1 @@ +../../../../../target/pallet-revive-fixtures/rpc_demo.polkavm \ No newline at end of file diff --git a/substrate/frame/revive/rpc/examples/rust/deploy.rs b/substrate/frame/revive/rpc/examples/rust/deploy.rs new file mode 100644 index 000000000000..f2be5d233f6d --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rust/deploy.rs @@ -0,0 +1,74 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use jsonrpsee::http_client::HttpClientBuilder; +use pallet_revive::{ + create1, + evm::{Account, BlockTag, Bytes, ReceiptInfo, U256}, +}; +use pallet_revive_eth_rpc::{ + example::{send_transaction, wait_for_receipt}, + EthRpcClient, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init(); + let account = Account::default(); + + let data = vec![]; + let (bytes, _) = pallet_revive_fixtures::compile_module("dummy")?; + let input = bytes.into_iter().chain(data.clone()).collect::>(); + + println!("Account:"); + println!("- address: {:?}", account.address()); + println!("- substrate: {}", account.substrate_account()); + let client = HttpClientBuilder::default().build("http://localhost:8545")?; + + println!("\n\n=== Deploying contract ===\n\n"); + + let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; + let hash = + send_transaction(&account, &client, 5_000_000_000_000u128.into(), input.into(), None) + .await?; + + println!("Deploy Tx hash: {hash:?}"); + let ReceiptInfo { block_number, gas_used, contract_address, .. } = + wait_for_receipt(&client, hash).await?; + + let contract_address = contract_address.unwrap(); + assert_eq!(contract_address, create1(&account.address(), nonce.try_into().unwrap())); + + println!("Receipt:"); + println!("- Block number: {block_number}"); + println!("- Gas used: {gas_used}"); + println!("- Contract address: {contract_address:?}"); + let balance = client.get_balance(contract_address, BlockTag::Latest.into()).await?; + println!("- Contract balance: {balance:?}"); + + println!("\n\n=== Calling contract ===\n\n"); + let hash = + send_transaction(&account, &client, U256::zero(), Bytes::default(), Some(contract_address)) + .await?; + + println!("Contract call tx hash: {hash:?}"); + let ReceiptInfo { block_number, gas_used, to, .. } = wait_for_receipt(&client, hash).await?; + println!("Receipt:"); + println!("- Block number: {block_number}"); + println!("- Gas used: {gas_used}"); + println!("- To: {to:?}"); + Ok(()) +} diff --git a/substrate/frame/revive/rpc/examples/rust/extrinsic.rs b/substrate/frame/revive/rpc/examples/rust/extrinsic.rs new file mode 100644 index 000000000000..e15743e2385e --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rust/extrinsic.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use pallet_revive_eth_rpc::subxt_client::{ + self, revive::calls::types::InstantiateWithCode, SrcChainConfig, +}; +use sp_weights::Weight; +use subxt::OnlineClient; +use subxt_signer::sr25519::dev; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = OnlineClient::::new().await?; + + let (bytes, _) = pallet_revive_fixtures::compile_module("dummy")?; + + let tx_payload = subxt_client::tx().revive().instantiate_with_code( + 0u32.into(), + Weight::from_parts(100_000, 0).into(), + 3_000_000_000_000_000_000, + bytes, + vec![], + None, + ); + + let res = client + .tx() + .sign_and_submit_then_watch_default(&tx_payload, &dev::alice()) + .await? + .wait_for_finalized() + .await?; + println!("Transaction finalized: {:?}", res.extrinsic_hash()); + + let block_hash = res.block_hash(); + + let block = client.blocks().at(block_hash).await.unwrap(); + let extrinsics = block.extrinsics().await.unwrap(); + let _ = extrinsics.find_first::()?; + + Ok(()) +} diff --git a/substrate/frame/revive/rpc/examples/rust/remark-extrinsic.rs b/substrate/frame/revive/rpc/examples/rust/remark-extrinsic.rs new file mode 100644 index 000000000000..b106d27c218a --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rust/remark-extrinsic.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use pallet_revive_eth_rpc::subxt_client::{self, system::calls::types::Remark, SrcChainConfig}; +use subxt::OnlineClient; +use subxt_signer::sr25519::dev; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = OnlineClient::::new().await?; + let tx_payload = subxt_client::tx().system().remark(b"bonjour".to_vec()); + let res = client + .tx() + .sign_and_submit_then_watch_default(&tx_payload, &dev::alice()) + .await? + .wait_for_finalized() + .await?; + + println!("Transaction finalized: {:?}", res.extrinsic_hash()); + let block_hash = res.block_hash(); + let block = client.blocks().at(block_hash).await.unwrap(); + let extrinsics = block.extrinsics().await.unwrap(); + let remarks = extrinsics + .find::() + .map(|remark| remark.unwrap().value) + .collect::>(); + + dbg!(remarks); + Ok(()) +} diff --git a/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs b/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs new file mode 100644 index 000000000000..64175ca60b5f --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use jsonrpsee::http_client::HttpClientBuilder; +use pallet_revive::evm::{Account, BlockTag}; +use pallet_revive_eth_rpc::EthRpcClient; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let account = Account::default(); + println!("Account address: {:?}", account.address()); + + let client = HttpClientBuilder::default().build("http://localhost:8545")?; + + let block = client.get_block_by_number(BlockTag::Latest.into(), false).await?; + println!("Latest block: {block:#?}"); + + let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; + println!("Account nonce: {nonce:?}"); + + let balance = client.get_balance(account.address(), BlockTag::Latest.into()).await?; + println!("Account balance: {balance:?}"); + + let sync_state = client.syncing().await?; + println!("Sync state: {sync_state:?}"); + + Ok(()) +} diff --git a/substrate/frame/revive/rpc/examples/rust/transfer.rs b/substrate/frame/revive/rpc/examples/rust/transfer.rs new file mode 100644 index 000000000000..185ad808e787 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/rust/transfer.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use jsonrpsee::http_client::HttpClientBuilder; +use pallet_revive::evm::{Account, BlockTag, Bytes, ReceiptInfo}; +use pallet_revive_eth_rpc::{ + example::{send_transaction, wait_for_receipt}, + EthRpcClient, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let alith = Account::default(); + let client = HttpClientBuilder::default().build("http://localhost:8545")?; + + let baltathar = Account::from(subxt_signer::eth::dev::baltathar()); + let value = 1_000_000_000_000_000_000u128.into(); // 1 ETH + + let print_balance = || async { + let balance = client.get_balance(alith.address(), BlockTag::Latest.into()).await?; + println!("Alith {:?} balance: {balance:?}", alith.address()); + let balance = client.get_balance(baltathar.address(), BlockTag::Latest.into()).await?; + println!("Baltathar {:?} balance: {balance:?}", baltathar.address()); + anyhow::Result::<()>::Ok(()) + }; + + print_balance().await?; + println!("\n\n=== Transferring ===\n\n"); + + let hash = + send_transaction(&alith, &client, value, Bytes::default(), Some(baltathar.address())) + .await?; + println!("Transaction hash: {hash:?}"); + + let ReceiptInfo { block_number, gas_used, .. } = wait_for_receipt(&client, hash).await?; + println!("Receipt: "); + println!("- Block number: {block_number}"); + println!("- Gas used: {gas_used}"); + + print_balance().await?; + Ok(()) +} diff --git a/substrate/frame/revive/rpc/examples/westend_local_network.toml b/substrate/frame/revive/rpc/examples/westend_local_network.toml new file mode 100644 index 000000000000..28295db76133 --- /dev/null +++ b/substrate/frame/revive/rpc/examples/westend_local_network.toml @@ -0,0 +1,41 @@ +[settings] +node_spawn_timeout = 240 + +[relaychain] +default_command = "{{POLKADOT_BINARY}}" +default_args = ["-lparachain=debug,xcm=trace"] +chain = "westend-local" +[[relaychain.nodes]] +name = "alice-westend-validator" +validator = true +rpc_port = 9935 +ws_port = 9945 +balance = 2000000000000 + +[[relaychain.nodes]] +name = "bob-westend-validator" +validator = true +rpc_port = 9936 +ws_port = 9946 +balance = 2000000000000 + +[[parachains]] +id = 1000 +chain = "asset-hub-westend-local" +cumulus_based = true + +[[parachains.collators]] +name = "asset-hub-westend-collator1" +rpc_port = 9011 +ws_port = 9944 +command = "{{POLKADOT_PARACHAIN_BINARY}}" +args = [ + "-lparachain=debug,runtime::revive=debug", +] + +[[parachains.collators]] +name = "asset-hub-westend-collator2" +command = "{{POLKADOT_PARACHAIN_BINARY}}" +args = [ + "-lparachain=debug,runtime::revive=debug", +] diff --git a/substrate/frame/revive/rpc/revive_chain.metadata b/substrate/frame/revive/rpc/revive_chain.metadata new file mode 100644 index 0000000000000000000000000000000000000000..eef6dd2a0aeaa5b55ef9519aafb2301bd8cd51dc GIT binary patch literal 342591 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 XtqGG|`D)@FGI!*k7+yGXOYQ#ynerH6 literal 0 HcmV?d00001 diff --git a/substrate/frame/revive/rpc/src/cli.rs b/substrate/frame/revive/rpc/src/cli.rs new file mode 100644 index 000000000000..019eb624b99e --- /dev/null +++ b/substrate/frame/revive/rpc/src/cli.rs @@ -0,0 +1,131 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! The Ethereum JSON-RPC server. +use crate::{client::Client, EthRpcClient, EthRpcServer, EthRpcServerImpl, LOG_TARGET}; +use clap::Parser; +use hyper::Method; +use jsonrpsee::{ + http_client::HttpClientBuilder, + server::{RpcModule, Server}, +}; +use std::net::SocketAddr; +use tower_http::cors::{Any, CorsLayer}; + +// Parsed command instructions from the command line +#[derive(Parser)] +#[clap(author, about, version)] +pub struct CliCommand { + /// The server address to bind to + #[clap(long, default_value = "8545")] + pub rpc_port: String, + + /// The node url to connect to + #[clap(long, default_value = "ws://127.0.0.1:9944")] + pub node_rpc_url: String, +} + +/// Run the JSON-RPC server. +pub async fn run(cmd: CliCommand) -> anyhow::Result<()> { + let CliCommand { rpc_port, node_rpc_url } = cmd; + let client = Client::from_url(&node_rpc_url).await?; + let mut updates = client.updates.clone(); + + let server_addr = run_server(client, &format!("127.0.0.1:{rpc_port}")).await?; + log::info!("Running JSON-RPC server: addr={server_addr}"); + + let url = format!("http://{}", server_addr); + let client = HttpClientBuilder::default().build(url)?; + + let block_number = client.block_number().await?; + log::info!(target: LOG_TARGET, "Client initialized - Current 📦 block: #{block_number:?}"); + + // keep running server until ctrl-c or client subscription fails + let _ = updates.wait_for(|_| false).await; + Ok(()) +} + +#[cfg(feature = "dev")] +mod dev { + use crate::LOG_TARGET; + use futures::{future::BoxFuture, FutureExt}; + use jsonrpsee::{server::middleware::rpc::RpcServiceT, types::Request, MethodResponse}; + + /// Dev Logger middleware, that logs the method and params of the request, along with the + /// success of the response. + #[derive(Clone)] + pub struct DevLogger(pub S); + + impl<'a, S> RpcServiceT<'a> for DevLogger + where + S: RpcServiceT<'a> + Send + Sync + Clone + 'static, + { + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let service = self.0.clone(); + let method = req.method.clone(); + let params = req.params.clone().unwrap_or_default(); + + async move { + log::info!(target: LOG_TARGET, "Method: {method} params: {params}"); + let resp = service.call(req).await; + if resp.is_success() { + log::info!(target: LOG_TARGET, "✅ rpc: {method}"); + } else { + log::info!(target: LOG_TARGET, "❌ rpc: {method} {}", resp.as_result()); + } + resp + } + .boxed() + } + } +} + +/// Starts the rpc server and returns the server address. +async fn run_server(client: Client, url: &str) -> anyhow::Result { + let cors = CorsLayer::new() + .allow_methods([Method::POST]) + .allow_origin(Any) + .allow_headers([hyper::header::CONTENT_TYPE]); + let cors_middleware = tower::ServiceBuilder::new().layer(cors); + + let builder = Server::builder().set_http_middleware(cors_middleware); + + #[cfg(feature = "dev")] + let builder = builder + .set_rpc_middleware(jsonrpsee::server::RpcServiceBuilder::new().layer_fn(dev::DevLogger)); + + let server = builder.build(url.parse::()?).await?; + let addr = server.local_addr()?; + + let eth_api = EthRpcServerImpl::new(client) + .with_accounts(if cfg!(feature = "dev") { + use pallet_revive::evm::Account; + vec![Account::default()] + } else { + vec![] + }) + .into_rpc(); + + let mut module = RpcModule::new(()); + module.merge(eth_api)?; + + let handle = server.start(module); + tokio::spawn(handle.stopped()); + + Ok(addr) +} diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs new file mode 100644 index 000000000000..c707f298512c --- /dev/null +++ b/substrate/frame/revive/rpc/src/client.rs @@ -0,0 +1,799 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! The client connects to the source substrate chain +//! and is used by the rpc server to query and send transactions to the substrate chain. +use crate::{ + rlp, + runtime::GAS_PRICE, + subxt_client::{ + revive::calls::types::EthTransact, runtime_types::pallet_revive::storage::ContractInfo, + }, + TransactionLegacySigned, LOG_TARGET, +}; +use codec::Encode; +use futures::{stream, StreamExt}; +use jsonrpsee::types::{ErrorCode, ErrorObjectOwned}; +use pallet_revive::{ + create1, + evm::{ + Block, BlockNumberOrTag, BlockNumberOrTagOrHash, Bytes256, GenericTransaction, ReceiptInfo, + SyncingProgress, SyncingStatus, TransactionSigned, H160, H256, U256, + }, + EthContractResult, +}; +use sp_runtime::traits::{BlakeTwo256, Hash}; +use sp_weights::Weight; +use std::{ + collections::{HashMap, VecDeque}, + sync::Arc, + time::Duration, +}; +use subxt::{ + backend::{ + legacy::LegacyRpcMethods, + rpc::{ + reconnecting_rpc_client::{Client as ReconnectingRpcClient, ExponentialBackoff}, + RpcClient, + }, + }, + config::Header, + error::RpcError, + storage::Storage, + Config, OnlineClient, +}; +use subxt_client::transaction_payment::events::TransactionFeePaid; +use thiserror::Error; +use tokio::{ + sync::{watch::Sender, RwLock}, + task::JoinSet, +}; + +use crate::subxt_client::{self, system::events::ExtrinsicSuccess, SrcChainConfig}; + +/// The substrate block type. +pub type SubstrateBlock = subxt::blocks::Block>; + +/// The substrate block number type. +pub type SubstrateBlockNumber = <::Header as Header>::Number; + +/// The substrate block hash type. +pub type SubstrateBlockHash = ::Hash; + +/// Type alias for shared data. +pub type Shared = Arc>; + +/// The runtime balance type. +pub type Balance = u128; + +/// The cache maintains a buffer of the last N blocks, +#[derive(Default)] +struct BlockCache { + /// A double-ended queue of the last N blocks. + /// The most recent block is at the back of the queue, and the oldest block is at the front. + buffer: VecDeque>, + + /// A map of blocks by block number. + blocks_by_number: HashMap>, + + /// A map of blocks by block hash. + blocks_by_hash: HashMap>, + + /// A map of receipts by hash. + receipts_by_hash: HashMap, + + /// A map of Signed transaction by hash. + signed_tx_by_hash: HashMap, + + /// A map of receipt hashes by block hash. + tx_hashes_by_block_and_index: HashMap>, +} + +fn unwrap_subxt_err(err: &subxt::Error) -> String { + match err { + subxt::Error::Rpc(err) => unwrap_rpc_err(err), + _ => err.to_string(), + } +} + +fn unwrap_rpc_err(err: &subxt::error::RpcError) -> String { + match err { + subxt::error::RpcError::ClientError(err) => match err + // TODO use the re-export from subxt once available + .downcast_ref::() + { + Some(jsonrpsee::core::ClientError::Call(call_err)) => call_err.message().to_string(), + Some(other_err) => other_err.to_string(), + None => err.to_string(), + }, + _ => err.to_string(), + } +} + +/// The error type for the client. +#[derive(Error, Debug)] +pub enum ClientError { + /// A [`subxt::Error`] wrapper error. + #[error("{}",unwrap_subxt_err(.0))] + SubxtError(#[from] subxt::Error), + /// A [`RpcError`] wrapper error. + #[error("{}",unwrap_rpc_err(.0))] + RpcError(#[from] RpcError), + /// A [`codec::Error`] wrapper error. + #[error(transparent)] + CodecError(#[from] codec::Error), + /// The dry run failed. + #[error("Dry run failed")] + DryRunFailed, + /// A decimal conversion failed. + #[error("Conversion failed")] + ConversionFailed, + /// The block hash was not found. + #[error("Hash not found")] + BlockNotFound, + /// The transaction fee could not be found + #[error("TransactionFeePaid event not found")] + TxFeeNotFound, + /// The token decimals property was not found + #[error("tokenDecimals not found in properties")] + TokenDecimalsNotFound, + /// The cache is empty. + #[error("Cache is empty")] + CacheEmpty, +} + +const GENERIC_ERROR_CODE: ErrorCode = ErrorCode::ServerError(-32000); + +// Convert a `ClientError` to an RPC `ErrorObjectOwned`. +impl From for ErrorObjectOwned { + fn from(value: ClientError) -> Self { + log::debug!(target: LOG_TARGET, "ClientError: {value:?}"); + ErrorObjectOwned::owned::<()>(GENERIC_ERROR_CODE.code(), value.to_string(), None) + } +} + +/// The number of recent blocks maintained by the cache. +/// For each block in the cache, we also store the EVM transaction receipts. +pub const CACHE_SIZE: usize = 10; + +impl BlockCache { + fn latest_block(&self) -> Option<&Arc> { + self.buffer.back() + } + + /// Insert an entry into the cache, and prune the oldest entry if the cache is full. + fn insert(&mut self, block: SubstrateBlock) { + if self.buffer.len() >= N { + if let Some(block) = self.buffer.pop_front() { + log::trace!(target: LOG_TARGET, "Pruning block: {}", block.number()); + let hash = block.hash(); + self.blocks_by_hash.remove(&hash); + self.blocks_by_number.remove(&block.number()); + if let Some(entries) = self.tx_hashes_by_block_and_index.remove(&hash) { + for hash in entries.values() { + self.receipts_by_hash.remove(hash); + } + } + } + } + + let block = Arc::new(block); + self.buffer.push_back(block.clone()); + self.blocks_by_number.insert(block.number(), block.clone()); + self.blocks_by_hash.insert(block.hash(), block); + } +} + +/// A client connect to a node and maintains a cache of the last `CACHE_SIZE` blocks. +pub struct Client { + /// The inner state of the client. + inner: Arc, + // JoinSet to manage spawned tasks. + join_set: JoinSet>, + /// A watch channel to signal cache updates. + pub updates: tokio::sync::watch::Receiver<()>, +} + +/// The inner state of the client. +struct ClientInner { + api: OnlineClient, + rpc_client: ReconnectingRpcClient, + rpc: LegacyRpcMethods, + cache: Shared>, + chain_id: u64, + max_block_weight: Weight, + native_to_evm_ratio: U256, +} + +impl ClientInner { + /// Create a new client instance connecting to the substrate node at the given URL. + async fn from_url(url: &str) -> Result { + let rpc_client = ReconnectingRpcClient::builder() + .retry_policy(ExponentialBackoff::from_millis(100).max_delay(Duration::from_secs(10))) + .build(url.to_string()) + .await?; + + let api = OnlineClient::::from_rpc_client(rpc_client.clone()).await?; + let cache = Arc::new(RwLock::new(BlockCache::::default())); + + let rpc = LegacyRpcMethods::::new(RpcClient::new(rpc_client.clone())); + + let (native_to_evm_ratio, chain_id, max_block_weight) = + tokio::try_join!(native_to_evm_ratio(&rpc), chain_id(&api), max_block_weight(&api))?; + + Ok(Self { api, rpc_client, rpc, cache, chain_id, max_block_weight, native_to_evm_ratio }) + } + + /// Convert a native balance to an EVM balance. + fn native_to_evm_decimals(&self, value: U256) -> U256 { + value.saturating_mul(self.native_to_evm_ratio) + } + + /// Get the receipt infos from the extrinsics in a block. + async fn receipt_infos( + &self, + block: &SubstrateBlock, + ) -> Result, ClientError> { + // Get extrinsics from the block + let extrinsics = block.extrinsics().await?; + + // Filter extrinsics from pallet_revive + let extrinsics = extrinsics.iter().flat_map(|ext| { + let ext = ext.ok()?; + + let call = ext.as_extrinsic::().ok()??; + let tx = rlp::decode::(&call.payload).ok()?; + let from = tx.recover_eth_address().ok()?; + let contract_address = if tx.transaction_legacy_unsigned.to.is_none() { + Some(create1(&from, tx.transaction_legacy_unsigned.nonce.try_into().ok()?)) + } else { + None + }; + + Some((from, tx, contract_address, ext)) + }); + + // Map each extrinsic to a receipt + stream::iter(extrinsics) + .map(|(from, tx, contract_address, ext)| async move { + let events = ext.events().await?; + let tx_fees = + events.find_first::()?.ok_or(ClientError::TxFeeNotFound)?; + + let gas_price = tx.transaction_legacy_unsigned.gas_price; + let gas_used = (tx_fees.tip.saturating_add(tx_fees.actual_fee)) + .checked_div(gas_price.as_u128()) + .unwrap_or_default(); + + let success = events.find_first::().is_ok(); + let transaction_index = ext.index(); + let transaction_hash = BlakeTwo256::hash(&Vec::from(ext.bytes()).encode()); + let block_hash = block.hash(); + let block_number = block.number().into(); + + let receipt = ReceiptInfo { + block_hash, + block_number, + contract_address, + from, + to: tx.transaction_legacy_unsigned.to, + effective_gas_price: gas_price, + gas_used: gas_used.into(), + status: Some(if success { U256::one() } else { U256::zero() }), + transaction_hash, + transaction_index: transaction_index.into(), + ..Default::default() + }; + + Ok::<_, ClientError>((receipt.transaction_hash, (tx.into(), receipt))) + }) + .buffer_unordered(10) + .collect::>>() + .await + .into_iter() + .collect::, _>>() + } +} + +/// Drop all the tasks spawned by the client on drop. +impl Drop for Client { + fn drop(&mut self) { + self.join_set.abort_all() + } +} + +/// Fetch the chain ID from the substrate chain. +async fn chain_id(api: &OnlineClient) -> Result { + let query = subxt_client::constants().revive().chain_id(); + api.constants().at(&query).map_err(|err| err.into()) +} + +/// Fetch the max block weight from the substrate chain. +async fn max_block_weight(api: &OnlineClient) -> Result { + let query = subxt_client::constants().system().block_weights(); + let weights = api.constants().at(&query)?; + let max_block = weights.per_class.normal.max_extrinsic.unwrap_or(weights.max_block); + Ok(max_block.0) +} + +/// Fetch the native to EVM ratio from the substrate chain. +async fn native_to_evm_ratio(rpc: &LegacyRpcMethods) -> Result { + let props = rpc.system_properties().await?; + let eth_decimals = U256::from(18u32); + let native_decimals: U256 = props + .get("tokenDecimals") + .and_then(|v| v.as_number()?.as_u64()) + .ok_or(ClientError::TokenDecimalsNotFound)? + .into(); + + Ok(U256::from(10u32).pow(eth_decimals - native_decimals)) +} + +/// Extract the block timestamp. +async fn extract_block_timestamp(block: &SubstrateBlock) -> Option { + let extrinsics = block.extrinsics().await.ok()?; + let ext = extrinsics + .find_first::() + .ok()??; + + Some(ext.value.now / 1000) +} + +impl Client { + /// Create a new client instance. + /// The client will subscribe to new blocks and maintain a cache of [`CACHE_SIZE`] blocks. + pub async fn from_url(url: &str) -> Result { + log::info!(target: LOG_TARGET, "Connecting to node at: {url} ..."); + let inner: Arc = Arc::new(ClientInner::from_url(url).await?); + log::info!(target: LOG_TARGET, "Connected to node at: {url}"); + + let (tx, mut updates) = tokio::sync::watch::channel(()); + let mut join_set = JoinSet::new(); + join_set.spawn(Self::subscribe_blocks(inner.clone(), tx)); + join_set.spawn(Self::subscribe_reconnect(inner.clone())); + + updates.changed().await.expect("tx is not dropped"); + Ok(Self { inner, join_set, updates }) + } + + /// Expose the storage API. + async fn storage_api( + &self, + at: &BlockNumberOrTagOrHash, + ) -> Result>, ClientError> { + match at { + BlockNumberOrTagOrHash::U256(block_number) => { + let n: SubstrateBlockNumber = + (*block_number).try_into().map_err(|_| ClientError::ConversionFailed)?; + + let hash = self.get_block_hash(n).await?.ok_or(ClientError::BlockNotFound)?; + Ok(self.inner.api.storage().at(hash)) + }, + BlockNumberOrTagOrHash::H256(hash) => Ok(self.inner.api.storage().at(*hash)), + BlockNumberOrTagOrHash::BlockTag(_) => { + if let Some(block) = self.latest_block().await { + return Ok(self.inner.api.storage().at(block.hash())); + } + let storage = self.inner.api.storage().at_latest().await?; + Ok(storage) + }, + } + } + + /// Expose the runtime API. + async fn runtime_api( + &self, + at: &BlockNumberOrTagOrHash, + ) -> Result< + subxt::runtime_api::RuntimeApi>, + ClientError, + > { + match at { + BlockNumberOrTagOrHash::U256(block_number) => { + let n: SubstrateBlockNumber = + (*block_number).try_into().map_err(|_| ClientError::ConversionFailed)?; + + let hash = self.get_block_hash(n).await?.ok_or(ClientError::BlockNotFound)?; + Ok(self.inner.api.runtime_api().at(hash)) + }, + BlockNumberOrTagOrHash::H256(hash) => Ok(self.inner.api.runtime_api().at(*hash)), + BlockNumberOrTagOrHash::BlockTag(_) => { + if let Some(block) = self.latest_block().await { + return Ok(self.inner.api.runtime_api().at(block.hash())); + } + + let api = self.inner.api.runtime_api().at_latest().await?; + Ok(api) + }, + } + } + + /// Subscribe and log reconnection events. + async fn subscribe_reconnect(inner: Arc) -> Result<(), ClientError> { + let rpc = inner.as_ref().rpc_client.clone(); + loop { + let reconnected = rpc.reconnect_initiated().await; + log::info!(target: LOG_TARGET, "RPC client connection lost"); + let now = std::time::Instant::now(); + reconnected.await; + log::info!(target: LOG_TARGET, "RPC client reconnection took `{}s`", now.elapsed().as_secs()); + } + } + + /// Subscribe to new blocks and update the cache. + async fn subscribe_blocks(inner: Arc, tx: Sender<()>) -> Result<(), ClientError> { + log::info!(target: LOG_TARGET, "Subscribing to new blocks"); + let mut block_stream = + inner.as_ref().api.blocks().subscribe_best().await.inspect_err(|err| { + log::error!("Failed to subscribe to blocks: {err:?}"); + })?; + + while let Some(block) = block_stream.next().await { + let block = match block { + Ok(block) => block, + Err(err) => { + if err.is_disconnected_will_reconnect() { + log::warn!( + "The RPC connection was lost and we may have missed a few blocks" + ); + continue; + } + + log::error!("Failed to fetch block: {err:?}"); + return Err(err.into()); + }, + }; + + log::trace!(target: LOG_TARGET, "Pushing block: {}", block.number()); + let mut cache = inner.cache.write().await; + + let receipts = inner + .receipt_infos(&block) + .await + .inspect_err(|err| { + log::error!("Failed to get receipts: {err:?}"); + }) + .unwrap_or_default(); + + if !receipts.is_empty() { + log::debug!(target: LOG_TARGET, "Adding {} receipts", receipts.len()); + let values = receipts + .iter() + .map(|(hash, (_, receipt))| (receipt.transaction_index, *hash)) + .collect::>(); + + cache.tx_hashes_by_block_and_index.insert(block.hash(), values); + + cache + .receipts_by_hash + .extend(receipts.iter().map(|(hash, (_, receipt))| (*hash, receipt.clone()))); + + cache.signed_tx_by_hash.extend( + receipts.iter().map(|(hash, (signed_tx, _))| (*hash, signed_tx.clone())), + ) + } + + cache.insert(block); + tx.send_replace(()); + } + + log::info!(target: LOG_TARGET, "Block subscription ended"); + Ok(()) + } +} + +impl Client { + /// Get the most recent block stored in the cache. + pub async fn latest_block(&self) -> Option> { + let cache = self.inner.cache.read().await; + let block = cache.latest_block()?; + Some(block.clone()) + } + + /// Expose the transaction API. + pub async fn submit( + &self, + call: subxt::tx::DefaultPayload, + ) -> Result { + let ext = self.inner.api.tx().create_unsigned(&call).map_err(ClientError::from)?; + let hash = ext.submit().await?; + Ok(hash) + } + + /// Get an EVM transaction receipt by hash. + pub async fn receipt(&self, tx_hash: &H256) -> Option { + let cache = self.inner.cache.read().await; + cache.receipts_by_hash.get(tx_hash).cloned() + } + + /// Get the syncing status of the chain. + pub async fn syncing(&self) -> Result { + let health = self.inner.rpc.system_health().await?; + + let status = if health.is_syncing { + let client = RpcClient::new(self.inner.rpc_client.clone()); + let sync_state: sc_rpc::system::SyncState = + client.request("system_syncState", Default::default()).await?; + + SyncingProgress { + current_block: Some(sync_state.current_block.into()), + highest_block: Some(sync_state.highest_block.into()), + starting_block: Some(sync_state.starting_block.into()), + } + .into() + } else { + SyncingStatus::Bool(false) + }; + + Ok(status) + } + + /// Get an EVM transaction receipt by hash. + pub async fn receipt_by_hash_and_index( + &self, + block_hash: &H256, + transaction_index: &U256, + ) -> Option { + let cache = self.inner.cache.read().await; + let receipt_hash = + cache.tx_hashes_by_block_and_index.get(block_hash)?.get(transaction_index)?; + let receipt = cache.receipts_by_hash.get(receipt_hash)?; + Some(receipt.clone()) + } + + pub async fn signed_tx_by_hash(&self, tx_hash: &H256) -> Option { + let cache = self.inner.cache.read().await; + cache.signed_tx_by_hash.get(tx_hash).cloned() + } + + /// Get receipts count per block. + pub async fn receipts_count_per_block(&self, block_hash: &SubstrateBlockHash) -> Option { + let cache = self.inner.cache.read().await; + cache.tx_hashes_by_block_and_index.get(block_hash).map(|v| v.len()) + } + + /// Get the balance of the given address. + pub async fn balance( + &self, + address: H160, + at: &BlockNumberOrTagOrHash, + ) -> Result { + // TODO: remove once subxt is updated + let address = address.0.into(); + + let runtime_api = self.runtime_api(at).await?; + let payload = subxt_client::apis().revive_api().balance(address); + let balance = runtime_api.call(payload).await?.into(); + Ok(self.inner.native_to_evm_decimals(balance)) + } + + /// Get the contract storage for the given contract address and key. + pub async fn get_contract_storage( + &self, + contract_address: H160, + key: U256, + block: BlockNumberOrTagOrHash, + ) -> Result, ClientError> { + let runtime_api = self.runtime_api(&block).await?; + + // TODO: remove once subxt is updated + let contract_address = contract_address.0.into(); + + let payload = subxt_client::apis() + .revive_api() + .get_storage(contract_address, key.to_big_endian()); + let result = runtime_api.call(payload).await?.unwrap_or_default().unwrap_or_default(); + Ok(result) + } + + /// Get the contract code for the given contract address. + pub async fn get_contract_code( + &self, + contract_address: &H160, + block: BlockNumberOrTagOrHash, + ) -> Result, ClientError> { + let storage_api = self.storage_api(&block).await?; + + // TODO: remove once subxt is updated + let contract_address: subxt::utils::H160 = contract_address.0.into(); + + let query = subxt_client::storage().revive().contract_info_of(contract_address); + let Some(ContractInfo { code_hash, .. }) = storage_api.fetch(&query).await? else { + return Ok(Vec::new()); + }; + + let query = subxt_client::storage().revive().pristine_code(code_hash); + let result = storage_api.fetch(&query).await?.map(|v| v.0).unwrap_or_default(); + Ok(result) + } + + /// Dry run a transaction and returns the [`EthContractResult`] for the transaction. + pub async fn dry_run( + &self, + tx: &GenericTransaction, + block: BlockNumberOrTagOrHash, + ) -> Result, ClientError> { + let runtime_api = self.runtime_api(&block).await?; + let value = tx + .value + .unwrap_or_default() + .try_into() + .map_err(|_| ClientError::ConversionFailed)?; + + // TODO: remove once subxt is updated + let from = tx.from.map(|v| v.0.into()); + let to = tx.to.map(|v| v.0.into()); + + let payload = subxt_client::apis().revive_api().eth_transact( + from.unwrap_or_default(), + to, + value, + tx.input.clone().unwrap_or_default().0, + None, + None, + ); + let res = runtime_api.call(payload).await?.0; + Ok(res) + } + + /// Dry run a transaction and returns the gas estimate for the transaction. + pub async fn estimate_gas( + &self, + tx: &GenericTransaction, + block: BlockNumberOrTagOrHash, + ) -> Result { + let dry_run = self.dry_run(tx, block).await?; + Ok(U256::from(dry_run.fee / GAS_PRICE as u128) + GAS_PRICE) + } + + /// Get the nonce of the given address. + pub async fn nonce( + &self, + address: H160, + at: BlockNumberOrTagOrHash, + ) -> Result { + let address = address.0.into(); + + let runtime_api = self.runtime_api(&at).await?; + let payload = subxt_client::apis().revive_api().nonce(address); + let nonce = runtime_api.call(payload).await?; + Ok(nonce.into()) + } + + /// Get the block number of the latest block. + pub async fn block_number(&self) -> Result { + let cache = self.inner.cache.read().await; + let latest_block = cache.buffer.back().ok_or(ClientError::CacheEmpty)?; + Ok(latest_block.number()) + } + + /// Get a block hash for the given block number. + pub async fn get_block_hash( + &self, + block_number: SubstrateBlockNumber, + ) -> Result, ClientError> { + let cache = self.inner.cache.read().await; + if let Some(block) = cache.blocks_by_number.get(&block_number) { + return Ok(Some(block.hash())); + } + + let hash = self.inner.rpc.chain_get_block_hash(Some(block_number.into())).await?; + Ok(hash) + } + + /// Get a block for the specified hash or number. + pub async fn block_by_number_or_tag( + &self, + block: &BlockNumberOrTag, + ) -> Result>, ClientError> { + match block { + BlockNumberOrTag::U256(n) => { + let n = (*n).try_into().map_err(|_| ClientError::ConversionFailed)?; + self.block_by_number(n).await + }, + BlockNumberOrTag::BlockTag(_) => { + let cache = self.inner.cache.read().await; + Ok(cache.buffer.back().cloned()) + }, + } + } + + /// Get a block by hash + pub async fn block_by_hash( + &self, + hash: &SubstrateBlockHash, + ) -> Result>, ClientError> { + let cache = self.inner.cache.read().await; + if let Some(block) = cache.blocks_by_hash.get(hash) { + return Ok(Some(block.clone())); + } + + match self.inner.api.blocks().at(*hash).await { + Ok(block) => Ok(Some(Arc::new(block))), + Err(subxt::Error::Block(subxt::error::BlockError::NotFound(_))) => Ok(None), + Err(err) => Err(err.into()), + } + } + + /// Get a block by number + pub async fn block_by_number( + &self, + block_number: SubstrateBlockNumber, + ) -> Result>, ClientError> { + let cache = self.inner.cache.read().await; + if let Some(block) = cache.blocks_by_number.get(&block_number) { + return Ok(Some(block.clone())); + } + + let Some(hash) = self.get_block_hash(block_number).await? else { + return Ok(None); + }; + + self.block_by_hash(&hash).await + } + + /// Get the EVM block for the given hash. + pub async fn evm_block(&self, block: Arc) -> Result { + let runtime_api = self.inner.api.runtime_api().at(block.hash()); + let max_fee = Self::weight_to_fee(&runtime_api, self.max_block_weight()).await?; + let gas_limit = U256::from(max_fee / GAS_PRICE as u128); + + let header = block.header(); + let timestamp = extract_block_timestamp(&block).await.unwrap_or_default(); + + // TODO: remove once subxt is updated + let parent_hash = header.parent_hash.0.into(); + let state_root = header.state_root.0.into(); + let extrinsics_root = header.extrinsics_root.0.into(); + + Ok(Block { + hash: block.hash(), + parent_hash, + state_root, + transactions_root: extrinsics_root, + number: header.number.into(), + timestamp: timestamp.into(), + difficulty: Some(0u32.into()), + gas_limit, + logs_bloom: Bytes256([0u8; 256]), + receipts_root: extrinsics_root, + ..Default::default() + }) + } + + /// Convert a weight to a fee. + async fn weight_to_fee( + runtime_api: &subxt::runtime_api::RuntimeApi>, + weight: Weight, + ) -> Result { + let payload = subxt_client::apis() + .transaction_payment_api() + .query_weight_to_fee(weight.into()); + + let fee = runtime_api.call(payload).await?; + Ok(fee) + } + + /// Get the chain ID. + pub fn chain_id(&self) -> u64 { + self.inner.chain_id + } + + /// Get the Max Block Weight. + pub fn max_block_weight(&self) -> Weight { + self.inner.max_block_weight + } +} diff --git a/substrate/frame/revive/rpc/src/example.rs b/substrate/frame/revive/rpc/src/example.rs new file mode 100644 index 000000000000..cdf5ce9d1b98 --- /dev/null +++ b/substrate/frame/revive/rpc/src/example.rs @@ -0,0 +1,96 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Example utilities +#![cfg(any(feature = "example", test))] + +use crate::{EthRpcClient, ReceiptInfo}; +use anyhow::Context; +use pallet_revive::evm::{ + rlp::*, Account, BlockTag, Bytes, GenericTransaction, TransactionLegacyUnsigned, H160, H256, + U256, +}; + +/// Wait for a transaction receipt. +pub async fn wait_for_receipt( + client: &(impl EthRpcClient + Send + Sync), + hash: H256, +) -> anyhow::Result { + for _ in 0..30 { + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let receipt = client.get_transaction_receipt(hash).await?; + if let Some(receipt) = receipt { + return Ok(receipt) + } + } + + anyhow::bail!("Failed to get receipt") +} + +/// Send a transaction. +pub async fn send_transaction( + signer: &Account, + client: &(impl EthRpcClient + Send + Sync), + value: U256, + input: Bytes, + to: Option, +) -> anyhow::Result { + let from = signer.address(); + + let chain_id = Some(client.chain_id().await?); + + let gas_price = client.gas_price().await?; + let nonce = client + .get_transaction_count(from, BlockTag::Latest.into()) + .await + .with_context(|| "Failed to fetch account nonce")?; + + let gas = client + .estimate_gas( + GenericTransaction { + from: Some(from), + input: Some(input.clone()), + value: Some(value), + gas_price: Some(gas_price), + to, + ..Default::default() + }, + None, + ) + .await + .with_context(|| "Failed to fetch gas estimate")?; + + let unsigned_tx = TransactionLegacyUnsigned { + gas, + nonce, + to, + value, + input, + gas_price, + chain_id, + ..Default::default() + }; + + let tx = signer.sign_transaction(unsigned_tx.clone()); + let bytes = tx.rlp_bytes().to_vec(); + + let hash = client + .send_raw_transaction(bytes.clone().into()) + .await + .with_context(|| "transaction failed")?; + + Ok(hash) +} diff --git a/substrate/frame/revive/rpc/src/lib.rs b/substrate/frame/revive/rpc/src/lib.rs new file mode 100644 index 000000000000..a6d47063ef90 --- /dev/null +++ b/substrate/frame/revive/rpc/src/lib.rs @@ -0,0 +1,359 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! The [`EthRpcServer`] RPC server implementation +#![cfg_attr(docsrs, feature(doc_cfg))] + +use crate::runtime::GAS_PRICE; +use client::ClientError; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + types::{ErrorCode, ErrorObjectOwned}, +}; +use pallet_revive::{evm::*, EthContractResult}; +use sp_core::{H160, H256, U256}; +use thiserror::Error; + +pub mod cli; +pub mod client; +pub mod example; +pub mod subxt_client; + +#[cfg(test)] +mod tests; + +mod rpc_methods_gen; +pub use rpc_methods_gen::*; + +pub const LOG_TARGET: &str = "eth-rpc"; + +/// An EVM RPC server implementation. +pub struct EthRpcServerImpl { + /// The client used to interact with the substrate node. + client: client::Client, + + /// The accounts managed by the server. + accounts: Vec, +} + +impl EthRpcServerImpl { + /// Creates a new [`EthRpcServerImpl`]. + pub fn new(client: client::Client) -> Self { + Self { client, accounts: vec![] } + } + + /// Sets the accounts managed by the server. + pub fn with_accounts(mut self, accounts: Vec) -> Self { + self.accounts = accounts; + self + } +} + +/// The error type for the EVM RPC server. +#[derive(Error, Debug)] +pub enum EthRpcError { + /// A [`ClientError`] wrapper error. + #[error("Client error: {0}")] + ClientError(#[from] ClientError), + /// A [`rlp::DecoderError`] wrapper error. + #[error("Decoding error: {0}")] + RlpError(#[from] rlp::DecoderError), + /// A Decimals conversion error. + #[error("Conversion error")] + ConversionError, + /// An invalid signature error. + #[error("Invalid signature")] + InvalidSignature, + /// The account was not found at the given address + #[error("Account not found for address {0:?}")] + AccountNotFound(H160), + /// Received an invalid transaction + #[error("Invalid transaction")] + InvalidTransaction, + /// Received an invalid transaction + #[error("Invalid transaction {0:?}")] + TransactionTypeNotSupported(Byte), +} + +impl From for ErrorObjectOwned { + fn from(value: EthRpcError) -> Self { + let code = match value { + EthRpcError::ClientError(_) => ErrorCode::InternalError, + _ => ErrorCode::InvalidRequest, + }; + Self::owned::(code.code(), value.to_string(), None) + } +} + +#[async_trait] +impl EthRpcServer for EthRpcServerImpl { + async fn net_version(&self) -> RpcResult { + Ok(self.client.chain_id().to_string()) + } + + async fn syncing(&self) -> RpcResult { + Ok(self.client.syncing().await?) + } + + async fn block_number(&self) -> RpcResult { + let number = self.client.block_number().await?; + Ok(number.into()) + } + + async fn get_transaction_receipt( + &self, + transaction_hash: H256, + ) -> RpcResult> { + let receipt = self.client.receipt(&transaction_hash).await; + Ok(receipt) + } + + async fn estimate_gas( + &self, + transaction: GenericTransaction, + _block: Option, + ) -> RpcResult { + let result = self.client.estimate_gas(&transaction, BlockTag::Latest.into()).await?; + Ok(result) + } + + async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult { + let tx = rlp::decode::(&transaction.0).map_err(|err| { + log::debug!(target: LOG_TARGET, "Failed to decode transaction: {err:?}"); + EthRpcError::from(err) + })?; + + let eth_addr = tx.recover_eth_address().map_err(|err| { + log::debug!(target: LOG_TARGET, "Failed to recover eth address: {err:?}"); + EthRpcError::InvalidSignature + })?; + + // Dry run the transaction to get the weight limit and storage deposit limit + let TransactionLegacyUnsigned { to, input, value, .. } = tx.transaction_legacy_unsigned; + let dry_run = self + .client + .dry_run( + &GenericTransaction { + from: Some(eth_addr), + input: Some(input.clone()), + to, + value: Some(value), + ..Default::default() + }, + BlockTag::Latest.into(), + ) + .await?; + + let EthContractResult { gas_required, storage_deposit, .. } = dry_run; + let call = subxt_client::tx().revive().eth_transact( + transaction.0, + gas_required.into(), + storage_deposit, + ); + let hash = self.client.submit(call).await?; + Ok(hash) + } + + async fn send_transaction(&self, transaction: GenericTransaction) -> RpcResult { + log::debug!(target: LOG_TARGET, "{transaction:#?}"); + let GenericTransaction { from, gas, gas_price, input, to, value, r#type, .. } = transaction; + + let Some(from) = from else { + log::debug!(target: LOG_TARGET, "Transaction must have a sender"); + return Err(EthRpcError::InvalidTransaction.into()); + }; + + let account = self + .accounts + .iter() + .find(|account| account.address() == from) + .ok_or(EthRpcError::AccountNotFound(from))?; + + let gas_price = gas_price.unwrap_or_else(|| U256::from(GAS_PRICE)); + let chain_id = Some(self.client.chain_id().into()); + let input = input.unwrap_or_default(); + let value = value.unwrap_or_default(); + let r#type = r#type.unwrap_or_default(); + + let Some(gas) = gas else { + log::debug!(target: LOG_TARGET, "Transaction must have a gas limit"); + return Err(EthRpcError::InvalidTransaction.into()); + }; + + let r#type = Type0::try_from_byte(r#type.clone()) + .map_err(|_| EthRpcError::TransactionTypeNotSupported(r#type))?; + + let nonce = self.get_transaction_count(from, BlockTag::Latest.into()).await?; + + let tx = + TransactionLegacyUnsigned { chain_id, gas, gas_price, input, nonce, to, value, r#type }; + let tx = account.sign_transaction(tx); + let rlp_bytes = rlp::encode(&tx).to_vec(); + self.send_raw_transaction(Bytes(rlp_bytes)).await + } + + async fn get_block_by_hash( + &self, + block_hash: H256, + _hydrated_transactions: bool, + ) -> RpcResult> { + let Some(block) = self.client.block_by_hash(&block_hash).await? else { + return Ok(None); + }; + let block = self.client.evm_block(block).await?; + Ok(Some(block)) + } + + async fn get_balance(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult { + let balance = self.client.balance(address, &block).await?; + log::debug!(target: LOG_TARGET, "balance({address}): {balance:?}"); + Ok(balance) + } + + async fn chain_id(&self) -> RpcResult { + Ok(self.client.chain_id().into()) + } + + async fn gas_price(&self) -> RpcResult { + Ok(U256::from(GAS_PRICE)) + } + + async fn get_code(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult { + let code = self.client.get_contract_code(&address, block).await?; + Ok(code.into()) + } + + async fn accounts(&self) -> RpcResult> { + Ok(self.accounts.iter().map(|account| account.address()).collect()) + } + + async fn call( + &self, + transaction: GenericTransaction, + block: Option, + ) -> RpcResult { + let dry_run = self + .client + .dry_run(&transaction, block.unwrap_or_else(|| BlockTag::Latest.into())) + .await?; + let output = dry_run.result.map_err(|err| { + log::debug!(target: LOG_TARGET, "Dry run failed: {err:?}"); + ClientError::DryRunFailed + })?; + + Ok(output.into()) + } + + async fn get_block_by_number( + &self, + block: BlockNumberOrTag, + _hydrated_transactions: bool, + ) -> RpcResult> { + let Some(block) = self.client.block_by_number_or_tag(&block).await? else { + return Ok(None); + }; + let block = self.client.evm_block(block).await?; + Ok(Some(block)) + } + + async fn get_block_transaction_count_by_hash( + &self, + block_hash: Option, + ) -> RpcResult> { + let block_hash = if let Some(block_hash) = block_hash { + block_hash + } else { + self.client.latest_block().await.ok_or(ClientError::BlockNotFound)?.hash() + }; + Ok(self.client.receipts_count_per_block(&block_hash).await.map(U256::from)) + } + + async fn get_block_transaction_count_by_number( + &self, + block: Option, + ) -> RpcResult> { + let Some(block) = self + .get_block_by_number(block.unwrap_or_else(|| BlockTag::Latest.into()), false) + .await? + else { + return Ok(None); + }; + + Ok(self.client.receipts_count_per_block(&block.hash).await.map(U256::from)) + } + + async fn get_storage_at( + &self, + address: H160, + storage_slot: U256, + block: BlockNumberOrTagOrHash, + ) -> RpcResult { + let bytes = self.client.get_contract_storage(address, storage_slot, block).await?; + Ok(bytes.into()) + } + + async fn get_transaction_by_block_hash_and_index( + &self, + block_hash: H256, + transaction_index: U256, + ) -> RpcResult> { + let Some(receipt) = + self.client.receipt_by_hash_and_index(&block_hash, &transaction_index).await + else { + return Ok(None); + }; + + let Some(signed_tx) = self.client.signed_tx_by_hash(&receipt.transaction_hash).await else { + return Ok(None); + }; + + Ok(Some(TransactionInfo::new(receipt, signed_tx))) + } + + async fn get_transaction_by_block_number_and_index( + &self, + block: BlockNumberOrTag, + transaction_index: U256, + ) -> RpcResult> { + let Some(block) = self.client.block_by_number_or_tag(&block).await? else { + return Ok(None); + }; + self.get_transaction_by_block_hash_and_index(block.hash(), transaction_index) + .await + } + + async fn get_transaction_by_hash( + &self, + transaction_hash: H256, + ) -> RpcResult> { + let receipt = self.client.receipt(&transaction_hash).await; + let signed_tx = self.client.signed_tx_by_hash(&transaction_hash).await; + if let (Some(receipt), Some(signed_tx)) = (receipt, signed_tx) { + return Ok(Some(TransactionInfo::new(receipt, signed_tx))); + } + + Ok(None) + } + + async fn get_transaction_count( + &self, + address: H160, + block: BlockNumberOrTagOrHash, + ) -> RpcResult { + let nonce = self.client.nonce(address, block).await?; + Ok(nonce) + } +} diff --git a/substrate/frame/revive/rpc/src/main.rs b/substrate/frame/revive/rpc/src/main.rs new file mode 100644 index 000000000000..b1306ad096b0 --- /dev/null +++ b/substrate/frame/revive/rpc/src/main.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! The Ethereum JSON-RPC server. +use clap::Parser; +use pallet_revive_eth_rpc::cli; +use tracing_subscriber::{util::SubscriberInitExt, EnvFilter, FmtSubscriber}; + +/// Initialize tracing +fn init_tracing() { + let env_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("eth_rpc=trace")); + + FmtSubscriber::builder() + .with_env_filter(env_filter) + .finish() + .try_init() + .expect("failed to initialize tracing"); +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + init_tracing(); + let cmd = cli::CliCommand::parse(); + cli::run(cmd).await +} diff --git a/substrate/frame/revive/rpc/src/rpc_methods_gen.rs b/substrate/frame/revive/rpc/src/rpc_methods_gen.rs new file mode 100644 index 000000000000..339080368969 --- /dev/null +++ b/substrate/frame/revive/rpc/src/rpc_methods_gen.rs @@ -0,0 +1,160 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Generated JSON-RPC methods. +#![allow(missing_docs)] + +use super::*; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; + +#[rpc(server, client)] +pub trait EthRpc { + /// Returns a list of addresses owned by client. + #[method(name = "eth_accounts")] + async fn accounts(&self) -> RpcResult>; + + /// Returns the number of most recent block. + #[method(name = "eth_blockNumber")] + async fn block_number(&self) -> RpcResult; + + /// Executes a new message call immediately without creating a transaction on the block chain. + #[method(name = "eth_call")] + async fn call( + &self, + transaction: GenericTransaction, + block: Option, + ) -> RpcResult; + + /// Returns the chain ID of the current network. + #[method(name = "eth_chainId")] + async fn chain_id(&self) -> RpcResult; + + /// Generates and returns an estimate of how much gas is necessary to allow the transaction to + /// complete. + #[method(name = "eth_estimateGas")] + async fn estimate_gas( + &self, + transaction: GenericTransaction, + block: Option, + ) -> RpcResult; + + /// Returns the current price per gas in wei. + #[method(name = "eth_gasPrice")] + async fn gas_price(&self) -> RpcResult; + + /// Returns the balance of the account of given address. + #[method(name = "eth_getBalance")] + async fn get_balance(&self, address: Address, block: BlockNumberOrTagOrHash) + -> RpcResult; + + /// Returns information about a block by hash. + #[method(name = "eth_getBlockByHash")] + async fn get_block_by_hash( + &self, + block_hash: H256, + hydrated_transactions: bool, + ) -> RpcResult>; + + /// Returns information about a block by number. + #[method(name = "eth_getBlockByNumber")] + async fn get_block_by_number( + &self, + block: BlockNumberOrTag, + hydrated_transactions: bool, + ) -> RpcResult>; + + /// Returns the number of transactions in a block from a block matching the given block hash. + #[method(name = "eth_getBlockTransactionCountByHash")] + async fn get_block_transaction_count_by_hash( + &self, + block_hash: Option, + ) -> RpcResult>; + + /// Returns the number of transactions in a block matching the given block number. + #[method(name = "eth_getBlockTransactionCountByNumber")] + async fn get_block_transaction_count_by_number( + &self, + block: Option, + ) -> RpcResult>; + + /// Returns code at a given address. + #[method(name = "eth_getCode")] + async fn get_code(&self, address: Address, block: BlockNumberOrTagOrHash) -> RpcResult; + + /// Returns the value from a storage position at a given address. + #[method(name = "eth_getStorageAt")] + async fn get_storage_at( + &self, + address: Address, + storage_slot: U256, + block: BlockNumberOrTagOrHash, + ) -> RpcResult; + + /// Returns information about a transaction by block hash and transaction index position. + #[method(name = "eth_getTransactionByBlockHashAndIndex")] + async fn get_transaction_by_block_hash_and_index( + &self, + block_hash: H256, + transaction_index: U256, + ) -> RpcResult>; + + /// Returns information about a transaction by block number and transaction index position. + #[method(name = "eth_getTransactionByBlockNumberAndIndex")] + async fn get_transaction_by_block_number_and_index( + &self, + block: BlockNumberOrTag, + transaction_index: U256, + ) -> RpcResult>; + + /// Returns the information about a transaction requested by transaction hash. + #[method(name = "eth_getTransactionByHash")] + async fn get_transaction_by_hash( + &self, + transaction_hash: H256, + ) -> RpcResult>; + + /// Returns the number of transactions sent from an address. + #[method(name = "eth_getTransactionCount")] + async fn get_transaction_count( + &self, + address: Address, + block: BlockNumberOrTagOrHash, + ) -> RpcResult; + + /// Returns the receipt of a transaction by transaction hash. + #[method(name = "eth_getTransactionReceipt")] + async fn get_transaction_receipt( + &self, + transaction_hash: H256, + ) -> RpcResult>; + + /// Submits a raw transaction. For EIP-4844 transactions, the raw form must be the network form. + /// This means it includes the blobs, KZG commitments, and KZG proofs. + #[method(name = "eth_sendRawTransaction")] + async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult; + + /// Signs and submits a transaction. + #[method(name = "eth_sendTransaction")] + async fn send_transaction(&self, transaction: GenericTransaction) -> RpcResult; + + /// Returns an object with data about the sync status or false. + #[method(name = "eth_syncing")] + async fn syncing(&self) -> RpcResult; + + /// The string value of current network id + #[method(name = "net_version")] + async fn net_version(&self) -> RpcResult; +} diff --git a/substrate/frame/revive/rpc/src/subxt_client.rs b/substrate/frame/revive/rpc/src/subxt_client.rs new file mode 100644 index 000000000000..cb2737beae70 --- /dev/null +++ b/substrate/frame/revive/rpc/src/subxt_client.rs @@ -0,0 +1,71 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! The generated subxt client. +//! Generated against a substrate chain configured with [`pallet_revive`] using: +//! subxt metadata --url ws://localhost:9944 -o rpc/revive_chain.scale +use subxt::config::{signed_extensions, Config, PolkadotConfig}; + +#[subxt::subxt( + runtime_metadata_path = "revive_chain.metadata", + substitute_type( + path = "pallet_revive::primitives::EthContractResult", + with = "::subxt::utils::Static<::pallet_revive::EthContractResult>" + ), + substitute_type( + path = "sp_weights::weight_v2::Weight", + with = "::subxt::utils::Static<::sp_weights::Weight>" + ) +)] +mod src_chain {} +pub use src_chain::*; + +/// The configuration for the source chain. +pub enum SrcChainConfig {} +impl Config for SrcChainConfig { + type Hash = sp_core::H256; + type AccountId = ::AccountId; + type Address = ::Address; + type Signature = ::Signature; + type Hasher = BlakeTwo256; + type Header = subxt::config::substrate::SubstrateHeader; + type AssetId = ::AssetId; + type ExtrinsicParams = signed_extensions::AnyOf< + Self, + ( + signed_extensions::CheckSpecVersion, + signed_extensions::CheckTxVersion, + signed_extensions::CheckNonce, + signed_extensions::CheckGenesis, + signed_extensions::CheckMortality, + signed_extensions::ChargeAssetTxPayment, + signed_extensions::ChargeTransactionPayment, + signed_extensions::CheckMetadataHash, + ), + >; +} + +/// A type that can hash values using the blaks2_256 algorithm. +/// TODO remove once subxt is updated +#[derive(Debug, Clone, Copy, PartialEq, Eq, codec::Encode)] +pub struct BlakeTwo256; + +impl subxt::config::Hasher for BlakeTwo256 { + type Output = sp_core::H256; + fn hash(s: &[u8]) -> Self::Output { + sp_crypto_hashing::blake2_256(s).into() + } +} diff --git a/substrate/frame/revive/rpc/src/tests.rs b/substrate/frame/revive/rpc/src/tests.rs new file mode 100644 index 000000000000..8b618469f25d --- /dev/null +++ b/substrate/frame/revive/rpc/src/tests.rs @@ -0,0 +1,103 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Test the eth-rpc cli with the kitchensink node. + +// We require the `riscv` feature to get access to the compiled fixtures. +#![cfg(feature = "riscv")] +use crate::{ + cli, + example::{send_transaction, wait_for_receipt}, + EthRpcClient, +}; +use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; +use pallet_revive::{ + create1, + evm::{Account, BlockTag, Bytes, U256}, +}; +use std::thread; +use substrate_cli_test_utils::*; + +/// Create a websocket client with a 30s timeout. +async fn ws_client_with_retry(url: &str) -> WsClient { + let timeout = tokio::time::Duration::from_secs(30); + tokio::time::timeout(timeout, async { + loop { + if let Ok(client) = WsClientBuilder::default().build(url).await { + return client + } else { + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + } + }) + .await + .expect("Hit timeout") +} + +#[tokio::test] +async fn test_jsonrpsee_server() -> anyhow::Result<()> { + // Start the node. + let _ = thread::spawn(move || match start_node_inline(vec!["--dev", "--rpc-port=45789"]) { + Ok(_) => {}, + Err(e) => { + panic!("Node exited with error: {}", e); + }, + }); + + // Start the rpc server. + tokio::spawn(cli::run(cli::CliCommand { + rpc_port: "45788".to_string(), + node_rpc_url: "ws://localhost:45789".to_string(), + })); + + let client = ws_client_with_retry("ws://localhost:45788").await; + let account = Account::default(); + + // Deploy contract + let data = b"hello world".to_vec(); + let value = U256::from(5_000_000_000_000u128); + let (bytes, _) = pallet_revive_fixtures::compile_module("dummy")?; + let input = bytes.into_iter().chain(data.clone()).collect::>(); + let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; + let hash = send_transaction(&account, &client, value, input.into(), None).await?; + let receipt = wait_for_receipt(&client, hash).await?; + let contract_address = create1(&account.address(), nonce.try_into().unwrap()); + assert_eq!( + Some(contract_address), + receipt.contract_address, + "Contract should be deployed with the correct address." + ); + + let balance = client.get_balance(contract_address, BlockTag::Latest.into()).await?; + assert_eq!( + value * 1_000_000, + balance, + "Contract balance should be the same as the value sent." + ); + + // Call contract + let hash = + send_transaction(&account, &client, U256::zero(), Bytes::default(), Some(contract_address)) + .await?; + let receipt = wait_for_receipt(&client, hash).await?; + assert_eq!( + Some(contract_address), + receipt.to, + "Receipt should have the correct contract address." + ); + + Ok(()) +} diff --git a/substrate/frame/revive/src/evm/api/account.rs b/substrate/frame/revive/src/evm/api/account.rs index c2217defc31f..06fb6e7e9c21 100644 --- a/substrate/frame/revive/src/evm/api/account.rs +++ b/substrate/frame/revive/src/evm/api/account.rs @@ -20,6 +20,7 @@ use crate::{ H160, }; use rlp::Encodable; +use sp_runtime::AccountId32; /// A simple account that can sign transactions pub struct Account(subxt_signer::eth::Keypair); @@ -42,6 +43,14 @@ impl Account { H160::from_slice(&self.0.account_id().as_ref()) } + /// Get the substrate [`AccountId32`] of the account. + pub fn substrate_account(&self) -> AccountId32 { + let mut account_id = AccountId32::new([0xEE; 32]); + >::as_mut(&mut account_id)[..20] + .copy_from_slice(self.address().as_ref()); + account_id + } + /// Sign a transaction. pub fn sign_transaction(&self, tx: TransactionLegacyUnsigned) -> TransactionLegacySigned { let rlp_encoded = tx.rlp_bytes(); diff --git a/substrate/frame/revive/src/evm/api/rpc_types_gen.rs b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs index e4663a82232c..1f391ae846a5 100644 --- a/substrate/frame/revive/src/evm/api/rpc_types_gen.rs +++ b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs @@ -17,7 +17,7 @@ //! Generated JSON-RPC types. #![allow(missing_docs)] -use super::{byte::*, Type0, Type1, Type2}; +use super::{byte::*, Type0, Type1, Type2, Type3}; use alloc::vec::Vec; use codec::{Decode, Encode}; use derive_more::{From, TryInto}; @@ -531,7 +531,7 @@ pub struct Transaction4844Unsigned { /// to address pub to: Address, /// type - pub r#type: Byte, + pub r#type: Type3, /// value pub value: U256, } diff --git a/substrate/frame/revive/src/evm/api/type_id.rs b/substrate/frame/revive/src/evm/api/type_id.rs index 7d75d53500b6..7434ca6e9b7f 100644 --- a/substrate/frame/revive/src/evm/api/type_id.rs +++ b/substrate/frame/revive/src/evm/api/type_id.rs @@ -17,6 +17,7 @@ //! Ethereum Typed Transaction types use super::Byte; use codec::{Decode, Encode}; +use rlp::Decodable; use scale_info::TypeInfo; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -29,6 +30,11 @@ macro_rules! transaction_type { pub struct $name; impl $name { + /// Get the value of the type + pub fn value(&self) -> u8 { + $value + } + /// Convert to Byte pub fn as_byte(&self) -> Byte { Byte::from($value) @@ -44,6 +50,17 @@ macro_rules! transaction_type { } } + impl Decodable for $name { + fn decode(rlp: &rlp::Rlp) -> Result { + let value: u8 = rlp.as_val()?; + if value == $value { + Ok(Self {}) + } else { + Err(rlp::DecoderError::Custom(concat!("expected ", $value))) + } + } + } + impl Encode for $name { fn using_encoded R>(&self, f: F) -> R { f(&[$value]) @@ -93,3 +110,4 @@ macro_rules! transaction_type { transaction_type!(Type0, 0); transaction_type!(Type1, 1); transaction_type!(Type2, 2); +transaction_type!(Type3, 3); diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 80406a49bca2..024f0750d2ae 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -48,7 +48,7 @@ type CallOf = ::RuntimeCall; /// We use a fixed value for the gas price. /// This let us calculate the gas estimate for a transaction with the formula: /// `estimate_gas = substrate_fee / gas_price`. -pub const GAS_PRICE: u32 = 1_000u32; +pub const GAS_PRICE: u32 = 1u32; /// Wraps [`generic::UncheckedExtrinsic`] to support checking unsigned /// [`crate::Call::eth_transact`] extrinsic. diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 1967f9868d2b..164ffcf7a495 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -1453,12 +1453,19 @@ environmental!(executing_contract: bool); sp_api::decl_runtime_apis! { /// The API used to dry-run contract interactions. #[api_version(1)] - pub trait ReviveApi where + pub trait ReviveApi where AccountId: Codec, Balance: Codec, + Nonce: Codec, BlockNumber: Codec, EventRecord: Codec, { + /// Returns the free balance of the given `[H160]` address. + fn balance(address: H160) -> Balance; + + /// Returns the nonce of the given `[H160]` address. + fn nonce(address: H160) -> Nonce; + /// Perform a call from a specified account to a given contract. /// /// See [`crate::Pallet::bare_call`]. diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 370673622b95..28d6a2c3fb01 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -605,12 +605,13 @@ runtime = [ "sp-wasm-interface", "sp-weights", ] -node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-approval-voting-parallel", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-omni-node-lib", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-eth-rpc", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-approval-voting-parallel", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-omni-node-lib", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", "frame-support?/tuples-96", ] riscv = [ + "pallet-revive-eth-rpc?/riscv", "pallet-revive-fixtures?/riscv", "pallet-revive-mock-network?/riscv", "pallet-revive?/riscv", @@ -1902,6 +1903,11 @@ path = "../substrate/frame/contracts/mock-network" default-features = false optional = true +[dependencies.pallet-revive-eth-rpc] +path = "../substrate/frame/revive/rpc" +default-features = false +optional = true + [dependencies.pallet-revive-mock-network] path = "../substrate/frame/revive/mock-network" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index f3fc949c66ec..2216864fad0f 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -580,6 +580,10 @@ pub use pallet_remark; #[cfg(feature = "pallet-revive")] pub use pallet_revive; +/// An Ethereum JSON-RPC server for pallet-revive. +#[cfg(feature = "pallet-revive-eth-rpc")] +pub use pallet_revive_eth_rpc; + /// Fixtures for testing and benchmarking. #[cfg(feature = "pallet-revive-fixtures")] pub use pallet_revive_fixtures; From a3b51c4e454355b1547c1fbbb89090b591620b67 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 28 Oct 2024 21:28:47 +0100 Subject: [PATCH 50/58] [pallet-revive] Update typeInfo (#6263) Update typeinfo impl to make it transparent for subxt see https://github.com/paritytech/subxt/pull/1845 --------- Co-authored-by: GitHub Action --- prdoc/pr_6263.prdoc | 10 ++++++++++ substrate/frame/revive/src/evm/runtime.rs | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_6263.prdoc diff --git a/prdoc/pr_6263.prdoc b/prdoc/pr_6263.prdoc new file mode 100644 index 000000000000..1b1da78c85a2 --- /dev/null +++ b/prdoc/pr_6263.prdoc @@ -0,0 +1,10 @@ +title: '[pallet-revive] Update typeInfo' +doc: +- audience: Runtime Dev + description: |- + Update typeinfo impl to make it transparent for subxt + + see https://github.com/paritytech/subxt/pull/1845 +crates: +- name: pallet-revive + bump: minor diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 024f0750d2ae..bb076da3b3a6 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -25,7 +25,7 @@ use frame_support::{ traits::{ExtrinsicCall, InherentBuilder, SignedTransactionBuilder}, }; use pallet_transaction_payment::OnChargeTransaction; -use scale_info::TypeInfo; +use scale_info::{StaticTypeInfo, TypeInfo}; use sp_arithmetic::Percent; use sp_core::{Get, U256}; use sp_runtime::{ @@ -52,12 +52,24 @@ pub const GAS_PRICE: u32 = 1u32; /// Wraps [`generic::UncheckedExtrinsic`] to support checking unsigned /// [`crate::Call::eth_transact`] extrinsic. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -#[scale_info(skip_type_params(E))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct UncheckedExtrinsic( pub generic::UncheckedExtrinsic, Signature, E::Extension>, ); +impl TypeInfo for UncheckedExtrinsic +where + Address: StaticTypeInfo, + Signature: StaticTypeInfo, + E::Extension: StaticTypeInfo, +{ + type Identity = + generic::UncheckedExtrinsic, Signature, E::Extension>; + fn type_info() -> scale_info::Type { + generic::UncheckedExtrinsic::, Signature, E::Extension>::type_info() + } +} + impl From, Signature, E::Extension>> for UncheckedExtrinsic From e9184b60c397fa4c4390b586e7af27100746c5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 28 Oct 2024 23:42:01 +0100 Subject: [PATCH 51/58] pallet-revive: Trade code size for call stack depth (#6264) This will reduce the call stack depth in order to raise the allowed code size. Should allow around 100KB of instructions. This is necessary to stay within the memory envelope. More code size is more appropriate for testing right now. We will re-evaluate parameters once we have 64bit support. --------- Co-authored-by: GitHub Action --- prdoc/pr_6264.prdoc | 12 ++++++++++++ .../revive/fixtures/contracts/oom_rw_trailing.rs | 2 +- substrate/frame/revive/src/limits.rs | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_6264.prdoc diff --git a/prdoc/pr_6264.prdoc b/prdoc/pr_6264.prdoc new file mode 100644 index 000000000000..59bdd9da7fac --- /dev/null +++ b/prdoc/pr_6264.prdoc @@ -0,0 +1,12 @@ +title: 'pallet-revive: Trade code size for call stack depth' +doc: +- audience: Runtime Dev + description: This will reduce the call stack depth in order to raise the allowed + code size. Should allow around 100KB of instructions. This is necessary to stay + within the memory envelope. More code size is more appropriate for testing right + now. We will re-evaluate parameters once we have 64bit support. +crates: +- name: pallet-revive-fixtures + bump: major +- name: pallet-revive + bump: major diff --git a/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs index 993be8e9cda4..ddd4139db3ed 100644 --- a/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs +++ b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs @@ -26,7 +26,7 @@ extern crate common; use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; -static mut BUFFER: [u8; 1025 * 1024] = [0; 1025 * 1024]; +static mut BUFFER: [u8; 2 * 1025 * 1024] = [0; 2 * 1025 * 1024]; #[no_mangle] #[polkavm_derive::polkavm_export] diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index c6d5ef8d8b1b..0695590f5379 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -36,7 +36,7 @@ /// /// A 0 means that no callings of other contracts are possible. In other words only the origin /// called "root contract" is allowed to execute then. -pub const CALL_STACK_DEPTH: u32 = 10; +pub const CALL_STACK_DEPTH: u32 = 5; /// The maximum number of topics a call to [`crate::SyscallDoc::deposit_event`] can emit. /// @@ -97,7 +97,7 @@ pub mod code { /// for more code or more data. However, since code will decompress /// into a bigger representation on compilation it will only increase /// the allowed code size by [`BYTE_PER_INSTRUCTION`]. - pub const STATIC_MEMORY_BYTES: u32 = 1024 * 1024; + pub const STATIC_MEMORY_BYTES: u32 = 2 * 1024 * 1024; /// How much memory each instruction will take in-memory after compilation. /// From ecc9520c9f8d359f5c654c598d45d920d9eb207a Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Tue, 29 Oct 2024 09:52:00 +0100 Subject: [PATCH 52/58] [pallet-revive] implement tx origin API (#6105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a syscall to retreive the transaction origin. --------- Signed-off-by: Cyrill Leutwiler Signed-off-by: xermicus Co-authored-by: GitHub Action Co-authored-by: command-bot <> Co-authored-by: Alexander Theißen --- prdoc/pr_6105.prdoc | 14 + .../frame/revive/fixtures/contracts/origin.rs | 62 ++ .../frame/revive/src/benchmarking/mod.rs | 18 + substrate/frame/revive/src/exec.rs | 72 ++ substrate/frame/revive/src/tests.rs | 16 + substrate/frame/revive/src/wasm/runtime.rs | 18 + substrate/frame/revive/src/weights.rs | 903 +++++++++--------- substrate/frame/revive/uapi/src/host.rs | 11 + .../frame/revive/uapi/src/host/riscv32.rs | 3 +- 9 files changed, 670 insertions(+), 447 deletions(-) create mode 100644 prdoc/pr_6105.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/origin.rs diff --git a/prdoc/pr_6105.prdoc b/prdoc/pr_6105.prdoc new file mode 100644 index 000000000000..f8339c6ce535 --- /dev/null +++ b/prdoc/pr_6105.prdoc @@ -0,0 +1,14 @@ +title: '[pallet-revive] implement tx origin API' + +doc: +- audience: + - Runtime Dev + description: Implement a syscall to retreive the transaction origin. + +crates: +- name: pallet-revive + bump: minor +- name: pallet-revive-uapi + bump: minor +- name: pallet-revive-fixtures + bump: patch diff --git a/substrate/frame/revive/fixtures/contracts/origin.rs b/substrate/frame/revive/fixtures/contracts/origin.rs new file mode 100644 index 000000000000..8e9afd8e8052 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/origin.rs @@ -0,0 +1,62 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests that the `origin` syscall works. +//! The fixture returns the observed origin if the caller is not the origin, +//! otherwise call itself recursively and assert the returned origin to match. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut caller = [0; 20]; + api::caller(&mut caller); + + let mut origin = [0; 20]; + api::origin(&mut origin); + + if caller != origin { + api::return_value(Default::default(), &origin); + } + + let mut addr = [0u8; 20]; + api::address(&mut addr); + + let mut buf = [0u8; 20]; + api::call( + uapi::CallFlags::ALLOW_REENTRY, + &addr, + 0u64, + 0u64, + None, + &[0; 32], + &[], + Some(&mut &mut buf[..]), + ) + .unwrap(); + + assert_eq!(buf, origin); +} diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 8c9bf2cf70f0..bf27c660e1a4 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -523,6 +523,24 @@ mod benchmarks { ); } + #[benchmark(pov_mode = Measured)] + fn seal_origin() { + let len = H160::len_bytes(); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_origin(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[..]).unwrap(), + T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap()) + ); + } + #[benchmark(pov_mode = Measured)] fn seal_is_contract() { let Contract { account_id, .. } = diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 9f3a75c0090d..8b9fd660296e 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -284,6 +284,9 @@ pub trait Ext: sealing::Sealed { /// Returns the caller. fn caller(&self) -> Origin; + /// Return the origin of the whole call stack. + fn origin(&self) -> &Origin; + /// Check if a contract lives at the specified `address`. fn is_contract(&self, address: &H160) -> bool; @@ -1547,6 +1550,10 @@ where } } + fn origin(&self) -> &Origin { + &self.origin + } + fn is_contract(&self, address: &H160) -> bool { ContractInfoOf::::contains_key(&address) } @@ -2381,6 +2388,71 @@ mod tests { assert_eq!(WitnessedCallerCharlie::get(), Some(BOB_ADDR)); } + #[test] + fn origin_returns_proper_values() { + parameter_types! { + static WitnessedCallerBob: Option = None; + static WitnessedCallerCharlie: Option = None; + } + + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Record the origin for bob. + WitnessedCallerBob::mutate(|witness| { + let origin = ctx.ext.origin(); + *witness = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); + }); + + // Call into CHARLIE contract. + assert_matches!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ), + Ok(_) + ); + exec_success() + }); + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + // Record the origin for charlie. + WitnessedCallerCharlie::mutate(|witness| { + let origin = ctx.ext.origin(); + *witness = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); + }); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + place_contract(&CHARLIE, charlie_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + + assert_eq!(WitnessedCallerBob::get(), Some(ALICE_ADDR)); + assert_eq!(WitnessedCallerCharlie::get(), Some(ALICE_ADDR)); + } + #[test] fn is_contract_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 1b5e64739d88..37167d20a438 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -4476,6 +4476,22 @@ mod run_tests { }); } + #[test] + fn origin_api_works() { + let (code, _) = compile_module("origin").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create fixture: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // Call the contract: Asserts the origin API to work as expected + assert_ok!(builder::call(addr).build()); + }); + } + #[test] fn code_hash_works() { let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap(); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 00be26aeaf8b..4221498a725c 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -291,6 +291,8 @@ pub enum RuntimeCosts { CopyToContract(u32), /// Weight of calling `seal_caller`. Caller, + /// Weight of calling `seal_origin`. + Origin, /// Weight of calling `seal_is_contract`. IsContract, /// Weight of calling `seal_code_hash`. @@ -448,6 +450,7 @@ impl Token for RuntimeCosts { CopyToContract(len) => T::WeightInfo::seal_input(len), CopyFromContract(len) => T::WeightInfo::seal_return(len), Caller => T::WeightInfo::seal_caller(), + Origin => T::WeightInfo::seal_origin(), IsContract => T::WeightInfo::seal_is_contract(), CodeHash => T::WeightInfo::seal_code_hash(), OwnCodeHash => T::WeightInfo::seal_own_code_hash(), @@ -1388,6 +1391,21 @@ pub mod env { )?) } + /// Stores the address of the call stack origin into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::origin`]. + #[api_version(0)] + fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Origin)?; + let origin = ::AddressMapper::to_address(self.ext.origin().account_id()?); + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + origin.as_bytes(), + false, + already_charged, + )?) + } + /// Checks whether a specified address belongs to a contract. /// See [`pallet_revive_uapi::HostFn::is_contract`]. #[api_version(0)] diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 3203a0cba9fb..bf2beb94d7a5 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-10-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-dr4vwrkf-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wmcgzesc-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -63,6 +63,7 @@ pub trait WeightInfo { fn dispatch_as_fallback_account() -> Weight; fn noop_host_fn(r: u32, ) -> Weight; fn seal_caller() -> Weight; + fn seal_origin() -> Weight; fn seal_is_contract() -> Weight; fn seal_code_hash() -> Weight; fn seal_own_code_hash() -> Weight; @@ -129,8 +130,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 3_053_000 picoseconds. - Weight::from_parts(3_150_000, 1594) + // Minimum execution time: 3_293_000 picoseconds. + Weight::from_parts(3_530_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -140,10 +141,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `425 + k * (69 ±0)` // Estimated: `415 + k * (70 ±0)` - // Minimum execution time: 15_219_000 picoseconds. - Weight::from_parts(12_576_960, 415) - // Standard Error: 1_429 - .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) + // Minimum execution time: 16_103_000 picoseconds. + Weight::from_parts(16_692_000, 415) + // Standard Error: 2_700 + .saturating_add(Weight::from_parts(1_493_715, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -163,12 +164,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(_c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `7459` - // Minimum execution time: 88_906_000 picoseconds. - Weight::from_parts(93_353_224, 7459) + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1561` + // Estimated: `7501` + // Minimum execution time: 98_125_000 picoseconds. + Weight::from_parts(105_486_409, 7501) + // Standard Error: 2 + .saturating_add(Weight::from_parts(4, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -191,11 +194,11 @@ impl WeightInfo for SubstrateWeight { fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `416` - // Estimated: `6360` - // Minimum execution time: 202_688_000 picoseconds. - Weight::from_parts(197_366_807, 6360) - // Standard Error: 13 - .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + // Estimated: `6348` + // Minimum execution time: 204_069_000 picoseconds. + Weight::from_parts(206_289_328, 6348) + // Standard Error: 16 + .saturating_add(Weight::from_parts(4_438, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -216,12 +219,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1313` - // Estimated: `4779` - // Minimum execution time: 169_246_000 picoseconds. - Weight::from_parts(149_480_457, 4779) - // Standard Error: 16 - .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + // Measured: `1334` + // Estimated: `4782` + // Minimum execution time: 171_790_000 picoseconds. + Weight::from_parts(152_418_252, 4782) + // Standard Error: 20 + .saturating_add(Weight::from_parts(4_271, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -239,10 +242,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `7459` - // Minimum execution time: 91_129_000 picoseconds. - Weight::from_parts(94_220_000, 7459) + // Measured: `1561` + // Estimated: `7501` + // Minimum execution time: 150_910_000 picoseconds. + Weight::from_parts(163_308_000, 7501) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -253,12 +256,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn upload_code(_c: u32, ) -> Weight { + fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 54_849_000 picoseconds. - Weight::from_parts(57_508_591, 3574) + // Minimum execution time: 57_970_000 picoseconds. + Weight::from_parts(62_605_851, 3574) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -272,8 +277,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 45_017_000 picoseconds. - Weight::from_parts(46_312_000, 3750) + // Minimum execution time: 47_117_000 picoseconds. + Weight::from_parts(48_310_000, 3750) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -285,8 +290,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `529` // Estimated: `6469` - // Minimum execution time: 26_992_000 picoseconds. - Weight::from_parts(28_781_000, 6469) + // Minimum execution time: 30_754_000 picoseconds. + Weight::from_parts(32_046_000, 6469) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -298,8 +303,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 44_031_000 picoseconds. - Weight::from_parts(45_133_000, 3574) + // Minimum execution time: 46_338_000 picoseconds. + Weight::from_parts(47_697_000, 3574) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -311,8 +316,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `56` // Estimated: `3521` - // Minimum execution time: 35_681_000 picoseconds. - Weight::from_parts(36_331_000, 3521) + // Minimum execution time: 36_480_000 picoseconds. + Weight::from_parts(37_310_000, 3521) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -324,8 +329,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 11_550_000 picoseconds. - Weight::from_parts(12_114_000, 3610) + // Minimum execution time: 12_950_000 picoseconds. + Weight::from_parts(13_431_000, 3610) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -333,17 +338,24 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_063_000 picoseconds. - Weight::from_parts(7_671_454, 0) - // Standard Error: 105 - .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) + // Minimum execution time: 7_540_000 picoseconds. + Weight::from_parts(8_481_295, 0) + // Standard Error: 900 + .saturating_add(Weight::from_parts(183_511, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(313_000, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(361_000, 0) + } + fn seal_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 272_000 picoseconds. + Weight::from_parts(310_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) @@ -351,8 +363,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `306` // Estimated: `3771` - // Minimum execution time: 7_397_000 picoseconds. - Weight::from_parts(7_967_000, 3771) + // Minimum execution time: 7_755_000 picoseconds. + Weight::from_parts(8_364_000, 3771) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) @@ -361,51 +373,51 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `403` // Estimated: `3868` - // Minimum execution time: 8_395_000 picoseconds. - Weight::from_parts(8_863_000, 3868) + // Minimum execution time: 8_848_000 picoseconds. + Weight::from_parts(9_317_000, 3868) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(333_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 298_000 picoseconds. - Weight::from_parts(334_000, 0) + // Minimum execution time: 314_000 picoseconds. + Weight::from_parts(418_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 262_000 picoseconds. - Weight::from_parts(274_000, 0) + // Minimum execution time: 297_000 picoseconds. + Weight::from_parts(353_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 277_000 picoseconds. - Weight::from_parts(297_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(316_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 620_000 picoseconds. - Weight::from_parts(706_000, 0) + // Minimum execution time: 676_000 picoseconds. + Weight::from_parts(895_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `178` // Estimated: `0` - // Minimum execution time: 5_475_000 picoseconds. - Weight::from_parts(5_706_000, 0) + // Minimum execution time: 6_842_000 picoseconds. + Weight::from_parts(7_790_000, 0) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) @@ -415,8 +427,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `264` // Estimated: `3729` - // Minimum execution time: 9_141_000 picoseconds. - Weight::from_parts(9_674_000, 3729) + // Minimum execution time: 10_982_000 picoseconds. + Weight::from_parts(13_664_000, 3729) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) @@ -426,10 +438,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + n * (1 ±0)` // Estimated: `3703 + n * (1 ±0)` - // Minimum execution time: 6_443_000 picoseconds. - Weight::from_parts(7_252_595, 3703) - // Standard Error: 12 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) + // Minimum execution time: 6_690_000 picoseconds. + Weight::from_parts(7_522_685, 3703) + // Standard Error: 21 + .saturating_add(Weight::from_parts(1_084, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -440,39 +452,39 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_745_000 picoseconds. - Weight::from_parts(3_121_250, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) + // Minimum execution time: 3_090_000 picoseconds. + Weight::from_parts(3_585_913, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(594, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(274_000, 0) + // Minimum execution time: 261_000 picoseconds. + Weight::from_parts(305_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(261_000, 0) + // Minimum execution time: 279_000 picoseconds. + Weight::from_parts(299_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 249_000 picoseconds. - Weight::from_parts(263_000, 0) + // Minimum execution time: 280_000 picoseconds. + Weight::from_parts(317_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 287_000 picoseconds. - Weight::from_parts(300_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -480,8 +492,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 6_147_000 picoseconds. - Weight::from_parts(6_562_000, 1552) + // Minimum execution time: 6_116_000 picoseconds. + Weight::from_parts(6_584_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -489,20 +501,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 453_000 picoseconds. - Weight::from_parts(548_774, 0) + // Minimum execution time: 477_000 picoseconds. + Weight::from_parts(887_560, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(154, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 264_000 picoseconds. - Weight::from_parts(490_374, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) + // Minimum execution time: 315_000 picoseconds. + Weight::from_parts(870_254, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(248, 0).saturating_mul(n.into())) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) @@ -518,11 +530,11 @@ impl WeightInfo for SubstrateWeight { fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `323 + n * (88 ±0)` - // Estimated: `3788 + n * (2563 ±0)` - // Minimum execution time: 22_833_000 picoseconds. - Weight::from_parts(24_805_620, 3788) - // Standard Error: 9_498 - .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + // Estimated: `3789 + n * (2563 ±0)` + // Minimum execution time: 23_098_000 picoseconds. + Weight::from_parts(26_001_186, 3789) + // Standard Error: 23_098 + .saturating_add(Weight::from_parts(4_935_203, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -535,22 +547,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_969_000 picoseconds. - Weight::from_parts(4_994_916, 0) - // Standard Error: 3_727 - .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) - // Standard Error: 33 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + // Minimum execution time: 5_271_000 picoseconds. + Weight::from_parts(5_803_969, 0) + // Standard Error: 10_511 + .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(t.into())) + // Standard Error: 93 + .saturating_add(Weight::from_parts(361, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 328_000 picoseconds. - Weight::from_parts(928_905, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) + // Minimum execution time: 375_000 picoseconds. + Weight::from_parts(1_601_309, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(787, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -558,8 +570,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_612_000 picoseconds. - Weight::from_parts(9_326_000, 744) + // Minimum execution time: 9_557_000 picoseconds. + Weight::from_parts(10_131_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -568,8 +580,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_542_000 picoseconds. - Weight::from_parts(45_397_000, 10754) + // Minimum execution time: 45_601_000 picoseconds. + Weight::from_parts(46_296_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -578,8 +590,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 10_343_000 picoseconds. - Weight::from_parts(10_883_000, 744) + // Minimum execution time: 10_718_000 picoseconds. + Weight::from_parts(12_282_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -589,8 +601,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 46_835_000 picoseconds. - Weight::from_parts(47_446_000, 10754) + // Minimum execution time: 47_580_000 picoseconds. + Weight::from_parts(50_301_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -602,12 +614,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 10_604_000 picoseconds. - Weight::from_parts(11_282_849, 247) - // Standard Error: 48 - .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) - // Standard Error: 48 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) + // Minimum execution time: 11_483_000 picoseconds. + Weight::from_parts(13_084_262, 247) + // Standard Error: 218 + .saturating_add(Weight::from_parts(83, 0).saturating_mul(n.into())) + // Standard Error: 218 + .saturating_add(Weight::from_parts(683, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -619,10 +631,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 10_081_000 picoseconds. - Weight::from_parts(11_186_557, 247) - // Standard Error: 68 - .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) + // Minimum execution time: 10_972_000 picoseconds. + Weight::from_parts(12_960_831, 247) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -634,10 +644,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_758_000 picoseconds. - Weight::from_parts(9_939_492, 247) - // Standard Error: 69 - .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) + // Minimum execution time: 9_989_000 picoseconds. + Weight::from_parts(12_783_294, 247) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -648,10 +656,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_525_000 picoseconds. - Weight::from_parts(9_522_265, 247) - // Standard Error: 66 - .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) + // Minimum execution time: 9_732_000 picoseconds. + Weight::from_parts(11_156_576, 247) + // Standard Error: 255 + .saturating_add(Weight::from_parts(1_956, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -662,10 +670,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 10_603_000 picoseconds. - Weight::from_parts(11_817_752, 247) - // Standard Error: 82 - .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) + // Minimum execution time: 10_883_000 picoseconds. + Weight::from_parts(13_454_925, 247) + // Standard Error: 276 + .saturating_add(Weight::from_parts(1_509, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -674,36 +682,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_553_000 picoseconds. - Weight::from_parts(1_615_000, 0) + // Minimum execution time: 1_586_000 picoseconds. + Weight::from_parts(1_869_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_932_000 picoseconds. - Weight::from_parts(2_064_000, 0) + // Minimum execution time: 1_997_000 picoseconds. + Weight::from_parts(2_093_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_510_000 picoseconds. - Weight::from_parts(1_545_000, 0) + // Minimum execution time: 1_531_000 picoseconds. + Weight::from_parts(1_734_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_663_000 picoseconds. - Weight::from_parts(1_801_000, 0) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(1_880_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_026_000 picoseconds. - Weight::from_parts(1_137_000, 0) + // Minimum execution time: 1_192_000 picoseconds. + Weight::from_parts(1_339_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -711,59 +719,59 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_446_000 picoseconds. - Weight::from_parts(2_644_525, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) + // Minimum execution time: 2_294_000 picoseconds. + Weight::from_parts(2_848_884, 0) + // Standard Error: 53 + .saturating_add(Weight::from_parts(176, 0).saturating_mul(n.into())) + // Standard Error: 53 + .saturating_add(Weight::from_parts(148, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_085_000 picoseconds. - Weight::from_parts(2_379_853, 0) - // Standard Error: 19 - .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) + // Minimum execution time: 2_073_000 picoseconds. + Weight::from_parts(2_670_081, 0) + // Standard Error: 64 + .saturating_add(Weight::from_parts(270, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_876_000 picoseconds. - Weight::from_parts(2_073_689, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) + // Minimum execution time: 1_879_000 picoseconds. + Weight::from_parts(2_294_904, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(481, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_914_470, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_151_924, 0) + // Standard Error: 56 + .saturating_add(Weight::from_parts(98, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_479_000 picoseconds. - Weight::from_parts(2_758_250, 0) + // Minimum execution time: 2_615_000 picoseconds. + Weight::from_parts(3_050_600, 0) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `3817` - // Minimum execution time: 15_745_000 picoseconds. - Weight::from_parts(16_300_000, 3817) + // Measured: `390` + // Estimated: `3855` + // Minimum execution time: 18_629_000 picoseconds. + Weight::from_parts(19_520_000, 3855) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) @@ -778,17 +786,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1309 + t * (140 ±0)` - // Estimated: `4774 + t * (140 ±0)` - // Minimum execution time: 39_639_000 picoseconds. - Weight::from_parts(40_909_376, 4774) - // Standard Error: 54_479 - .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + // Measured: `1319 + t * (178 ±0)` + // Estimated: `4784 + t * (178 ±0)` + // Minimum execution time: 43_655_000 picoseconds. + Weight::from_parts(50_252_813, 4784) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 178).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -796,10 +802,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1081` - // Estimated: `4546` - // Minimum execution time: 29_651_000 picoseconds. - Weight::from_parts(31_228_000, 4546) + // Measured: `1089` + // Estimated: `4554` + // Minimum execution time: 31_153_000 picoseconds. + Weight::from_parts(33_625_000, 4554) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -813,12 +819,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1327` - // Estimated: `4792` - // Minimum execution time: 126_995_000 picoseconds. - Weight::from_parts(114_028_446, 4792) - // Standard Error: 11 - .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) + // Measured: `1360` + // Estimated: `4814` + // Minimum execution time: 130_134_000 picoseconds. + Weight::from_parts(120_699_282, 4814) + // Standard Error: 20 + .saturating_add(Weight::from_parts(4_054, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -827,64 +833,64 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 653_000 picoseconds. - Weight::from_parts(973_524, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) + // Minimum execution time: 665_000 picoseconds. + Weight::from_parts(1_964_310, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_091, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_118_000 picoseconds. - Weight::from_parts(795_498, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) + // Minimum execution time: 1_245_000 picoseconds. + Weight::from_parts(2_362_235, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(3_360, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 647_000 picoseconds. - Weight::from_parts(667_024, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) + // Minimum execution time: 668_000 picoseconds. + Weight::from_parts(1_216_363, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 605_000 picoseconds. - Weight::from_parts(675_568, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) + // Minimum execution time: 702_000 picoseconds. + Weight::from_parts(1_283_345, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 42_743_000 picoseconds. - Weight::from_parts(26_131_984, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) + // Minimum execution time: 45_690_000 picoseconds. + Weight::from_parts(28_207_599, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(5_142, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 50_838_000 picoseconds. - Weight::from_parts(52_248_000, 0) + // Minimum execution time: 51_869_000 picoseconds. + Weight::from_parts(56_118_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_605_000 picoseconds. - Weight::from_parts(12_796_000, 0) + // Minimum execution time: 12_927_000 picoseconds. + Weight::from_parts(13_256_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -892,8 +898,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `300` // Estimated: `3765` - // Minimum execution time: 16_377_000 picoseconds. - Weight::from_parts(16_932_000, 3765) + // Minimum execution time: 16_969_000 picoseconds. + Weight::from_parts(17_796_000, 3765) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -903,8 +909,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `338` // Estimated: `3803` - // Minimum execution time: 11_499_000 picoseconds. - Weight::from_parts(12_104_000, 3803) + // Minimum execution time: 11_827_000 picoseconds. + Weight::from_parts(13_675_000, 3803) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -914,8 +920,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `338` // Estimated: `3561` - // Minimum execution time: 10_308_000 picoseconds. - Weight::from_parts(11_000_000, 3561) + // Minimum execution time: 10_529_000 picoseconds. + Weight::from_parts(12_080_000, 3561) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -924,10 +930,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_162_000 picoseconds. - Weight::from_parts(9_180_011, 0) - // Standard Error: 63 - .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) + // Minimum execution time: 8_269_000 picoseconds. + Weight::from_parts(11_148_702, 0) + // Standard Error: 366 + .saturating_add(Weight::from_parts(87_469, 0).saturating_mul(r.into())) } } @@ -939,8 +945,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 3_053_000 picoseconds. - Weight::from_parts(3_150_000, 1594) + // Minimum execution time: 3_293_000 picoseconds. + Weight::from_parts(3_530_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -950,10 +956,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `425 + k * (69 ±0)` // Estimated: `415 + k * (70 ±0)` - // Minimum execution time: 15_219_000 picoseconds. - Weight::from_parts(12_576_960, 415) - // Standard Error: 1_429 - .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) + // Minimum execution time: 16_103_000 picoseconds. + Weight::from_parts(16_692_000, 415) + // Standard Error: 2_700 + .saturating_add(Weight::from_parts(1_493_715, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -973,12 +979,14 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(_c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `7459` - // Minimum execution time: 88_906_000 picoseconds. - Weight::from_parts(93_353_224, 7459) + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1561` + // Estimated: `7501` + // Minimum execution time: 98_125_000 picoseconds. + Weight::from_parts(105_486_409, 7501) + // Standard Error: 2 + .saturating_add(Weight::from_parts(4, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1001,11 +1009,11 @@ impl WeightInfo for () { fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `416` - // Estimated: `6360` - // Minimum execution time: 202_688_000 picoseconds. - Weight::from_parts(197_366_807, 6360) - // Standard Error: 13 - .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + // Estimated: `6348` + // Minimum execution time: 204_069_000 picoseconds. + Weight::from_parts(206_289_328, 6348) + // Standard Error: 16 + .saturating_add(Weight::from_parts(4_438, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1026,12 +1034,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1313` - // Estimated: `4779` - // Minimum execution time: 169_246_000 picoseconds. - Weight::from_parts(149_480_457, 4779) - // Standard Error: 16 - .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + // Measured: `1334` + // Estimated: `4782` + // Minimum execution time: 171_790_000 picoseconds. + Weight::from_parts(152_418_252, 4782) + // Standard Error: 20 + .saturating_add(Weight::from_parts(4_271, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1049,10 +1057,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `7459` - // Minimum execution time: 91_129_000 picoseconds. - Weight::from_parts(94_220_000, 7459) + // Measured: `1561` + // Estimated: `7501` + // Minimum execution time: 150_910_000 picoseconds. + Weight::from_parts(163_308_000, 7501) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1063,12 +1071,14 @@ impl WeightInfo for () { /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn upload_code(_c: u32, ) -> Weight { + fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 54_849_000 picoseconds. - Weight::from_parts(57_508_591, 3574) + // Minimum execution time: 57_970_000 picoseconds. + Weight::from_parts(62_605_851, 3574) + // Standard Error: 3 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1082,8 +1092,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 45_017_000 picoseconds. - Weight::from_parts(46_312_000, 3750) + // Minimum execution time: 47_117_000 picoseconds. + Weight::from_parts(48_310_000, 3750) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1095,8 +1105,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `529` // Estimated: `6469` - // Minimum execution time: 26_992_000 picoseconds. - Weight::from_parts(28_781_000, 6469) + // Minimum execution time: 30_754_000 picoseconds. + Weight::from_parts(32_046_000, 6469) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1108,8 +1118,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 44_031_000 picoseconds. - Weight::from_parts(45_133_000, 3574) + // Minimum execution time: 46_338_000 picoseconds. + Weight::from_parts(47_697_000, 3574) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1121,8 +1131,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `56` // Estimated: `3521` - // Minimum execution time: 35_681_000 picoseconds. - Weight::from_parts(36_331_000, 3521) + // Minimum execution time: 36_480_000 picoseconds. + Weight::from_parts(37_310_000, 3521) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1134,8 +1144,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 11_550_000 picoseconds. - Weight::from_parts(12_114_000, 3610) + // Minimum execution time: 12_950_000 picoseconds. + Weight::from_parts(13_431_000, 3610) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -1143,17 +1153,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_063_000 picoseconds. - Weight::from_parts(7_671_454, 0) - // Standard Error: 105 - .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) + // Minimum execution time: 7_540_000 picoseconds. + Weight::from_parts(8_481_295, 0) + // Standard Error: 900 + .saturating_add(Weight::from_parts(183_511, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(313_000, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(361_000, 0) + } + fn seal_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 272_000 picoseconds. + Weight::from_parts(310_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) @@ -1161,8 +1178,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `306` // Estimated: `3771` - // Minimum execution time: 7_397_000 picoseconds. - Weight::from_parts(7_967_000, 3771) + // Minimum execution time: 7_755_000 picoseconds. + Weight::from_parts(8_364_000, 3771) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) @@ -1171,51 +1188,51 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `403` // Estimated: `3868` - // Minimum execution time: 8_395_000 picoseconds. - Weight::from_parts(8_863_000, 3868) + // Minimum execution time: 8_848_000 picoseconds. + Weight::from_parts(9_317_000, 3868) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(333_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 298_000 picoseconds. - Weight::from_parts(334_000, 0) + // Minimum execution time: 314_000 picoseconds. + Weight::from_parts(418_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 262_000 picoseconds. - Weight::from_parts(274_000, 0) + // Minimum execution time: 297_000 picoseconds. + Weight::from_parts(353_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 277_000 picoseconds. - Weight::from_parts(297_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(316_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 620_000 picoseconds. - Weight::from_parts(706_000, 0) + // Minimum execution time: 676_000 picoseconds. + Weight::from_parts(895_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `178` // Estimated: `0` - // Minimum execution time: 5_475_000 picoseconds. - Weight::from_parts(5_706_000, 0) + // Minimum execution time: 6_842_000 picoseconds. + Weight::from_parts(7_790_000, 0) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) @@ -1225,8 +1242,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `264` // Estimated: `3729` - // Minimum execution time: 9_141_000 picoseconds. - Weight::from_parts(9_674_000, 3729) + // Minimum execution time: 10_982_000 picoseconds. + Weight::from_parts(13_664_000, 3729) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) @@ -1236,10 +1253,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + n * (1 ±0)` // Estimated: `3703 + n * (1 ±0)` - // Minimum execution time: 6_443_000 picoseconds. - Weight::from_parts(7_252_595, 3703) - // Standard Error: 12 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) + // Minimum execution time: 6_690_000 picoseconds. + Weight::from_parts(7_522_685, 3703) + // Standard Error: 21 + .saturating_add(Weight::from_parts(1_084, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1250,39 +1267,39 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_745_000 picoseconds. - Weight::from_parts(3_121_250, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) + // Minimum execution time: 3_090_000 picoseconds. + Weight::from_parts(3_585_913, 0) + // Standard Error: 11 + .saturating_add(Weight::from_parts(594, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(274_000, 0) + // Minimum execution time: 261_000 picoseconds. + Weight::from_parts(305_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(261_000, 0) + // Minimum execution time: 279_000 picoseconds. + Weight::from_parts(299_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 249_000 picoseconds. - Weight::from_parts(263_000, 0) + // Minimum execution time: 280_000 picoseconds. + Weight::from_parts(317_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 287_000 picoseconds. - Weight::from_parts(300_000, 0) + // Minimum execution time: 285_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1290,8 +1307,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 6_147_000 picoseconds. - Weight::from_parts(6_562_000, 1552) + // Minimum execution time: 6_116_000 picoseconds. + Weight::from_parts(6_584_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -1299,20 +1316,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 453_000 picoseconds. - Weight::from_parts(548_774, 0) + // Minimum execution time: 477_000 picoseconds. + Weight::from_parts(887_560, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(154, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 264_000 picoseconds. - Weight::from_parts(490_374, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) + // Minimum execution time: 315_000 picoseconds. + Weight::from_parts(870_254, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(248, 0).saturating_mul(n.into())) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) @@ -1328,11 +1345,11 @@ impl WeightInfo for () { fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `323 + n * (88 ±0)` - // Estimated: `3788 + n * (2563 ±0)` - // Minimum execution time: 22_833_000 picoseconds. - Weight::from_parts(24_805_620, 3788) - // Standard Error: 9_498 - .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + // Estimated: `3789 + n * (2563 ±0)` + // Minimum execution time: 23_098_000 picoseconds. + Weight::from_parts(26_001_186, 3789) + // Standard Error: 23_098 + .saturating_add(Weight::from_parts(4_935_203, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -1345,22 +1362,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_969_000 picoseconds. - Weight::from_parts(4_994_916, 0) - // Standard Error: 3_727 - .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) - // Standard Error: 33 - .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) + // Minimum execution time: 5_271_000 picoseconds. + Weight::from_parts(5_803_969, 0) + // Standard Error: 10_511 + .saturating_add(Weight::from_parts(163_106, 0).saturating_mul(t.into())) + // Standard Error: 93 + .saturating_add(Weight::from_parts(361, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 328_000 picoseconds. - Weight::from_parts(928_905, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) + // Minimum execution time: 375_000 picoseconds. + Weight::from_parts(1_601_309, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(787, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1368,8 +1385,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_612_000 picoseconds. - Weight::from_parts(9_326_000, 744) + // Minimum execution time: 9_557_000 picoseconds. + Weight::from_parts(10_131_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1378,8 +1395,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_542_000 picoseconds. - Weight::from_parts(45_397_000, 10754) + // Minimum execution time: 45_601_000 picoseconds. + Weight::from_parts(46_296_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1388,8 +1405,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 10_343_000 picoseconds. - Weight::from_parts(10_883_000, 744) + // Minimum execution time: 10_718_000 picoseconds. + Weight::from_parts(12_282_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1399,8 +1416,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 46_835_000 picoseconds. - Weight::from_parts(47_446_000, 10754) + // Minimum execution time: 47_580_000 picoseconds. + Weight::from_parts(50_301_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1412,12 +1429,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 10_604_000 picoseconds. - Weight::from_parts(11_282_849, 247) - // Standard Error: 48 - .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) - // Standard Error: 48 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) + // Minimum execution time: 11_483_000 picoseconds. + Weight::from_parts(13_084_262, 247) + // Standard Error: 218 + .saturating_add(Weight::from_parts(83, 0).saturating_mul(n.into())) + // Standard Error: 218 + .saturating_add(Weight::from_parts(683, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1429,10 +1446,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 10_081_000 picoseconds. - Weight::from_parts(11_186_557, 247) - // Standard Error: 68 - .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) + // Minimum execution time: 10_972_000 picoseconds. + Weight::from_parts(12_960_831, 247) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1444,10 +1459,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_758_000 picoseconds. - Weight::from_parts(9_939_492, 247) - // Standard Error: 69 - .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) + // Minimum execution time: 9_989_000 picoseconds. + Weight::from_parts(12_783_294, 247) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1458,10 +1471,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_525_000 picoseconds. - Weight::from_parts(9_522_265, 247) - // Standard Error: 66 - .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) + // Minimum execution time: 9_732_000 picoseconds. + Weight::from_parts(11_156_576, 247) + // Standard Error: 255 + .saturating_add(Weight::from_parts(1_956, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1472,10 +1485,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 10_603_000 picoseconds. - Weight::from_parts(11_817_752, 247) - // Standard Error: 82 - .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) + // Minimum execution time: 10_883_000 picoseconds. + Weight::from_parts(13_454_925, 247) + // Standard Error: 276 + .saturating_add(Weight::from_parts(1_509, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1484,36 +1497,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_553_000 picoseconds. - Weight::from_parts(1_615_000, 0) + // Minimum execution time: 1_586_000 picoseconds. + Weight::from_parts(1_869_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_932_000 picoseconds. - Weight::from_parts(2_064_000, 0) + // Minimum execution time: 1_997_000 picoseconds. + Weight::from_parts(2_093_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_510_000 picoseconds. - Weight::from_parts(1_545_000, 0) + // Minimum execution time: 1_531_000 picoseconds. + Weight::from_parts(1_734_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_663_000 picoseconds. - Weight::from_parts(1_801_000, 0) + // Minimum execution time: 1_635_000 picoseconds. + Weight::from_parts(1_880_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_026_000 picoseconds. - Weight::from_parts(1_137_000, 0) + // Minimum execution time: 1_192_000 picoseconds. + Weight::from_parts(1_339_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -1521,59 +1534,59 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_446_000 picoseconds. - Weight::from_parts(2_644_525, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) + // Minimum execution time: 2_294_000 picoseconds. + Weight::from_parts(2_848_884, 0) + // Standard Error: 53 + .saturating_add(Weight::from_parts(176, 0).saturating_mul(n.into())) + // Standard Error: 53 + .saturating_add(Weight::from_parts(148, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_085_000 picoseconds. - Weight::from_parts(2_379_853, 0) - // Standard Error: 19 - .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) + // Minimum execution time: 2_073_000 picoseconds. + Weight::from_parts(2_670_081, 0) + // Standard Error: 64 + .saturating_add(Weight::from_parts(270, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_876_000 picoseconds. - Weight::from_parts(2_073_689, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) + // Minimum execution time: 1_879_000 picoseconds. + Weight::from_parts(2_294_904, 0) + // Standard Error: 55 + .saturating_add(Weight::from_parts(481, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_688_000 picoseconds. - Weight::from_parts(1_914_470, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(2_151_924, 0) + // Standard Error: 56 + .saturating_add(Weight::from_parts(98, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_479_000 picoseconds. - Weight::from_parts(2_758_250, 0) + // Minimum execution time: 2_615_000 picoseconds. + Weight::from_parts(3_050_600, 0) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `3817` - // Minimum execution time: 15_745_000 picoseconds. - Weight::from_parts(16_300_000, 3817) + // Measured: `390` + // Estimated: `3855` + // Minimum execution time: 18_629_000 picoseconds. + Weight::from_parts(19_520_000, 3855) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::AddressSuffix` (r:1 w:0) @@ -1588,17 +1601,15 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1309 + t * (140 ±0)` - // Estimated: `4774 + t * (140 ±0)` - // Minimum execution time: 39_639_000 picoseconds. - Weight::from_parts(40_909_376, 4774) - // Standard Error: 54_479 - .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + // Measured: `1319 + t * (178 ±0)` + // Estimated: `4784 + t * (178 ±0)` + // Minimum execution time: 43_655_000 picoseconds. + Weight::from_parts(50_252_813, 4784) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 178).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1606,10 +1617,10 @@ impl WeightInfo for () { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1081` - // Estimated: `4546` - // Minimum execution time: 29_651_000 picoseconds. - Weight::from_parts(31_228_000, 4546) + // Measured: `1089` + // Estimated: `4554` + // Minimum execution time: 31_153_000 picoseconds. + Weight::from_parts(33_625_000, 4554) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -1623,12 +1634,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1327` - // Estimated: `4792` - // Minimum execution time: 126_995_000 picoseconds. - Weight::from_parts(114_028_446, 4792) - // Standard Error: 11 - .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) + // Measured: `1360` + // Estimated: `4814` + // Minimum execution time: 130_134_000 picoseconds. + Weight::from_parts(120_699_282, 4814) + // Standard Error: 20 + .saturating_add(Weight::from_parts(4_054, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1637,64 +1648,64 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 653_000 picoseconds. - Weight::from_parts(973_524, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) + // Minimum execution time: 665_000 picoseconds. + Weight::from_parts(1_964_310, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_091, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_118_000 picoseconds. - Weight::from_parts(795_498, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) + // Minimum execution time: 1_245_000 picoseconds. + Weight::from_parts(2_362_235, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(3_360, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 647_000 picoseconds. - Weight::from_parts(667_024, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) + // Minimum execution time: 668_000 picoseconds. + Weight::from_parts(1_216_363, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 605_000 picoseconds. - Weight::from_parts(675_568, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) + // Minimum execution time: 702_000 picoseconds. + Weight::from_parts(1_283_345, 0) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 42_743_000 picoseconds. - Weight::from_parts(26_131_984, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) + // Minimum execution time: 45_690_000 picoseconds. + Weight::from_parts(28_207_599, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(5_142, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 50_838_000 picoseconds. - Weight::from_parts(52_248_000, 0) + // Minimum execution time: 51_869_000 picoseconds. + Weight::from_parts(56_118_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_605_000 picoseconds. - Weight::from_parts(12_796_000, 0) + // Minimum execution time: 12_927_000 picoseconds. + Weight::from_parts(13_256_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1702,8 +1713,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `300` // Estimated: `3765` - // Minimum execution time: 16_377_000 picoseconds. - Weight::from_parts(16_932_000, 3765) + // Minimum execution time: 16_969_000 picoseconds. + Weight::from_parts(17_796_000, 3765) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1713,8 +1724,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `338` // Estimated: `3803` - // Minimum execution time: 11_499_000 picoseconds. - Weight::from_parts(12_104_000, 3803) + // Minimum execution time: 11_827_000 picoseconds. + Weight::from_parts(13_675_000, 3803) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1724,8 +1735,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `338` // Estimated: `3561` - // Minimum execution time: 10_308_000 picoseconds. - Weight::from_parts(11_000_000, 3561) + // Minimum execution time: 10_529_000 picoseconds. + Weight::from_parts(12_080_000, 3561) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1734,9 +1745,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_162_000 picoseconds. - Weight::from_parts(9_180_011, 0) - // Standard Error: 63 - .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) + // Minimum execution time: 8_269_000 picoseconds. + Weight::from_parts(11_148_702, 0) + // Standard Error: 366 + .saturating_add(Weight::from_parts(87_469, 0).saturating_mul(r.into())) } } diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 2663d7c2cf0c..aa92ed73a050 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -206,6 +206,17 @@ pub trait HostFn: private::Sealed { /// - `output`: A reference to the output data buffer to write the caller address. fn caller(output: &mut [u8; 20]); + /// Stores the origin address (initator of the call stack) into the supplied buffer. + /// + /// If there is no address associated with the origin (e.g. because the origin is root) then + /// it traps with `BadOrigin`. This can only happen through on-chain governance actions or + /// customized runtimes. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the origin's address. + fn origin(output: &mut [u8; 20]); + /// Checks whether the caller of the current contract is the origin of the whole call stack. /// /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index c2508198c935..07bbb24adedd 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -73,6 +73,7 @@ mod sys { pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); pub fn caller(out_ptr: *mut u8); + pub fn origin(out_ptr: *mut u8); pub fn is_contract(account_ptr: *const u8) -> ReturnCode; pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8); pub fn own_code_hash(out_ptr: *mut u8); @@ -453,7 +454,7 @@ impl HostFn for HostFnImpl { impl_wrapper_for! { [u8; 32] => block_number, balance, value_transferred, now, minimum_balance, chain_id; - [u8; 20] => address, caller; + [u8; 20] => address, caller, origin; } fn weight_left(output: &mut &mut [u8]) { From f7d40abb24971d46094c101cf0c021429ff0eed1 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:51:36 +0000 Subject: [PATCH 53/58] Use frame umbrella crate in `pallet-proxy` and `pallet-multisig` (#5995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A step towards https://github.com/paritytech/polkadot-sdk/issues/4782 In order to nail down the right preludes in `polkadot-sdk-frame`, we need to migrate a number of pallets to be written with it. Moreover, migrating our pallets to this simpler patter will encourage the ecosystem to also follow along. If this PR is approved and has no unwanted negative consequences, I will make a tracking issue to migrate all pallets to this umbrella crate. TODO: - [x] fix frame benchmarking template. Can we detect the umbrella crate in there and have an `if else`? cc @ggwpez - [x] Migrate benchmarking to v2 @re-gius a good candidate for you, you can open a PR against my branch. - [x] tracking issue with follow-ups --------- Co-authored-by: Guillaume Thiolliere Co-authored-by: Giuseppe Re Co-authored-by: Dónal Murray Co-authored-by: Shawn Tabrizi Co-authored-by: Oliver Tale-Yazdi --- .github/scripts/cmd/cmd.py | 25 +- Cargo.lock | 13 +- prdoc/pr_5995.prdoc | 21 ++ .../frame-umbrella-weight-template.hbs | 120 +++++++++ .../frame/migrations/src/benchmarking.rs | 2 +- substrate/frame/multisig/Cargo.toml | 22 +- substrate/frame/multisig/src/benchmarking.rs | 228 +++++++++++------ substrate/frame/multisig/src/lib.rs | 33 +-- substrate/frame/multisig/src/migrations.rs | 21 +- substrate/frame/multisig/src/tests.rs | 15 +- substrate/frame/multisig/src/weights.rs | 5 +- substrate/frame/proxy/Cargo.toml | 25 +- substrate/frame/proxy/src/benchmarking.rs | 238 ++++++++++++------ substrate/frame/proxy/src/lib.rs | 34 +-- substrate/frame/proxy/src/tests.rs | 26 +- substrate/frame/proxy/src/weights.rs | 3 +- substrate/frame/src/lib.rs | 209 +++++++++++++-- substrate/scripts/run_all_benchmarks.sh | 14 +- templates/minimal/pallets/template/src/lib.rs | 4 + templates/minimal/runtime/Cargo.toml | 1 - templates/minimal/runtime/src/lib.rs | 13 +- 21 files changed, 735 insertions(+), 337 deletions(-) create mode 100644 prdoc/pr_5995.prdoc create mode 100644 substrate/.maintain/frame-umbrella-weight-template.hbs diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 5715a7fbfeda..e05cd4fc5901 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -6,6 +6,7 @@ import argparse import _help import importlib.util +import re _HelpAction = _help._HelpAction @@ -40,20 +41,20 @@ def setup_logging(): setup_logging() """ -BENCH +BENCH """ bench_example = '''**Examples**: - Runs all benchmarks + Runs all benchmarks %(prog)s Runs benchmarks for pallet_balances and pallet_multisig for all runtimes which have these pallets. **--quiet** makes it to output nothing to PR but reactions %(prog)s --pallet pallet_balances pallet_xcm_benchmarks::generic --quiet - + Runs bench for all pallets for westend runtime and fails fast on first failed benchmark %(prog)s --runtime westend --fail-fast - - Does not output anything and cleans up the previous bot's & author command triggering comments in PR + + Does not output anything and cleans up the previous bot's & author command triggering comments in PR %(prog)s --runtime westend rococo --pallet pallet_balances pallet_multisig --quiet --clean ''' @@ -96,14 +97,14 @@ def setup_logging(): """ -FMT +FMT """ parser_fmt = subparsers.add_parser('fmt', help='Formats code (cargo +nightly-VERSION fmt) and configs (taplo format)') for arg, config in common_args.items(): parser_fmt.add_argument(arg, **config) """ -Update UI +Update UI """ parser_ui = subparsers.add_parser('update-ui', help='Updates UI tests') for arg, config in common_args.items(): @@ -215,7 +216,15 @@ def main(): print(f'-- package_dir: {package_dir}') print(f'-- manifest_path: {manifest_path}') output_path = os.path.join(package_dir, "src", "weights.rs") + # TODO: we can remove once all pallets in dev runtime are migrated to polkadot-sdk-frame + try: + uses_polkadot_sdk_frame = "true" in os.popen(f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .dependencies | any(.name == \"polkadot-sdk-frame\")'").read() + # Empty output from the previous os.popen command + except StopIteration: + uses_polkadot_sdk_frame = False template = config['template'] + if uses_polkadot_sdk_frame and re.match(r"frame-(:?umbrella-)?weight-template\.hbs", os.path.normpath(template).split(os.path.sep)[-1]): + template = "substrate/.maintain/frame-umbrella-weight-template.hbs" else: default_path = f"./{config['path']}/src/weights" xcm_path = f"./{config['path']}/src/weights/xcm" @@ -434,4 +443,4 @@ def main(): print('🚀 Done') if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/Cargo.lock b/Cargo.lock index dfa70250e30f..127e30d666af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12099,15 +12099,11 @@ dependencies = [ name = "pallet-multisig" version = "28.0.0" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", "log", "pallet-balances", "parity-scale-codec", + "polkadot-sdk-frame", "scale-info", - "sp-io 30.0.0", - "sp-runtime 31.0.1", ] [[package]] @@ -12418,16 +12414,11 @@ dependencies = [ name = "pallet-proxy" version = "28.0.0" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", "pallet-balances", "pallet-utility", "parity-scale-codec", + "polkadot-sdk-frame", "scale-info", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-runtime 31.0.1", ] [[package]] diff --git a/prdoc/pr_5995.prdoc b/prdoc/pr_5995.prdoc new file mode 100644 index 000000000000..fdd754057bd1 --- /dev/null +++ b/prdoc/pr_5995.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Use frame umbrella crate in pallet-proxy and pallet-multisig + +doc: + - audience: Runtime Dev + description: | + Extends the FRAME umbrella crate and uses it in pallet-proxy and pallet-multisig. + Migrates benchmarking from v1 to v2 for pallet-proxy and pallet-multisig. + Allows CI to pick the umbrella crate weights template to run benchmarks. + +crates: + - name: pallet-multisig + bump: minor + - name: pallet-proxy + bump: minor + - name: polkadot-sdk-frame + bump: major + - name: pallet-migrations + bump: patch diff --git a/substrate/.maintain/frame-umbrella-weight-template.hbs b/substrate/.maintain/frame-umbrella-weight-template.hbs new file mode 100644 index 000000000000..0f26fae1d8f1 --- /dev/null +++ b/substrate/.maintain/frame-umbrella-weight-template.hbs @@ -0,0 +1,120 @@ +{{header}} +//! Autogenerated weights for `{{pallet}}` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` +//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}` + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame::weights_prelude::*; + +/// Weight functions needed for `{{pallet}}`. +pub trait WeightInfo { + {{#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{c.name}}: u32, {{/each~}} + ) -> Weight; + {{/each}} +} + +/// Weights for `{{pallet}}` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +{{#if (eq pallet "frame_system")}} +impl WeightInfo for SubstrateWeight { +{{else}} +impl WeightInfo for SubstrateWeight { +{{/if}} + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} diff --git a/substrate/frame/migrations/src/benchmarking.rs b/substrate/frame/migrations/src/benchmarking.rs index 8ad1fa50d149..c076d40bb05c 100644 --- a/substrate/frame/migrations/src/benchmarking.rs +++ b/substrate/frame/migrations/src/benchmarking.rs @@ -158,7 +158,7 @@ mod benches { fn on_init_loop() { T::Migrations::set_fail_after(0); // Should not be called anyway. System::::set_block_number(1u32.into()); - Pallet::::on_runtime_upgrade(); + as Hooks>>::on_runtime_upgrade(); #[block] { diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml index b24df856bcd7..c96be908faef 100644 --- a/substrate/frame/multisig/Cargo.toml +++ b/substrate/frame/multisig/Cargo.toml @@ -18,11 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } scale-info = { features = ["derive"], workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -sp-io = { workspace = true } -sp-runtime = { workspace = true } +frame = { workspace = true, features = ["experimental", "runtime"] } # third party log = { workspace = true } @@ -34,25 +30,15 @@ pallet-balances = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", + "frame/std", "log/std", - "pallet-balances/std", "scale-info/std", - "sp-io/std", - "sp-runtime/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", + "frame/try-runtime", "pallet-balances/try-runtime", - "sp-runtime/try-runtime", ] diff --git a/substrate/frame/multisig/src/benchmarking.rs b/substrate/frame/multisig/src/benchmarking.rs index ebe19df5dc43..ccaa1ceab66e 100644 --- a/substrate/frame/multisig/src/benchmarking.rs +++ b/substrate/frame/multisig/src/benchmarking.rs @@ -20,9 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::v1::{account, benchmarks}; -use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; +use frame::benchmarking::prelude::*; use crate::Pallet as Multisig; @@ -47,48 +45,59 @@ fn setup_multi( Ok((signatories, Box::new(call))) } -benchmarks! { - as_multi_threshold_1 { - // Transaction Length - let z in 0 .. 10_000; +#[benchmarks] +mod benchmarks { + use super::*; + + /// `z`: Transaction Length + #[benchmark] + fn as_multi_threshold_1(z: Linear<0, 10_000>) -> Result<(), BenchmarkError> { let max_signatories = T::MaxSignatories::get().into(); let (mut signatories, _) = setup_multi::(max_signatories, z)?; - let call: ::RuntimeCall = frame_system::Call::::remark { - remark: vec![0; z as usize] - }.into(); - let call_hash = call.using_encoded(blake2_256); - let multi_account_id = Multisig::::multi_account_id(&signatories, 1); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![0; z as usize] }.into(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: _(RawOrigin::Signed(caller.clone()), signatories, Box::new(call)) - verify { + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), signatories, Box::new(call)); + // If the benchmark resolves, then the call was dispatched successfully. + Ok(()) } - as_multi_create { - // Signatories, need at least 2 total people - let s in 2 .. T::MaxSignatories::get(); - // Transaction Length - let z in 0 .. 10_000; + /// `z`: Transaction Length + /// `s`: Signatories, need at least 2 people + #[benchmark] + fn as_multi_create( + s: Linear<2, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, Weight::zero()) - verify { + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, Weight::zero()); + assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + + Ok(()) } - as_multi_approve { - // Signatories, need at least 3 people (so we don't complete the multisig) - let s in 3 .. T::MaxSignatories::get(); - // Transaction Length - let z in 0 .. 10_000; + /// `z`: Transaction Length + /// `s`: Signatories, need at least 3 people (so we don't complete the multisig) + #[benchmark] + fn as_multi_approve( + s: Linear<3, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); @@ -97,22 +106,43 @@ benchmarks! { // before the call, get the timepoint let timepoint = Multisig::::timepoint(); // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; + Multisig::::as_multi( + RawOrigin::Signed(caller).into(), + s as u16, + signatories, + None, + call.clone(), + Weight::zero(), + )?; let caller2 = signatories2.remove(0); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::zero()) - verify { - let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + as_multi( + RawOrigin::Signed(caller2), + s as u16, + signatories2, + Some(timepoint), + call, + Weight::zero(), + ); + + let multisig = + Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; assert_eq!(multisig.approvals.len(), 2); + + Ok(()) } - as_multi_complete { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get(); - // Transaction Length - let z in 0 .. 10_000; + /// `z`: Transaction Length + /// `s`: Signatories, need at least 2 people + #[benchmark] + fn as_multi_complete( + s: Linear<2, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); @@ -121,47 +151,87 @@ benchmarks! { // before the call, get the timepoint let timepoint = Multisig::::timepoint(); // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; + Multisig::::as_multi( + RawOrigin::Signed(caller).into(), + s as u16, + signatories, + None, + call.clone(), + Weight::zero(), + )?; // Everyone except the first person approves - for i in 1 .. s - 1 { + for i in 1..s - 1 { let mut signatories_loop = signatories2.clone(); let caller_loop = signatories_loop.remove(i as usize); let o = RawOrigin::Signed(caller_loop).into(); - Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), Weight::zero())?; + Multisig::::as_multi( + o, + s as u16, + signatories_loop, + Some(timepoint), + call.clone(), + Weight::zero(), + )?; } let caller2 = signatories2.remove(0); assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::MAX) - verify { + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + as_multi( + RawOrigin::Signed(caller2), + s as u16, + signatories2, + Some(timepoint), + call, + Weight::MAX, + ); + assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); + + Ok(()) } - approve_as_multi_create { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get(); - // Transaction Length, not a component - let z = 10_000; + /// `z`: Transaction Length, not a component + /// `s`: Signatories, need at least 2 people + #[benchmark] + fn approve_as_multi_create( + s: Linear<2, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; let call_hash = call.using_encoded(blake2_256); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); + add_to_whitelist(caller_key.into()); + // Create the multi - }: approve_as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call_hash, Weight::zero()) - verify { + #[extrinsic_call] + approve_as_multi( + RawOrigin::Signed(caller), + s as u16, + signatories, + None, + call_hash, + Weight::zero(), + ); + assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + + Ok(()) } - approve_as_multi_approve { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get(); - // Transaction Length, not a component - let z = 10_000; + /// `z`: Transaction Length, not a component + /// `s`: Signatories, need at least 2 people + #[benchmark] + fn approve_as_multi_approve( + s: Linear<2, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let mut signatories2 = signatories.clone(); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); @@ -176,23 +246,37 @@ benchmarks! { signatories, None, call, - Weight::zero() + Weight::zero(), )?; let caller2 = signatories2.remove(0); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: approve_as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call_hash, Weight::zero()) - verify { - let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + approve_as_multi( + RawOrigin::Signed(caller2), + s as u16, + signatories2, + Some(timepoint), + call_hash, + Weight::zero(), + ); + + let multisig = + Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; assert_eq!(multisig.approvals.len(), 2); + + Ok(()) } - cancel_as_multi { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get(); - // Transaction Length, not a component - let z = 10_000; + /// `z`: Transaction Length, not a component + /// `s`: Signatories, need at least 2 people + #[benchmark] + fn cancel_as_multi( + s: Linear<2, { T::MaxSignatories::get() }>, + z: Linear<0, 10_000>, + ) -> Result<(), BenchmarkError> { let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; @@ -204,10 +288,14 @@ benchmarks! { assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) - verify { + add_to_whitelist(caller_key.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash); + assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); + + Ok(()) } impl_benchmark_test_suite!(Multisig, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/substrate/frame/multisig/src/lib.rs b/substrate/frame/multisig/src/lib.rs index 8faae73c7161..4a30b5c119b9 100644 --- a/substrate/frame/multisig/src/lib.rs +++ b/substrate/frame/multisig/src/lib.rs @@ -49,28 +49,15 @@ mod tests; pub mod weights; extern crate alloc; - use alloc::{boxed::Box, vec, vec::Vec}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{ - dispatch::{ - DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo, - PostDispatchInfo, - }, - ensure, - traits::{Currency, Get, ReservableCurrency}, - weights::Weight, - BoundedVec, -}; -use frame_system::{self as system, pallet_prelude::BlockNumberFor, RawOrigin}; -use scale_info::TypeInfo; -use sp_io::hashing::blake2_256; -use sp_runtime::{ - traits::{Dispatchable, TrailingZeroInput, Zero}, - DispatchError, RuntimeDebug, +use frame::{ + prelude::*, + traits::{Currency, ReservableCurrency}, }; +use frame_system::RawOrigin; pub use weights::WeightInfo; +/// Re-export all pallet items. pub use pallet::*; /// The log target of this pallet. @@ -127,11 +114,9 @@ enum CallOrHash { Hash([u8; 32]), } -#[frame_support::pallet] +#[frame::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { @@ -167,7 +152,7 @@ pub mod pallet { type MaxSignatories: Get; /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; + type WeightInfo: weights::WeightInfo; } /// The in-code storage version. @@ -641,8 +626,8 @@ impl Pallet { /// The current `Timepoint`. pub fn timepoint() -> Timepoint> { Timepoint { - height: >::block_number(), - index: >::extrinsic_index().unwrap_or_default(), + height: >::block_number(), + index: >::extrinsic_index().unwrap_or_default(), } } diff --git a/substrate/frame/multisig/src/migrations.rs b/substrate/frame/multisig/src/migrations.rs index e6402600d0d3..8d6e77813673 100644 --- a/substrate/frame/multisig/src/migrations.rs +++ b/substrate/frame/multisig/src/migrations.rs @@ -17,21 +17,15 @@ // Migrations for Multisig Pallet -use super::*; -use frame_support::{ - traits::{GetStorageVersion, OnRuntimeUpgrade, WrapperKeepOpaque}, - Identity, -}; - -#[cfg(feature = "try-runtime")] -use frame_support::ensure; +use crate::*; +use frame::prelude::*; pub mod v1 { use super::*; - type OpaqueCall = WrapperKeepOpaque<::RuntimeCall>; + type OpaqueCall = frame::traits::WrapperKeepOpaque<::RuntimeCall>; - #[frame_support::storage_alias] + #[frame::storage_alias] type Calls = StorageMap< Pallet, Identity, @@ -42,15 +36,14 @@ pub mod v1 { pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + fn pre_upgrade() -> Result, frame::try_runtime::TryRuntimeError> { log!(info, "Number of calls to refund and delete: {}", Calls::::iter().count()); Ok(Vec::new()) } fn on_runtime_upgrade() -> Weight { - use sp_runtime::Saturating; - + use frame::traits::ReservableCurrency as _; let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); @@ -76,7 +69,7 @@ pub mod v1 { } #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + fn post_upgrade(_state: Vec) -> Result<(), frame::try_runtime::TryRuntimeError> { ensure!( Calls::::iter().count() == 0, "there are some dangling calls that need to be destroyed and refunded" diff --git a/substrate/frame/multisig/src/tests.rs b/substrate/frame/multisig/src/tests.rs index 4f8a7a44243c..c5a98845270c 100644 --- a/substrate/frame/multisig/src/tests.rs +++ b/substrate/frame/multisig/src/tests.rs @@ -20,18 +20,13 @@ #![cfg(test)] use super::*; - use crate as pallet_multisig; -use frame_support::{ - assert_noop, assert_ok, derive_impl, - traits::{ConstU32, ConstU64, Contains}, -}; -use sp_runtime::{BuildStorage, TokenError}; +use frame::{prelude::*, runtime::prelude::*, testing_prelude::*}; type Block = frame_system::mocking::MockBlockU32; -frame_support::construct_runtime!( - pub enum Test { +construct_runtime!( + pub struct Test { System: frame_system, Balances: pallet_balances, Multisig: pallet_multisig, @@ -75,14 +70,14 @@ impl Config for Test { use pallet_balances::Call as BalancesCall; -pub fn new_test_ext() -> sp_io::TestExternalities { +pub fn new_test_ext() -> TestState { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 2)], } .assimilate_storage(&mut t) .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); + let mut ext = TestState::new(t); ext.execute_with(|| System::set_block_number(1)); ext } diff --git a/substrate/frame/multisig/src/weights.rs b/substrate/frame/multisig/src/weights.rs index ac1c1b23b030..fb263116ea62 100644 --- a/substrate/frame/multisig/src/weights.rs +++ b/substrate/frame/multisig/src/weights.rs @@ -46,9 +46,8 @@ #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - +// TODO update this in frame-weight-template.hbs +use frame::weights_prelude::*; /// Weight functions needed for `pallet_multisig`. pub trait WeightInfo { fn as_multi_threshold_1(z: u32, ) -> Weight; diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml index 40c1c9750614..8897c66419c7 100644 --- a/substrate/frame/proxy/Cargo.toml +++ b/substrate/frame/proxy/Cargo.toml @@ -18,43 +18,26 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["max-encoded-len"], workspace = true } scale-info = { features = ["derive"], workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -sp-io = { workspace = true } -sp-runtime = { workspace = true } +frame = { workspace = true, features = ["experimental", "runtime"] } [dev-dependencies] pallet-balances = { workspace = true, default-features = true } pallet-utility = { workspace = true, default-features = true } -sp-core = { workspace = true, default-features = true } [features] default = ["std"] std = [ "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-utility/std", + "frame/std", "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-utility/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", + "frame/try-runtime", "pallet-balances/try-runtime", "pallet-utility/try-runtime", - "sp-runtime/try-runtime", ] diff --git a/substrate/frame/proxy/src/benchmarking.rs b/substrate/frame/proxy/src/benchmarking.rs index 4081af49c243..eebb506bf374 100644 --- a/substrate/frame/proxy/src/benchmarking.rs +++ b/substrate/frame/proxy/src/benchmarking.rs @@ -22,9 +22,7 @@ use super::*; use crate::Pallet as Proxy; use alloc::{boxed::Box, vec}; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; -use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; -use sp_runtime::traits::Bounded; +use frame::benchmarking::prelude::*; const SEED: u32 = 0; @@ -80,24 +78,36 @@ fn add_announcements( Ok(()) } -benchmarks! { - proxy { - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; // In this case the caller is the "target" proxy let caller: T::AccountId = account("target", p - 1, SEED); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); // ... and "real" is the traditional caller. This is not a typo. let real: T::AccountId = whitelisted_caller(); let real_lookup = T::Lookup::unlookup(real); - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); - }: _(RawOrigin::Signed(caller), real_lookup, Some(T::ProxyType::default()), Box::new(call)) - verify { - assert_last_event::(Event::ProxyExecuted { result: Ok(()) }.into()) + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), real_lookup, Some(T::ProxyType::default()), Box::new(call)); + + assert_last_event::(Event::ProxyExecuted { result: Ok(()) }.into()); + + Ok(()) } - proxy_announced { - let a in 0 .. T::MaxPending::get() - 1; - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn proxy_announced( + a: Linear<0, { T::MaxPending::get() - 1 }>, + p: Linear<1, { T::MaxProxies::get() - 1 }>, + ) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; // In this case the caller is the "target" proxy let caller: T::AccountId = account("pure", 0, SEED); let delegate: T::AccountId = account("target", p - 1, SEED); @@ -106,43 +116,65 @@ benchmarks! { // ... and "real" is the traditional caller. This is not a typo. let real: T::AccountId = whitelisted_caller(); let real_lookup = T::Lookup::unlookup(real); - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); Proxy::::announce( RawOrigin::Signed(delegate.clone()).into(), real_lookup.clone(), T::CallHasher::hash_of(&call), )?; add_announcements::(a, Some(delegate.clone()), None)?; - }: _(RawOrigin::Signed(caller), delegate_lookup, real_lookup, Some(T::ProxyType::default()), Box::new(call)) - verify { - assert_last_event::(Event::ProxyExecuted { result: Ok(()) }.into()) + + #[extrinsic_call] + _( + RawOrigin::Signed(caller), + delegate_lookup, + real_lookup, + Some(T::ProxyType::default()), + Box::new(call), + ); + + assert_last_event::(Event::ProxyExecuted { result: Ok(()) }.into()); + + Ok(()) } - remove_announcement { - let a in 0 .. T::MaxPending::get() - 1; - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn remove_announcement( + a: Linear<0, { T::MaxPending::get() - 1 }>, + p: Linear<1, { T::MaxProxies::get() - 1 }>, + ) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; // In this case the caller is the "target" proxy let caller: T::AccountId = account("target", p - 1, SEED); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); // ... and "real" is the traditional caller. This is not a typo. let real: T::AccountId = whitelisted_caller(); let real_lookup = T::Lookup::unlookup(real); - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); Proxy::::announce( RawOrigin::Signed(caller.clone()).into(), real_lookup.clone(), T::CallHasher::hash_of(&call), )?; add_announcements::(a, Some(caller.clone()), None)?; - }: _(RawOrigin::Signed(caller.clone()), real_lookup, T::CallHasher::hash_of(&call)) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), real_lookup, T::CallHasher::hash_of(&call)); + let (announcements, _) = Announcements::::get(&caller); assert_eq!(announcements.len() as u32, a); + + Ok(()) } - reject_announcement { - let a in 0 .. T::MaxPending::get() - 1; - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn reject_announcement( + a: Linear<0, { T::MaxPending::get() - 1 }>, + p: Linear<1, { T::MaxProxies::get() - 1 }>, + ) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; // In this case the caller is the "target" proxy let caller: T::AccountId = account("target", p - 1, SEED); let caller_lookup = T::Lookup::unlookup(caller.clone()); @@ -150,22 +182,30 @@ benchmarks! { // ... and "real" is the traditional caller. This is not a typo. let real: T::AccountId = whitelisted_caller(); let real_lookup = T::Lookup::unlookup(real.clone()); - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); Proxy::::announce( RawOrigin::Signed(caller.clone()).into(), real_lookup, T::CallHasher::hash_of(&call), )?; add_announcements::(a, Some(caller.clone()), None)?; - }: _(RawOrigin::Signed(real), caller_lookup, T::CallHasher::hash_of(&call)) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(real), caller_lookup, T::CallHasher::hash_of(&call)); + let (announcements, _) = Announcements::::get(&caller); assert_eq!(announcements.len() as u32, a); + + Ok(()) } - announce { - let a in 0 .. T::MaxPending::get() - 1; - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn announce( + a: Linear<0, { T::MaxPending::get() - 1 }>, + p: Linear<1, { T::MaxProxies::get() - 1 }>, + ) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; // In this case the caller is the "target" proxy let caller: T::AccountId = account("target", p - 1, SEED); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); @@ -173,74 +213,101 @@ benchmarks! { let real: T::AccountId = whitelisted_caller(); let real_lookup = T::Lookup::unlookup(real.clone()); add_announcements::(a, Some(caller.clone()), None)?; - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); let call_hash = T::CallHasher::hash_of(&call); - }: _(RawOrigin::Signed(caller.clone()), real_lookup, call_hash) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), real_lookup, call_hash); + assert_last_event::(Event::Announced { real, proxy: caller, call_hash }.into()); + + Ok(()) } - add_proxy { - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn add_proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; let caller: T::AccountId = whitelisted_caller(); let real = T::Lookup::unlookup(account("target", T::MaxProxies::get(), SEED)); - }: _( - RawOrigin::Signed(caller.clone()), - real, - T::ProxyType::default(), - BlockNumberFor::::zero() - ) - verify { + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + real, + T::ProxyType::default(), + BlockNumberFor::::zero(), + ); + let (proxies, _) = Proxies::::get(caller); assert_eq!(proxies.len() as u32, p + 1); + + Ok(()) } - remove_proxy { - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn remove_proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; let caller: T::AccountId = whitelisted_caller(); let delegate = T::Lookup::unlookup(account("target", 0, SEED)); - }: _( - RawOrigin::Signed(caller.clone()), - delegate, - T::ProxyType::default(), - BlockNumberFor::::zero() - ) - verify { + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + delegate, + T::ProxyType::default(), + BlockNumberFor::::zero(), + ); + let (proxies, _) = Proxies::::get(caller); assert_eq!(proxies.len() as u32, p - 1); + + Ok(()) } - remove_proxies { - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn remove_proxies(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller.clone())) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + let (proxies, _) = Proxies::::get(caller); assert_eq!(proxies.len() as u32, 0); + + Ok(()) } - create_pure { - let p in 1 .. (T::MaxProxies::get() - 1) => add_proxies::(p, None)?; + #[benchmark] + fn create_pure(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> { + add_proxies::(p, None)?; let caller: T::AccountId = whitelisted_caller(); - }: _( - RawOrigin::Signed(caller.clone()), - T::ProxyType::default(), - BlockNumberFor::::zero(), - 0 - ) - verify { + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + T::ProxyType::default(), + BlockNumberFor::::zero(), + 0, + ); + let pure_account = Pallet::::pure_account(&caller, &T::ProxyType::default(), 0, None); - assert_last_event::(Event::PureCreated { - pure: pure_account, - who: caller, - proxy_type: T::ProxyType::default(), - disambiguation_index: 0, - }.into()); - } + assert_last_event::( + Event::PureCreated { + pure: pure_account, + who: caller, + proxy_type: T::ProxyType::default(), + disambiguation_index: 0, + } + .into(), + ); - kill_pure { - let p in 0 .. (T::MaxProxies::get() - 2); + Ok(()) + } + #[benchmark] + fn kill_pure(p: Linear<0, { T::MaxProxies::get() - 2 }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -248,17 +315,28 @@ benchmarks! { RawOrigin::Signed(whitelisted_caller()).into(), T::ProxyType::default(), BlockNumberFor::::zero(), - 0 + 0, )?; - let height = system::Pallet::::block_number(); - let ext_index = system::Pallet::::extrinsic_index().unwrap_or(0); + let height = frame_system::Pallet::::block_number(); + let ext_index = frame_system::Pallet::::extrinsic_index().unwrap_or(0); let pure_account = Pallet::::pure_account(&caller, &T::ProxyType::default(), 0, None); add_proxies::(p, Some(pure_account.clone()))?; ensure!(Proxies::::contains_key(&pure_account), "pure proxy not created"); - }: _(RawOrigin::Signed(pure_account.clone()), caller_lookup, T::ProxyType::default(), 0, height, ext_index) - verify { + + #[extrinsic_call] + _( + RawOrigin::Signed(pure_account.clone()), + caller_lookup, + T::ProxyType::default(), + 0, + height, + ext_index, + ); + assert!(!Proxies::::contains_key(&pure_account)); + + Ok(()) } impl_benchmark_test_suite!(Proxy, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/substrate/frame/proxy/src/lib.rs b/substrate/frame/proxy/src/lib.rs index c041880a59df..cc8aeedcc5f9 100644 --- a/substrate/frame/proxy/src/lib.rs +++ b/substrate/frame/proxy/src/lib.rs @@ -34,23 +34,12 @@ mod tests; pub mod weights; extern crate alloc; - use alloc::{boxed::Box, vec}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{ - dispatch::GetDispatchInfo, - ensure, - traits::{Currency, Get, InstanceFilter, IsSubType, IsType, OriginTrait, ReservableCurrency}, - BoundedVec, +use frame::{ + prelude::*, + traits::{Currency, ReservableCurrency}, }; -use frame_system::{self as system, ensure_signed, pallet_prelude::BlockNumberFor}; pub use pallet::*; -use scale_info::TypeInfo; -use sp_io::hashing::blake2_256; -use sp_runtime::{ - traits::{Dispatchable, Hash, Saturating, StaticLookup, TrailingZeroInput, Zero}, - DispatchError, DispatchResult, RuntimeDebug, -}; pub use weights::WeightInfo; type CallHashOf = <::CallHasher as Hash>::Output; @@ -96,11 +85,9 @@ pub struct Announcement { height: BlockNumber, } -#[frame_support::pallet] +#[frame::pallet] pub mod pallet { - use super::{DispatchResult, *}; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; + use super::*; #[pallet::pallet] pub struct Pallet(_); @@ -130,7 +117,7 @@ pub mod pallet { + Member + Ord + PartialOrd - + InstanceFilter<::RuntimeCall> + + frame::traits::InstanceFilter<::RuntimeCall> + Default + MaxEncodedLen; @@ -392,7 +379,7 @@ pub mod pallet { let announcement = Announcement { real: real.clone(), call_hash, - height: system::Pallet::::block_number(), + height: frame_system::Pallet::::block_number(), }; Announcements::::try_mutate(&who, |(ref mut pending, ref mut deposit)| { @@ -503,7 +490,7 @@ pub mod pallet { let def = Self::find_proxy(&real, &delegate, force_proxy_type)?; let call_hash = T::CallHasher::hash_of(&call); - let now = system::Pallet::::block_number(); + let now = frame_system::Pallet::::block_number(); Self::edit_announcements(&delegate, |ann| { ann.real != real || ann.call_hash != call_hash || @@ -639,8 +626,8 @@ impl Pallet { ) -> T::AccountId { let (height, ext_index) = maybe_when.unwrap_or_else(|| { ( - system::Pallet::::block_number(), - system::Pallet::::extrinsic_index().unwrap_or_default(), + frame_system::Pallet::::block_number(), + frame_system::Pallet::::extrinsic_index().unwrap_or_default(), ) }); let entropy = (b"modlpy/proxy____", who, height, ext_index, proxy_type, index) @@ -796,6 +783,7 @@ impl Pallet { real: T::AccountId, call: ::RuntimeCall, ) { + use frame::traits::{InstanceFilter as _, OriginTrait as _}; // This is a freshly authenticated new account, the origin restrictions doesn't apply. let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into(); origin.add_filter(move |c: &::RuntimeCall| { diff --git a/substrate/frame/proxy/src/tests.rs b/substrate/frame/proxy/src/tests.rs index 3edb96026a82..5baf9bb9e838 100644 --- a/substrate/frame/proxy/src/tests.rs +++ b/substrate/frame/proxy/src/tests.rs @@ -20,22 +20,14 @@ #![cfg(test)] use super::*; - use crate as proxy; use alloc::{vec, vec::Vec}; -use codec::{Decode, Encode}; -use frame_support::{ - assert_noop, assert_ok, derive_impl, - traits::{ConstU32, ConstU64, Contains}, -}; -use sp_core::H256; -use sp_runtime::{traits::BlakeTwo256, BuildStorage, DispatchError, RuntimeDebug}; +use frame::testing_prelude::*; type Block = frame_system::mocking::MockBlock; -frame_support::construct_runtime!( - pub enum Test - { +construct_runtime!( + pub struct Test { System: frame_system, Balances: pallet_balances, Proxy: proxy, @@ -86,7 +78,7 @@ impl Default for ProxyType { Self::Any } } -impl InstanceFilter for ProxyType { +impl frame::traits::InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { ProxyType::Any => true, @@ -136,20 +128,20 @@ use pallet_utility::{Call as UtilityCall, Event as UtilityEvent}; type SystemError = frame_system::Error; -pub fn new_test_ext() -> sp_io::TestExternalities { +pub fn new_test_ext() -> TestState { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 3)], } .assimilate_storage(&mut t) .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); + let mut ext = TestState::new(t); ext.execute_with(|| System::set_block_number(1)); ext } fn last_events(n: usize) -> Vec { - system::Pallet::::events() + frame_system::Pallet::::events() .into_iter() .rev() .take(n) @@ -286,7 +278,7 @@ fn delayed_requires_pre_announcement() { assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone()), e); let call_hash = BlakeTwo256::hash_of(&call); assert_ok!(Proxy::announce(RuntimeOrigin::signed(2), 1, call_hash)); - system::Pallet::::set_block_number(2); + frame_system::Pallet::::set_block_number(2); assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 2, 1, None, call.clone())); }); } @@ -304,7 +296,7 @@ fn proxy_announced_removes_announcement_and_returns_deposit() { let e = Error::::Unannounced; assert_noop!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone()), e); - system::Pallet::::set_block_number(2); + frame_system::Pallet::::set_block_number(2); assert_ok!(Proxy::proxy_announced(RuntimeOrigin::signed(0), 3, 1, None, call.clone())); let announcements = Announcements::::get(3); assert_eq!(announcements.0, vec![Announcement { real: 2, call_hash, height: 1 }]); diff --git a/substrate/frame/proxy/src/weights.rs b/substrate/frame/proxy/src/weights.rs index 3093298e3e54..eab2cb4b2683 100644 --- a/substrate/frame/proxy/src/weights.rs +++ b/substrate/frame/proxy/src/weights.rs @@ -46,8 +46,7 @@ #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; +use frame::weights_prelude::*; /// Weight functions needed for `pallet_proxy`. pub trait WeightInfo { diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index ade1095cc504..a8d2c0b3fc56 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -32,10 +32,17 @@ //! //! ## Usage //! -//! The main intended use of this crate is for it to be imported with its preludes: +//! This crate is organized into 3 stages: +//! +//! 1. preludes: `prelude`, `testing_prelude` and `runtime::prelude`, `benchmarking`, +//! `weights_prelude`, `try_runtime`. +//! 2. domain-specific modules: `traits`, `hashing`, `arithmetic` and `derive`. +//! 3. Accessing frame/substrate dependencies directly: `deps`. +//! +//! The main intended use of this crate is for it to be used with the former, preludes: //! //! ``` -//! # use polkadot_sdk_frame as frame; +//! use polkadot_sdk_frame as frame; //! #[frame::pallet] //! pub mod pallet { //! # use polkadot_sdk_frame as frame; @@ -49,36 +56,98 @@ //! pub struct Pallet(_); //! } //! +//! #[cfg(test)] //! pub mod tests { //! # use polkadot_sdk_frame as frame; //! use frame::testing_prelude::*; //! } //! +//! #[cfg(feature = "runtime-benchmarks")] +//! pub mod benchmarking { +//! # use polkadot_sdk_frame as frame; +//! use frame::benchmarking::prelude::*; +//! } +//! //! pub mod runtime { //! # use polkadot_sdk_frame as frame; //! use frame::runtime::prelude::*; //! } //! ``` //! -//! See: [`prelude`], [`testing_prelude`] and [`runtime::prelude`]. +//! If not in preludes, one can look into the domain-specific modules. Finally, if an import is +//! still not feasible, one can look into `deps`. //! -//! Please note that this crate can only be imported as `polkadot-sdk-frame` or `frame`. +//! This crate also uses a `runtime` feature to include all of the types and tools needed to build +//! FRAME-based runtimes. So, if you want to build a runtime with this, import it as //! -//! ## Documentation +//! ```text +//! polkadot-sdk-frame = { version = "foo", features = ["runtime"] } +//! ``` //! -//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html). +//! If you just want to build a pallet instead, import it as //! -//! ## Underlying dependencies +//! ```text +//! polkadot-sdk-frame = { version = "foo" } +//! ``` //! -//! This crate is an amalgamation of multiple other crates that are often used together to compose a -//! pallet. It is not necessary to use it, and it may fall short for certain purposes. +//! Notice that the preludes overlap since they have imports in common. More in detail: +//! - `testing_prelude` brings in frame `prelude` and `runtime::prelude`; +//! - `runtime::prelude` brings in frame `prelude`; +//! - `benchmarking` brings in frame `prelude`. //! -//! In short, this crate only re-exports types and traits from multiple sources. All of these -//! sources are listed (and re-exported again) in [`deps`]. +//! ## Naming +//! +//! Please note that this crate can only be imported as `polkadot-sdk-frame` or `frame`. This is due +//! to compatibility matters with `frame-support`. +//! +//! A typical pallet's `Cargo.toml` using this crate looks like: +//! +//! ```ignore +//! [dependencies] +//! codec = { features = ["max-encoded-len"], workspace = true } +//! scale-info = { features = ["derive"], workspace = true } +//! frame = { workspace = true, features = ["experimental", "runtime"] } +//! +//! [features] +//! default = ["std"] +//! std = [ +//! "codec/std", +//! "scale-info/std", +//! "frame/std", +//! ] +//! runtime-benchmarks = [ +//! "frame/runtime-benchmarks", +//! ] +//! try-runtime = [ +//! "frame/try-runtime", +//! ] +//! ``` +//! +//! ## Documentation +//! +//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html). //! //! ## WARNING: Experimental //! //! **This crate and all of its content is experimental, and should not yet be used in production.** +//! +//! ## Maintenance Note +//! +//! > Notes for the maintainers of this crate, describing how the re-exports and preludes should +//! > work. +//! +//! * Preludes should be extensive. The goal of this pallet is to be ONLY used with the preludes. +//! The domain-specific modules are just a backup, aiming to keep things organized. Don't hesitate +//! in adding more items to the main prelude. +//! * The only non-module, non-prelude items exported from the top level crate is the `pallet` +//! macro, such that we can have the `#[frame::pallet] mod pallet { .. }` syntax working. +//! * In most cases, you might want to create a domain-specific module, but also add it to the +//! preludes, such as `hashing`. +//! * The only items that should NOT be in preludes are those that have been placed in +//! `frame-support`/`sp-runtime`, but in truth are related to just one pallet. +//! * The currency related traits are kept out of the preludes to encourage a deliberate choice of +//! one over the other. +//! * `runtime::apis` should expose all common runtime APIs that all FRAME-based runtimes need. #![cfg_attr(not(feature = "std"), no_std)] #![cfg(feature = "experimental")] @@ -92,6 +161,9 @@ pub use frame_support::pallet_macros::{import_section, pallet_section}; /// The logging library of the runtime. Can normally be the classic `log` crate. pub use log; +#[doc(inline)] +pub use frame_support::storage_alias; + /// Macros used within the main [`pallet`] macro. /// /// Note: All of these macros are "stubs" and not really usable outside `#[pallet] mod pallet { .. @@ -128,6 +200,11 @@ pub mod prelude { #[doc(no_inline)] pub use frame_support::pallet_prelude::*; + /// Dispatch types from `frame-support`, other fundamental traits + #[doc(no_inline)] + pub use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; + pub use frame_support::traits::{Contains, IsSubType, OnRuntimeUpgrade}; + /// Pallet prelude of `frame-system`. #[doc(no_inline)] pub use frame_system::pallet_prelude::*; @@ -135,6 +212,78 @@ pub mod prelude { /// All FRAME-relevant derive macros. #[doc(no_inline)] pub use super::derive::*; + + /// All hashing related things + pub use super::hashing::*; + + /// Runtime traits + #[doc(no_inline)] + pub use sp_runtime::traits::{ + Bounded, DispatchInfoOf, Dispatchable, SaturatedConversion, Saturating, StaticLookup, + TrailingZeroInput, + }; + + /// Other error/result types for runtime + #[doc(no_inline)] + pub use sp_runtime::{DispatchErrorWithPostInfo, DispatchResultWithInfo, TokenError}; +} + +#[cfg(any(feature = "try-runtime", test))] +pub mod try_runtime { + pub use sp_runtime::TryRuntimeError; +} + +/// Prelude to be included in the `benchmarking.rs` of a pallet. +/// +/// It supports both the `benchmarking::v1::benchmarks` and `benchmarking::v2::benchmark` syntax. +/// +/// ``` +/// use polkadot_sdk_frame::benchmarking::prelude::*; +/// // rest of your code. +/// ``` +/// +/// It already includes `polkadot_sdk_frame::prelude::*` and `polkadot_sdk_frame::testing_prelude`. +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking { + mod shared { + pub use frame_benchmarking::{add_benchmark, v1::account, whitelist, whitelisted_caller}; + // all benchmarking functions. + pub use frame_benchmarking::benchmarking::*; + // The system origin, which is very often needed in benchmarking code. Might be tricky only + // if the pallet defines its own `#[pallet::origin]` and call it `RawOrigin`. + pub use frame_system::RawOrigin; + } + + #[deprecated( + note = "'The V1 benchmarking syntax is deprecated. Please use the V2 syntax. This warning may become a hard error any time after April 2025. For more info, see: https://github.com/paritytech/polkadot-sdk/pull/5995" + )] + pub mod v1 { + pub use super::shared::*; + pub use frame_benchmarking::benchmarks; + } + + pub mod prelude { + pub use super::shared::*; + pub use crate::prelude::*; + pub use frame_benchmarking::v2::*; + } +} + +/// Prelude to be included in the `weight.rs` of each pallet. +/// +/// ``` +/// pub use polkadot_sdk_frame::weights_prelude::*; +/// ``` +pub mod weights_prelude { + pub use core::marker::PhantomData; + pub use frame_support::{ + traits::Get, + weights::{ + constants::{ParityDbWeight, RocksDbWeight}, + Weight, + }, + }; + pub use frame_system; } /// The main testing prelude of FRAME. @@ -145,9 +294,13 @@ pub mod prelude { /// use polkadot_sdk_frame::testing_prelude::*; /// // rest of your test setup. /// ``` +/// +/// This automatically brings in `polkadot_sdk_frame::prelude::*` and +/// `polkadot_sdk_frame::runtime::prelude::*`. #[cfg(feature = "std")] pub mod testing_prelude { - pub use super::prelude::*; + pub use crate::{prelude::*, runtime::prelude::*}; + /// Testing includes building a runtime, so we bring in all preludes related to runtimes as /// well. pub use super::runtime::testing_prelude::*; @@ -159,6 +312,10 @@ pub mod testing_prelude { }; pub use frame_system::{self, mocking::*}; + + #[deprecated(note = "Use `frame::testing_prelude::TestExternalities` instead.")] + pub use sp_io::TestExternalities; + pub use sp_io::TestExternalities as TestState; } @@ -170,9 +327,13 @@ pub mod runtime { /// A runtime typically starts with: /// /// ``` - /// use polkadot_sdk_frame::{prelude::*, runtime::prelude::*}; + /// use polkadot_sdk_frame::runtime::prelude::*; /// ``` + /// + /// This automatically brings in `polkadot_sdk_frame::prelude::*`. pub mod prelude { + pub use crate::prelude::*; + /// All of the types related to the FRAME runtime executive. pub use frame_executive::*; @@ -328,7 +489,6 @@ pub mod runtime { /// counter part of `runtime::prelude`. #[cfg(feature = "std")] pub mod testing_prelude { - pub use super::prelude::*; pub use sp_core::storage::Storage; pub use sp_runtime::BuildStorage; } @@ -350,12 +510,6 @@ pub mod arithmetic { pub use sp_arithmetic::{traits::*, *}; } -/// Low level primitive types used in FRAME pallets. -pub mod primitives { - pub use sp_core::{H160, H256, H512, U256, U512}; - pub use sp_runtime::traits::{BlakeTwo256, Hash, Keccak256}; -} - /// All derive macros used in frame. /// /// This is already part of the [`prelude`]. @@ -370,12 +524,17 @@ pub mod derive { pub use sp_runtime::RuntimeDebug; } -/// Access to all of the dependencies of this crate. In case the re-exports are not enough, this -/// module can be used. +pub mod hashing { + pub use sp_core::{hashing::*, H160, H256, H512, U256, U512}; + pub use sp_runtime::traits::{BlakeTwo256, Hash, Keccak256}; +} + +/// Access to all of the dependencies of this crate. In case the prelude re-exports are not enough, +/// this module can be used. /// -/// Any time one uses this module to access a dependency, you can have a moment to think about -/// whether this item could have been placed in any of the other modules and preludes in this crate. -/// In most cases, hopefully the answer is yes. +/// Note for maintainers: Any time one uses this module to access a dependency, you can have a +/// moment to think about whether this item could have been placed in any of the other modules and +/// preludes in this crate. In most cases, hopefully the answer is yes. pub mod deps { // TODO: It would be great to somehow instruct RA to prefer *not* suggesting auto-imports from // these. For example, we prefer `polkadot_sdk_frame::derive::CloneNoBound` rather than diff --git a/substrate/scripts/run_all_benchmarks.sh b/substrate/scripts/run_all_benchmarks.sh index fe5f89a5b56e..053c230fedb4 100755 --- a/substrate/scripts/run_all_benchmarks.sh +++ b/substrate/scripts/run_all_benchmarks.sh @@ -108,6 +108,13 @@ for PALLET in "${PALLETS[@]}"; do FOLDER="$(echo "${PALLET#*_}" | tr '_' '-')"; WEIGHT_FILE="./frame/${FOLDER}/src/weights.rs" + TEMPLATE_FILE_NAME="frame-weight-template.hbs" + if [ $(cargo metadata --locked --format-version 1 --no-deps | jq --arg pallet "${PALLET//_/-}" -r '.packages[] | select(.name == $pallet) | .dependencies | any(.name == "polkadot-sdk-frame")') = true ] + then + TEMPLATE_FILE_NAME="frame-umbrella-weight-template.hbs" + fi + TEMPLATE_FILE="./.maintain/${TEMPLATE_FILE_NAME}" + # Special handling of custom weight paths. if [ "$PALLET" == "frame_system_extensions" ] || [ "$PALLET" == "frame-system-extensions" ] then @@ -118,6 +125,9 @@ for PALLET in "${PALLETS[@]}"; do elif [ "$PALLET" == "pallet_asset_tx_payment" ] || [ "$PALLET" == "pallet-asset-tx-payment" ] then WEIGHT_FILE="./frame/transaction-payment/asset-tx-payment/src/weights.rs" + elif [ "$PALLET" == "pallet_asset_conversion_ops" ] || [ "$PALLET" == "pallet-asset-conversion-ops" ] + then + WEIGHT_FILE="./frame/asset-conversion/ops/src/weights.rs" fi echo "[+] Benchmarking $PALLET with weight file $WEIGHT_FILE"; @@ -133,7 +143,7 @@ for PALLET in "${PALLETS[@]}"; do --heap-pages=4096 \ --output="$WEIGHT_FILE" \ --header="./HEADER-APACHE2" \ - --template=./.maintain/frame-weight-template.hbs 2>&1 + --template="$TEMPLATE_FILE" 2>&1 ) if [ $? -ne 0 ]; then echo "$OUTPUT" >> "$ERR_FILE" @@ -173,4 +183,4 @@ if [ -f "$ERR_FILE" ]; then else echo "[+] All benchmarks passed." exit 0 -fi +fi \ No newline at end of file diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs index b8a8614932a6..722b606079f9 100644 --- a/templates/minimal/pallets/template/src/lib.rs +++ b/templates/minimal/pallets/template/src/lib.rs @@ -5,6 +5,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use frame::prelude::*; use polkadot_sdk::polkadot_sdk_frame as frame; // Re-export all pallet parts, this is needed to properly import the pallet into the runtime. @@ -19,4 +20,7 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + + #[pallet::storage] + pub type Value = StorageValue; } diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml index 74a09b9396e5..b803c74539ef 100644 --- a/templates/minimal/runtime/Cargo.toml +++ b/templates/minimal/runtime/Cargo.toml @@ -13,7 +13,6 @@ publish = false codec = { workspace = true } scale-info = { workspace = true } polkadot-sdk = { workspace = true, features = [ - "experimental", "pallet-balances", "pallet-sudo", "pallet-timestamp", diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 4f914a823bf6..304e50af2508 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -30,7 +30,7 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_sdk::{ polkadot_sdk_frame::{ self as frame, - prelude::*, + deps::sp_genesis_builder, runtime::{apis, prelude::*}, }, *, @@ -38,15 +38,14 @@ use polkadot_sdk::{ /// Provides getters for genesis configuration presets. pub mod genesis_config_presets { + use super::*; use crate::{ interface::{Balance, MinimumBalance}, - sp_genesis_builder::PresetId, sp_keyring::AccountKeyring, BalancesConfig, RuntimeGenesisConfig, SudoConfig, }; use alloc::{vec, vec::Vec}; - use polkadot_sdk::{sp_core::Get, sp_genesis_builder}; use serde_json::Value; /// Returns a development genesis config preset. @@ -314,17 +313,17 @@ impl_runtime_apis! { } } - impl sp_genesis_builder::GenesisBuilder for Runtime { + impl apis::GenesisBuilder for Runtime { fn build_state(config: Vec) -> sp_genesis_builder::Result { build_state::(config) } - fn get_preset(id: &Option) -> Option> { + fn get_preset(id: &Option) -> Option> { get_preset::(id, self::genesis_config_presets::get_preset) } - fn preset_names() -> Vec { - crate::genesis_config_presets::preset_names() + fn preset_names() -> Vec { + self::genesis_config_presets::preset_names() } } } From 65ee48136e4ab62a3ac32156b28bf87edb9c6984 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 29 Oct 2024 21:21:48 +0800 Subject: [PATCH 54/58] Migrate pallet-vesting benchmark to v2 (#6254) Part of: - #6202. --- substrate/frame/vesting/src/benchmarking.rs | 348 ++++++++++-------- substrate/primitives/panic-handler/src/lib.rs | 4 +- 2 files changed, 192 insertions(+), 160 deletions(-) diff --git a/substrate/frame/vesting/src/benchmarking.rs b/substrate/frame/vesting/src/benchmarking.rs index 736dd6eac1a8..503655243fb4 100644 --- a/substrate/frame/vesting/src/benchmarking.rs +++ b/substrate/frame/vesting/src/benchmarking.rs @@ -19,13 +19,12 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::{v2::*, BenchmarkError}; use frame_support::assert_ok; -use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin}; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul}; -use super::{Vesting as VestingStorage, *}; -use crate::Pallet as Vesting; +use crate::*; const SEED: u32 = 0; @@ -35,7 +34,7 @@ type BalanceOf = fn add_locks(who: &T::AccountId, n: u8) { for id in 0..n { let lock_id = [id; 8]; - let locked = 256u32; + let locked = 256_u32; let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; T::Currency::set_lock(lock_id, who, locked.into(), reasons); } @@ -46,12 +45,12 @@ fn add_vesting_schedules( n: u32, ) -> Result, &'static str> { let min_transfer = T::MinVestedTransfer::get(); - let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); + let locked = min_transfer.checked_mul(&20_u32.into()).unwrap(); // Schedule has a duration of 20. let per_block = min_transfer; - let starting_block = 1u32; + let starting_block = 1_u32; - let source: T::AccountId = account("source", 0, SEED); + let source = account("source", 0, SEED); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); T::BlockNumberProvider::set_block_number(BlockNumberFor::::zero()); @@ -61,7 +60,7 @@ fn add_vesting_schedules( total_locked += locked; let schedule = VestingInfo::new(locked, per_block, starting_block.into()); - assert_ok!(Vesting::::do_vested_transfer(&source, target, schedule)); + assert_ok!(Pallet::::do_vested_transfer(&source, target, schedule)); // Top up to guarantee we can always transfer another schedule. T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); @@ -70,66 +69,76 @@ fn add_vesting_schedules( Ok(total_locked) } -benchmarks! { - vest_locked { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MAX_VESTING_SCHEDULES; +#[benchmarks] +mod benchmarks { + use super::*; - let caller: T::AccountId = whitelisted_caller(); + #[benchmark] + fn vest_locked( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<1, T::MAX_VESTING_SCHEDULES>, + ) -> Result<(), BenchmarkError> { + let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); let expected_balance = add_vesting_schedules::(&caller, s)?; // At block zero, everything is vested. - assert_eq!(System::::block_number(), BlockNumberFor::::zero()); + assert_eq!(frame_system::Pallet::::block_number(), BlockNumberFor::::zero()); assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting schedule not added", ); - }: vest(RawOrigin::Signed(caller.clone())) - verify { + + #[extrinsic_call] + vest(RawOrigin::Signed(caller.clone())); + // Nothing happened since everything is still vested. assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting schedule was removed", ); - } - vest_unlocked { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MAX_VESTING_SCHEDULES; + Ok(()) + } - let caller: T::AccountId = whitelisted_caller(); + #[benchmark] + fn vest_unlocked( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<1, T::MAX_VESTING_SCHEDULES>, + ) -> Result<(), BenchmarkError> { + let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); add_vesting_schedules::(&caller, s)?; // At block 21, everything is unlocked. - T::BlockNumberProvider::set_block_number(21u32.into()); + T::BlockNumberProvider::set_block_number(21_u32.into()); assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(BalanceOf::::zero()), "Vesting schedule still active", ); - }: vest(RawOrigin::Signed(caller.clone())) - verify { + + #[extrinsic_call] + vest(RawOrigin::Signed(caller.clone())); + // Vesting schedule is removed! - assert_eq!( - Vesting::::vesting_balance(&caller), - None, - "Vesting schedule was not removed", - ); - } + assert_eq!(Pallet::::vesting_balance(&caller), None, "Vesting schedule was not removed",); - vest_other_locked { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MAX_VESTING_SCHEDULES; + Ok(()) + } - let other: T::AccountId = account("other", 0, SEED); + #[benchmark] + fn vest_other_locked( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<1, T::MAX_VESTING_SCHEDULES>, + ) -> Result<(), BenchmarkError> { + let other = account::("other", 0, SEED); let other_lookup = T::Lookup::unlookup(other.clone()); T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); @@ -137,64 +146,70 @@ benchmarks! { let expected_balance = add_vesting_schedules::(&other, s)?; // At block zero, everything is vested. - assert_eq!(System::::block_number(), BlockNumberFor::::zero()); + assert_eq!(frame_system::Pallet::::block_number(), BlockNumberFor::::zero()); assert_eq!( - Vesting::::vesting_balance(&other), + Pallet::::vesting_balance(&other), Some(expected_balance), "Vesting schedule not added", ); - let caller: T::AccountId = whitelisted_caller(); - }: vest_other(RawOrigin::Signed(caller.clone()), other_lookup) - verify { + let caller = whitelisted_caller::(); + + #[extrinsic_call] + vest_other(RawOrigin::Signed(caller.clone()), other_lookup); + // Nothing happened since everything is still vested. assert_eq!( - Vesting::::vesting_balance(&other), + Pallet::::vesting_balance(&other), Some(expected_balance), "Vesting schedule was removed", ); - } - vest_other_unlocked { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MAX_VESTING_SCHEDULES; + Ok(()) + } - let other: T::AccountId = account("other", 0, SEED); + #[benchmark] + fn vest_other_unlocked( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<1, { T::MAX_VESTING_SCHEDULES }>, + ) -> Result<(), BenchmarkError> { + let other = account::("other", 0, SEED); let other_lookup = T::Lookup::unlookup(other.clone()); T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); add_locks::(&other, l as u8); add_vesting_schedules::(&other, s)?; // At block 21 everything is unlocked. - T::BlockNumberProvider::set_block_number(21u32.into()); + T::BlockNumberProvider::set_block_number(21_u32.into()); assert_eq!( - Vesting::::vesting_balance(&other), + Pallet::::vesting_balance(&other), Some(BalanceOf::::zero()), "Vesting schedule still active", ); - let caller: T::AccountId = whitelisted_caller(); - }: vest_other(RawOrigin::Signed(caller.clone()), other_lookup) - verify { + let caller = whitelisted_caller::(); + + #[extrinsic_call] + vest_other(RawOrigin::Signed(caller.clone()), other_lookup); + // Vesting schedule is removed. - assert_eq!( - Vesting::::vesting_balance(&other), - None, - "Vesting schedule was not removed", - ); - } + assert_eq!(Pallet::::vesting_balance(&other), None, "Vesting schedule was not removed",); - vested_transfer { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MAX_VESTING_SCHEDULES - 1; + Ok(()) + } - let caller: T::AccountId = whitelisted_caller(); + #[benchmark] + fn vested_transfer( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<0, { T::MAX_VESTING_SCHEDULES - 1 }>, + ) -> Result<(), BenchmarkError> { + let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let target: T::AccountId = account("target", 0, SEED); + let target = account::("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - // Give target existing locks + // Give target existing locks. T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); add_locks::(&target, l as u8); // Add one vesting schedules. @@ -202,74 +217,75 @@ benchmarks! { let mut expected_balance = add_vesting_schedules::(&target, s)?; let transfer_amount = T::MinVestedTransfer::get(); - let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); + let per_block = transfer_amount.checked_div(&20_u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::new( - transfer_amount, - per_block, - 1u32.into(), - ); - }: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule) - verify { + let vesting_schedule = VestingInfo::new(transfer_amount, per_block, 1_u32.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), target_lookup, vesting_schedule); + assert_eq!( orig_balance + expected_balance, T::Currency::free_balance(&target), "Transfer didn't happen", ); assert_eq!( - Vesting::::vesting_balance(&target), + Pallet::::vesting_balance(&target), Some(expected_balance), "Lock not correctly updated", ); - } - force_vested_transfer { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MAX_VESTING_SCHEDULES - 1; + Ok(()) + } - let source: T::AccountId = account("source", 0, SEED); + #[benchmark] + fn force_vested_transfer( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<0, { T::MAX_VESTING_SCHEDULES - 1 }>, + ) -> Result<(), BenchmarkError> { + let source = account::("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); - let target: T::AccountId = account("target", 0, SEED); + let target = account::("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - // Give target existing locks + // Give target existing locks. T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); add_locks::(&target, l as u8); - // Add one less than max vesting schedules + // Add one less than max vesting schedules. let orig_balance = T::Currency::free_balance(&target); let mut expected_balance = add_vesting_schedules::(&target, s)?; let transfer_amount = T::MinVestedTransfer::get(); - let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); + let per_block = transfer_amount.checked_div(&20_u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::new( - transfer_amount, - per_block, - 1u32.into(), - ); - }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) - verify { + let vesting_schedule = VestingInfo::new(transfer_amount, per_block, 1_u32.into()); + + #[extrinsic_call] + _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule); + assert_eq!( orig_balance + expected_balance, T::Currency::free_balance(&target), "Transfer didn't happen", ); assert_eq!( - Vesting::::vesting_balance(&target), + Pallet::::vesting_balance(&target), Some(expected_balance), - "Lock not correctly updated", - ); - } + "Lock not correctly updated", + ); - not_unlocking_merge_schedules { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MAX_VESTING_SCHEDULES; + Ok(()) + } - let caller: T::AccountId = account("caller", 0, SEED); - let caller_lookup = T::Lookup::unlookup(caller.clone()); + #[benchmark] + fn not_unlocking_merge_schedules( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<2, { T::MAX_VESTING_SCHEDULES }>, + ) -> Result<(), BenchmarkError> { + let caller = whitelisted_caller::(); // Give target existing locks. T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); @@ -277,115 +293,127 @@ benchmarks! { let expected_balance = add_vesting_schedules::(&caller, s)?; // Schedules are not vesting at block 0. - assert_eq!(System::::block_number(), BlockNumberFor::::zero()); + assert_eq!(frame_system::Pallet::::block_number(), BlockNumberFor::::zero()); assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting balance should equal sum locked of all schedules", ); assert_eq!( - VestingStorage::::get(&caller).unwrap().len(), + Vesting::::get(&caller).unwrap().len(), s as usize, "There should be exactly max vesting schedules" ); - }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) - verify { + + #[extrinsic_call] + merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1); + let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), - 1u32.into(), + T::MinVestedTransfer::get() * 20_u32.into() * 2_u32.into(), + T::MinVestedTransfer::get() * 2_u32.into(), + 1_u32.into(), ); let expected_index = (s - 2) as usize; + assert_eq!(Vesting::::get(&caller).unwrap()[expected_index], expected_schedule); assert_eq!( - VestingStorage::::get(&caller).unwrap()[expected_index], - expected_schedule - ); - assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting balance should equal total locked of all schedules", ); assert_eq!( - VestingStorage::::get(&caller).unwrap().len(), + Vesting::::get(&caller).unwrap().len(), (s - 1) as usize, "Schedule count should reduce by 1" ); - } - unlocking_merge_schedules { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MAX_VESTING_SCHEDULES; + Ok(()) + } + #[benchmark] + fn unlocking_merge_schedules( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<2, { T::MAX_VESTING_SCHEDULES }>, + ) -> Result<(), BenchmarkError> { // Destination used just for currency transfers in asserts. let test_dest: T::AccountId = account("test_dest", 0, SEED); - let caller: T::AccountId = account("caller", 0, SEED); - let caller_lookup = T::Lookup::unlookup(caller.clone()); - // Give target other locks. + let caller = whitelisted_caller::(); + // Give target existing locks. T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); // Add max vesting schedules. let total_transferred = add_vesting_schedules::(&caller, s)?; - // Go to about half way through all the schedules duration. (They all start at 1, and have a duration of 20 or 21). - T::BlockNumberProvider::set_block_number(11u32.into()); - // We expect half the original locked balance (+ any remainder that vests on the last block). - let expected_balance = total_transferred / 2u32.into(); + // Go to about half way through all the schedules duration. (They all start at 1, and have a + // duration of 20 or 21). + T::BlockNumberProvider::set_block_number(11_u32.into()); + // We expect half the original locked balance (+ any remainder that vests on the last + // block). + let expected_balance = total_transferred / 2_u32.into(); assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting balance should reflect that we are half way through all schedules duration", ); assert_eq!( - VestingStorage::::get(&caller).unwrap().len(), + Vesting::::get(&caller).unwrap().len(), s as usize, "There should be exactly max vesting schedules" ); // The balance is not actually transferable because it has not been unlocked. - assert!(T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath).is_err()); - }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) - verify { + assert!(T::Currency::transfer( + &caller, + &test_dest, + expected_balance, + ExistenceRequirement::AllowDeath + ) + .is_err()); + + #[extrinsic_call] + merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1); + let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), - 11u32.into(), + T::MinVestedTransfer::get() * 2_u32.into() * 10_u32.into(), + T::MinVestedTransfer::get() * 2_u32.into(), + 11_u32.into(), ); let expected_index = (s - 2) as usize; assert_eq!( - VestingStorage::::get(&caller).unwrap()[expected_index], + Vesting::::get(&caller).unwrap()[expected_index], expected_schedule, "New schedule is properly created and placed" ); assert_eq!( - VestingStorage::::get(&caller).unwrap()[expected_index], - expected_schedule - ); - assert_eq!( - Vesting::::vesting_balance(&caller), + Pallet::::vesting_balance(&caller), Some(expected_balance), "Vesting balance should equal half total locked of all schedules", ); assert_eq!( - VestingStorage::::get(&caller).unwrap().len(), + Vesting::::get(&caller).unwrap().len(), (s - 1) as usize, "Schedule count should reduce by 1" ); // Since merge unlocks all schedules we can now transfer the balance. - assert_ok!( - T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath) - ); + assert_ok!(T::Currency::transfer( + &caller, + &test_dest, + expected_balance, + ExistenceRequirement::AllowDeath + )); + + Ok(()) } -force_remove_vesting_schedule { - let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MAX_VESTING_SCHEDULES; - - let source: T::AccountId = account("source", 0, SEED); - let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); + #[benchmark] + fn force_remove_vesting_schedule( + l: Linear<0, { MaxLocksOf::::get() - 1 }>, + s: Linear<2, { T::MAX_VESTING_SCHEDULES }>, + ) -> Result<(), BenchmarkError> { + let source = account::("source", 0, SEED); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + let target = account::("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); // Give target existing locks. @@ -394,18 +422,22 @@ force_remove_vesting_schedule { // The last vesting schedule. let schedule_index = s - 1; - }: _(RawOrigin::Root, target_lookup, schedule_index) - verify { + + #[extrinsic_call] + _(RawOrigin::Root, target_lookup, schedule_index); + assert_eq!( - VestingStorage::::get(&target).unwrap().len(), + Vesting::::get(&target).unwrap().len(), schedule_index as usize, "Schedule count should reduce by 1" ); + + Ok(()) } - impl_benchmark_test_suite!( - Vesting, + impl_benchmark_test_suite! { + Pallet, crate::mock::ExtBuilder::default().existential_deposit(256).build(), - crate::mock::Test, - ); + crate::mock::Test + } } diff --git a/substrate/primitives/panic-handler/src/lib.rs b/substrate/primitives/panic-handler/src/lib.rs index c4a7eb8dc67c..81ccaaee828e 100644 --- a/substrate/primitives/panic-handler/src/lib.rs +++ b/substrate/primitives/panic-handler/src/lib.rs @@ -30,7 +30,7 @@ use std::{ cell::Cell, io::{self, Write}, marker::PhantomData, - panic::{self, PanicInfo}, + panic::{self, PanicHookInfo}, sync::LazyLock, thread, }; @@ -149,7 +149,7 @@ fn strip_control_codes(input: &str) -> std::borrow::Cow { } /// Function being called when a panic happens. -fn panic_hook(info: &PanicInfo, report_url: &str, version: &str) { +fn panic_hook(info: &PanicHookInfo, report_url: &str, version: &str) { let location = info.location(); let file = location.as_ref().map(|l| l.file()).unwrap_or(""); let line = location.as_ref().map(|l| l.line()).unwrap_or(0); From b41419ae42c083b536fb23ce44f7cc8a8a1fd9f6 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 29 Oct 2024 21:29:15 +0800 Subject: [PATCH 55/58] Migrate pallet-timestamp benchmark to v2 (#6258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of: - #6202 --------- Co-authored-by: Dónal Murray --- substrate/frame/timestamp/src/benchmarking.rs | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/substrate/frame/timestamp/src/benchmarking.rs b/substrate/frame/timestamp/src/benchmarking.rs index d8c27b4967af..1ba8c4195de9 100644 --- a/substrate/frame/timestamp/src/benchmarking.rs +++ b/substrate/frame/timestamp/src/benchmarking.rs @@ -19,43 +19,63 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; -use frame_benchmarking::v1::benchmarks; -use frame_support::{ensure, traits::OnFinalize}; +use frame_benchmarking::{benchmarking::add_to_whitelist, v2::*}; +use frame_support::traits::OnFinalize; use frame_system::RawOrigin; use sp_storage::TrackedStorageKey; -use crate::{Now, Pallet as Timestamp}; +use crate::*; const MAX_TIME: u32 = 100; -benchmarks! { - set { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn set() { let t = MAX_TIME; // Ignore write to `DidUpdate` since it transient. - let did_update_key = crate::DidUpdate::::hashed_key().to_vec(); - frame_benchmarking::benchmarking::add_to_whitelist(TrackedStorageKey { + let did_update_key = DidUpdate::::hashed_key().to_vec(); + add_to_whitelist(TrackedStorageKey { key: did_update_key, reads: 0, writes: 1, whitelisted: false, }); - }: _(RawOrigin::None, t.into()) - verify { - ensure!(Now::::get() == t.into(), "Time was not set."); + + #[extrinsic_call] + _(RawOrigin::None, t.into()); + + assert_eq!(Now::::get(), t.into(), "Time was not set."); } - on_finalize { + #[benchmark] + fn on_finalize() { let t = MAX_TIME; - Timestamp::::set(RawOrigin::None.into(), t.into())?; - ensure!(DidUpdate::::exists(), "Time was not set."); + Pallet::::set(RawOrigin::None.into(), t.into()).unwrap(); + assert!(DidUpdate::::exists(), "Time was not set."); + // Ignore read/write to `DidUpdate` since it is transient. - let did_update_key = crate::DidUpdate::::hashed_key().to_vec(); - frame_benchmarking::benchmarking::add_to_whitelist(did_update_key.into()); - }: { Timestamp::::on_finalize(t.into()); } - verify { - ensure!(!DidUpdate::::exists(), "Time was not removed."); + let did_update_key = DidUpdate::::hashed_key().to_vec(); + add_to_whitelist(TrackedStorageKey { + key: did_update_key, + reads: 0, + writes: 1, + whitelisted: false, + }); + + #[block] + { + Pallet::::on_finalize(t.into()); + } + + assert!(!DidUpdate::::exists(), "Time was not removed."); } - impl_benchmark_test_suite!(Timestamp, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite! { + Pallet, + mock::new_test_ext(), + mock::Test + } } From f5bfe0177264c8b26919d68c69824ecf0f4b2d25 Mon Sep 17 00:00:00 2001 From: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:07:09 +0200 Subject: [PATCH 56/58] [Identity] Decouple usernames from identities (#5554) This PR refactors `pallet-identity` to decouple usernames from identities. Main changes in this PR: - Separate usernames from identities in storage, allowing for correct deposit accounting - Introduce the option for username authorities to put up a deposit to issue a username - Allow authorities to remove usernames by declaring the intent to do so, then removing the username after the grace period expires - Refactor the authority storage to be keyed by suffix rather than owner account. - Introduce the concept of a system provider for a username, different from a governance allocation, allowing for usernames set by the system and not a specific authority - Implement multi-block migration to enable all of the changes described above --------- Signed-off-by: georgepisaltu Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> --- Cargo.lock | 4 + .../runtimes/people/people-rococo/Cargo.toml | 4 + .../runtimes/people/people-rococo/src/lib.rs | 24 + .../people/people-rococo/src/people.rs | 3 + .../people/people-rococo/src/weights/mod.rs | 1 + .../src/weights/pallet_identity.rs | 43 +- .../src/weights/pallet_migrations.rs | 172 +++ .../runtimes/people/people-westend/Cargo.toml | 4 + .../runtimes/people/people-westend/src/lib.rs | 24 + .../people/people-westend/src/people.rs | 3 + .../people/people-westend/src/weights/mod.rs | 1 + .../src/weights/pallet_identity.rs | 43 +- .../src/weights/pallet_migrations.rs | 172 +++ .../runtime/common/src/integration_tests.rs | 2 + polkadot/runtime/rococo/Cargo.toml | 4 + polkadot/runtime/rococo/src/lib.rs | 27 + polkadot/runtime/rococo/src/weights/mod.rs | 1 + .../rococo/src/weights/pallet_identity.rs | 45 +- .../rococo/src/weights/pallet_migrations.rs | 173 +++ polkadot/runtime/westend/Cargo.toml | 4 + polkadot/runtime/westend/src/lib.rs | 28 + polkadot/runtime/westend/src/weights/mod.rs | 1 + .../westend/src/weights/pallet_identity.rs | 43 +- .../westend/src/weights/pallet_migrations.rs | 173 +++ prdoc/pr_5554.prdoc | 31 + substrate/bin/node/runtime/src/impls.rs | 2 +- substrate/bin/node/runtime/src/lib.rs | 3 + substrate/frame/alliance/src/mock.rs | 6 +- substrate/frame/identity/README.md | 27 +- substrate/frame/identity/src/benchmarking.rs | 263 +++- substrate/frame/identity/src/lib.rs | 568 +++++---- substrate/frame/identity/src/migration.rs | 764 +++++++++++- substrate/frame/identity/src/tests.rs | 1066 ++++++++++++----- substrate/frame/identity/src/types.rs | 39 +- substrate/frame/identity/src/weights.rs | 98 +- 35 files changed, 3217 insertions(+), 649 deletions(-) create mode 100644 cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_migrations.rs create mode 100644 cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_migrations.rs create mode 100644 polkadot/runtime/rococo/src/weights/pallet_migrations.rs create mode 100644 polkadot/runtime/westend/src/weights/pallet_migrations.rs create mode 100644 prdoc/pr_5554.prdoc diff --git a/Cargo.lock b/Cargo.lock index 127e30d666af..d091e46e9bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13721,6 +13721,7 @@ dependencies = [ "pallet-collator-selection", "pallet-identity", "pallet-message-queue", + "pallet-migrations", "pallet-multisig", "pallet-proxy", "pallet-session", @@ -13821,6 +13822,7 @@ dependencies = [ "pallet-collator-selection", "pallet-identity", "pallet-message-queue", + "pallet-migrations", "pallet-multisig", "pallet-proxy", "pallet-session", @@ -17958,6 +17960,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-migrations", "pallet-mmr", "pallet-multisig", "pallet-nis", @@ -26626,6 +26629,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-migrations", "pallet-mmr", "pallet-multisig", "pallet-nomination-pools", diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index 373b82639def..34458c2352fb 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -30,6 +30,7 @@ pallet-authorship = { workspace = true } pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } +pallet-migrations = { workspace = true } pallet-multisig = { workspace = true } pallet-proxy = { workspace = true } pallet-session = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-collator-selection/std", "pallet-identity/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-multisig/std", "pallet-proxy/std", "pallet-session/std", @@ -154,6 +156,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", @@ -185,6 +188,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-multisig/try-runtime", "pallet-proxy/try-runtime", "pallet-session/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index f9499f9d1ebe..bb290c0af97d 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -193,6 +193,7 @@ impl frame_system::Config for Runtime { type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } impl pallet_timestamp::Config for Runtime { @@ -536,6 +537,25 @@ impl identity_migrator::Config for Runtime { type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = pallet_identity::migration::v2::LazyMigrationV1ToV2; + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = weights::pallet_migrations::WeightInfo; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -571,6 +591,9 @@ construct_runtime!( // The main stage. Identity: pallet_identity = 50, + // Migrations pallet + MultiBlockMigrations: pallet_migrations = 98, + // To migrate deposits IdentityMigrator: identity_migrator = 248, } @@ -589,6 +612,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_migrations, MultiBlockMigrations] [pallet_transaction_payment, TransactionPayment] // Polkadot [polkadot_runtime_common::identity_migrator, IdentityMigrator] diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/people.rs b/cumulus/parachains/runtimes/people/people-rococo/src/people.rs index 8211447d68c8..690bb974bd17 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/people.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/people.rs @@ -36,6 +36,7 @@ parameter_types! { // 17 | Min size without `IdentityInfo` (accounted for in byte deposit) pub const BasicDeposit: Balance = deposit(1, 17); pub const ByteDeposit: Balance = deposit(0, 1); + pub const UsernameDeposit: Balance = deposit(0, 32); pub const SubAccountDeposit: Balance = deposit(1, 53); pub RelayTreasuryAccount: AccountId = parachains_common::TREASURY_PALLET_ID.into_account_truncating(); @@ -46,6 +47,7 @@ impl pallet_identity::Config for Runtime { type Currency = Balances; type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = ConstU32<100>; type IdentityInformation = IdentityInfo; @@ -57,6 +59,7 @@ impl pallet_identity::Config for Runtime { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>; + type UsernameGracePeriod = ConstU32<{ 3 * DAYS }>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = weights::pallet_identity::WeightInfo; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs index 58480231f068..fab3c629ab3f 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; +pub mod pallet_migrations; pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_identity.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_identity.rs index 1e8ba87e2510..dfc522ab3b51 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_identity.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_identity.rs @@ -340,7 +340,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: `Identity::AccountOfUsername` (`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(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -368,7 +368,7 @@ impl pallet_identity::WeightInfo for WeightInfo { } /// Storage: `Identity::PendingUsernames` (r:1 w:1) /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + fn remove_expired_approval(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3542` @@ -392,18 +392,31 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `126` - // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32, ) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_migrations.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_migrations.rs new file mode 100644 index 000000000000..61857ac8202a --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_migrations.rs @@ -0,0 +1,172 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Need to rerun! + +#![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_migrations`. +pub struct WeightInfo(PhantomData); +impl pallet_migrations::WeightInfo for WeightInfo { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_762_000 picoseconds. + Weight::from_parts(8_100_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_077_000 picoseconds. + Weight::from_parts(2_138_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 5_868_000 picoseconds. + Weight::from_parts(6_143_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_283_000 picoseconds. + Weight::from_parts(10_964_000, 3795) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 9_900_000 picoseconds. + Weight::from_parts(10_396_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_411_000 picoseconds. + Weight::from_parts(11_956_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_398_000 picoseconds. + Weight::from_parts(12_910_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 166_000 picoseconds. + Weight::from_parts(193_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_686_000 picoseconds. + Weight::from_parts(2_859_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_070_000 picoseconds. + Weight::from_parts(3_250_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_901_000 picoseconds. + Weight::from_parts(6_320_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 15_952_000 picoseconds. + Weight::from_parts(14_358_665, 3834) + // Standard Error: 3_358 + .saturating_add(Weight::from_parts(1_323_674, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} \ No newline at end of file diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index efb67adba49d..6840b97d8c3f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -30,6 +30,7 @@ pallet-authorship = { workspace = true } pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } +pallet-migrations = { workspace = true } pallet-multisig = { workspace = true } pallet-proxy = { workspace = true } pallet-session = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-collator-selection/std", "pallet-identity/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-multisig/std", "pallet-proxy/std", "pallet-session/std", @@ -154,6 +156,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", @@ -185,6 +188,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-multisig/try-runtime", "pallet-proxy/try-runtime", "pallet-session/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 7e3cd1670fe5..7a586504badb 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -192,6 +192,7 @@ impl frame_system::Config for Runtime { type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } impl pallet_timestamp::Config for Runtime { @@ -535,6 +536,25 @@ impl identity_migrator::Config for Runtime { type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = pallet_identity::migration::v2::LazyMigrationV1ToV2; + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = weights::pallet_migrations::WeightInfo; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -570,6 +590,9 @@ construct_runtime!( // The main stage. Identity: pallet_identity = 50, + // Migrations pallet + MultiBlockMigrations: pallet_migrations = 98, + // To migrate deposits IdentityMigrator: identity_migrator = 248, } @@ -588,6 +611,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_migrations, MultiBlockMigrations] // Polkadot [polkadot_runtime_common::identity_migrator, IdentityMigrator] // Cumulus diff --git a/cumulus/parachains/runtimes/people/people-westend/src/people.rs b/cumulus/parachains/runtimes/people/people-westend/src/people.rs index 0255fd074b11..47551f6d4bdc 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/people.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/people.rs @@ -36,6 +36,7 @@ parameter_types! { // 17 | Min size without `IdentityInfo` (accounted for in byte deposit) pub const BasicDeposit: Balance = deposit(1, 17); pub const ByteDeposit: Balance = deposit(0, 1); + pub const UsernameDeposit: Balance = deposit(0, 32); pub const SubAccountDeposit: Balance = deposit(1, 53); pub RelayTreasuryAccount: AccountId = parachains_common::TREASURY_PALLET_ID.into_account_truncating(); @@ -46,6 +47,7 @@ impl pallet_identity::Config for Runtime { type Currency = Balances; type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = ConstU32<100>; type IdentityInformation = IdentityInfo; @@ -57,6 +59,7 @@ impl pallet_identity::Config for Runtime { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>; + type UsernameGracePeriod = ConstU32<{ 3 * DAYS }>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = weights::pallet_identity::WeightInfo; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs index 58480231f068..fab3c629ab3f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; +pub mod pallet_migrations; pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_identity.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_identity.rs index 1e8ba87e2510..dfc522ab3b51 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_identity.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_identity.rs @@ -340,7 +340,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: `Identity::AccountOfUsername` (`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(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -368,7 +368,7 @@ impl pallet_identity::WeightInfo for WeightInfo { } /// Storage: `Identity::PendingUsernames` (r:1 w:1) /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + fn remove_expired_approval(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3542` @@ -392,18 +392,31 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `126` - // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32, ) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_migrations.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_migrations.rs new file mode 100644 index 000000000000..61857ac8202a --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_migrations.rs @@ -0,0 +1,172 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Need to rerun! + +#![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_migrations`. +pub struct WeightInfo(PhantomData); +impl pallet_migrations::WeightInfo for WeightInfo { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_762_000 picoseconds. + Weight::from_parts(8_100_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_077_000 picoseconds. + Weight::from_parts(2_138_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 5_868_000 picoseconds. + Weight::from_parts(6_143_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_283_000 picoseconds. + Weight::from_parts(10_964_000, 3795) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 9_900_000 picoseconds. + Weight::from_parts(10_396_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_411_000 picoseconds. + Weight::from_parts(11_956_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_398_000 picoseconds. + Weight::from_parts(12_910_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 166_000 picoseconds. + Weight::from_parts(193_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_686_000 picoseconds. + Weight::from_parts(2_859_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_070_000 picoseconds. + Weight::from_parts(3_250_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_901_000 picoseconds. + Weight::from_parts(6_320_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 15_952_000 picoseconds. + Weight::from_parts(14_358_665, 3834) + // Standard Error: 3_358 + .saturating_add(Weight::from_parts(1_323_674, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} \ No newline at end of file diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index bfeed04a919f..8a76a138305e 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -288,6 +288,7 @@ impl pallet_identity::Config for Test { type Slashed = (); type BasicDeposit = ConstU32<100>; type ByteDeposit = ConstU32<10>; + type UsernameDeposit = ConstU32<10>; type SubAccountDeposit = ConstU32<100>; type MaxSubAccounts = ConstU32<2>; type IdentityInformation = IdentityInfo>; @@ -298,6 +299,7 @@ impl pallet_identity::Config for Test { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<100>; + type UsernameGracePeriod = ConstU32<10>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = (); diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 6bcb0da3d999..3b11c977edf3 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -66,6 +66,7 @@ pallet-identity = { workspace = true } pallet-indices = { workspace = true } pallet-membership = { workspace = true } pallet-message-queue = { workspace = true } +pallet-migrations = { workspace = true } pallet-mmr = { workspace = true } pallet-multisig = { workspace = true } pallet-nis = { workspace = true } @@ -157,6 +158,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-mmr/std", "pallet-multisig/std", "pallet-nis/std", @@ -239,6 +241,7 @@ runtime-benchmarks = [ "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nis/runtime-benchmarks", @@ -297,6 +300,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nis/try-runtime", diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 44dd820f8c3c..5bcde612cf1b 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -224,6 +224,7 @@ impl frame_system::Config for Runtime { type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } parameter_types! { @@ -712,6 +713,7 @@ parameter_types! { // Minimum 100 bytes/ROC deposited (1 CENT/byte) pub const BasicDeposit: Balance = 1000 * CENTS; // 258 bytes on-chain pub const ByteDeposit: Balance = deposit(0, 1); + pub const UsernameDeposit: Balance = deposit(0, 32); pub const SubAccountDeposit: Balance = 200 * CENTS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -723,6 +725,7 @@ impl pallet_identity::Config for Runtime { type Currency = Balances; type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; type IdentityInformation = IdentityInfo; @@ -734,6 +737,7 @@ impl pallet_identity::Config for Runtime { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>; + type UsernameGracePeriod = ConstU32<{ 30 * DAYS }>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = weights::pallet_identity::WeightInfo; @@ -1393,6 +1397,25 @@ impl validator_manager::Config for Runtime { type PrivilegedOrigin = EnsureRoot; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = pallet_identity::migration::v2::LazyMigrationV1ToV2; + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = weights::pallet_migrations::WeightInfo; +} + impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -1525,6 +1548,9 @@ construct_runtime! { Crowdloan: crowdloan = 73, Coretime: coretime = 74, + // Migrations pallet + MultiBlockMigrations: pallet_migrations = 98, + // Pallet for sending XCM. XcmPallet: pallet_xcm = 99, @@ -1790,6 +1816,7 @@ mod benches { [pallet_identity, Identity] [pallet_indices, Indices] [pallet_message_queue, MessageQueue] + [pallet_migrations, MultiBlockMigrations] [pallet_mmr, Mmr] [pallet_multisig, Multisig] [pallet_parameters, Parameters] diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 99477baeb281..1c030c444ac5 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_conviction_voting; pub mod pallet_identity; pub mod pallet_indices; pub mod pallet_message_queue; +pub mod pallet_migrations; pub mod pallet_mmr; pub mod pallet_multisig; pub mod pallet_nis; diff --git a/polkadot/runtime/rococo/src/weights/pallet_identity.rs b/polkadot/runtime/rococo/src/weights/pallet_identity.rs index 6df16351f2c2..8b0bf7ce826a 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_identity.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_identity.rs @@ -369,7 +369,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -396,8 +396,8 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + fn remove_expired_approval(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `3550` @@ -421,18 +421,31 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `11037` - // Minimum execution time: 10_829_000 picoseconds. - Weight::from_parts(11_113_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32, ) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_migrations.rs b/polkadot/runtime/rococo/src/weights/pallet_migrations.rs new file mode 100644 index 000000000000..4fa07a23bb8a --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_migrations.rs @@ -0,0 +1,173 @@ +// Copyright (C) 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 . + +// Need to rerun! + +#![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_migrations`. +pub struct WeightInfo(PhantomData); +impl pallet_migrations::WeightInfo for WeightInfo { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_762_000 picoseconds. + Weight::from_parts(8_100_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_077_000 picoseconds. + Weight::from_parts(2_138_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 5_868_000 picoseconds. + Weight::from_parts(6_143_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_283_000 picoseconds. + Weight::from_parts(10_964_000, 3795) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 9_900_000 picoseconds. + Weight::from_parts(10_396_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_411_000 picoseconds. + Weight::from_parts(11_956_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_398_000 picoseconds. + Weight::from_parts(12_910_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 166_000 picoseconds. + Weight::from_parts(193_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_686_000 picoseconds. + Weight::from_parts(2_859_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_070_000 picoseconds. + Weight::from_parts(3_250_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_901_000 picoseconds. + Weight::from_parts(6_320_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 15_952_000 picoseconds. + Weight::from_parts(14_358_665, 3834) + // Standard Error: 3_358 + .saturating_add(Weight::from_parts(1_323_674, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} \ No newline at end of file diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index fcb5719de895..f94301baab09 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -69,6 +69,7 @@ pallet-identity = { workspace = true } pallet-indices = { workspace = true } pallet-membership = { workspace = true } pallet-message-queue = { workspace = true } +pallet-migrations = { workspace = true } pallet-mmr = { workspace = true } pallet-multisig = { workspace = true } pallet-nomination-pools = { workspace = true } @@ -169,6 +170,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-mmr/std", "pallet-multisig/std", "pallet-nomination-pools-benchmarking?/std", @@ -259,6 +261,7 @@ runtime-benchmarks = [ "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", @@ -321,6 +324,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 4941d91df57d..970ef5318994 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -224,6 +224,7 @@ impl frame_system::Config for Runtime { type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } parameter_types! { @@ -948,6 +949,7 @@ parameter_types! { // Minimum 100 bytes/KSM deposited (1 CENT/byte) pub const BasicDeposit: Balance = 1000 * CENTS; // 258 bytes on-chain pub const ByteDeposit: Balance = deposit(0, 1); + pub const UsernameDeposit: Balance = deposit(0, 32); pub const SubAccountDeposit: Balance = 200 * CENTS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -960,6 +962,7 @@ impl pallet_identity::Config for Runtime { type Slashed = (); type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; type IdentityInformation = IdentityInfo; @@ -970,6 +973,7 @@ impl pallet_identity::Config for Runtime { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>; + type UsernameGracePeriod = ConstU32<{ 30 * DAYS }>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = weights::pallet_identity::WeightInfo; @@ -1531,6 +1535,25 @@ impl pallet_root_testing::Config for Runtime { type RuntimeEvent = RuntimeEvent; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = pallet_identity::migration::v2::LazyMigrationV1ToV2; + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = weights::pallet_migrations::WeightInfo; +} + parameter_types! { // The deposit configuration for the singed migration. Specially if you want to allow any signed account to do the migration (see `SignedFilter`, these deposits should be high) pub const MigrationSignedDepositPerItem: Balance = 1 * CENTS; @@ -1730,6 +1753,10 @@ mod runtime { #[runtime::pallet_index(66)] pub type Coretime = coretime; + // Migrations pallet + #[runtime::pallet_index(98)] + pub type MultiBlockMigrations = pallet_migrations; + // Pallet for sending XCM. #[runtime::pallet_index(99)] pub type XcmPallet = pallet_xcm; @@ -1866,6 +1893,7 @@ mod benches { [pallet_identity, Identity] [pallet_indices, Indices] [pallet_message_queue, MessageQueue] + [pallet_migrations, MultiBlockMigrations] [pallet_mmr, Mmr] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index 8c12c1adb9ca..efd18b38545a 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -28,6 +28,7 @@ pub mod pallet_fast_unstake; pub mod pallet_identity; pub mod pallet_indices; pub mod pallet_message_queue; +pub mod pallet_migrations; pub mod pallet_mmr; pub mod pallet_multisig; pub mod pallet_nomination_pools; diff --git a/polkadot/runtime/westend/src/weights/pallet_identity.rs b/polkadot/runtime/westend/src/weights/pallet_identity.rs index dc7061615c95..60899dd4d173 100644 --- a/polkadot/runtime/westend/src/weights/pallet_identity.rs +++ b/polkadot/runtime/westend/src/weights/pallet_identity.rs @@ -366,7 +366,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: `Identity::AccountOfUsername` (`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(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -394,7 +394,7 @@ impl pallet_identity::WeightInfo for WeightInfo { } /// Storage: `Identity::PendingUsernames` (r:1 w:1) /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + fn remove_expired_approval(_p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3542` @@ -418,18 +418,31 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `126` - // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32, ) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } diff --git a/polkadot/runtime/westend/src/weights/pallet_migrations.rs b/polkadot/runtime/westend/src/weights/pallet_migrations.rs new file mode 100644 index 000000000000..4fa07a23bb8a --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_migrations.rs @@ -0,0 +1,173 @@ +// Copyright (C) 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 . + +// Need to rerun! + +#![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_migrations`. +pub struct WeightInfo(PhantomData); +impl pallet_migrations::WeightInfo for WeightInfo { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_762_000 picoseconds. + Weight::from_parts(8_100_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_077_000 picoseconds. + Weight::from_parts(2_138_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 5_868_000 picoseconds. + Weight::from_parts(6_143_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_283_000 picoseconds. + Weight::from_parts(10_964_000, 3795) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 9_900_000 picoseconds. + Weight::from_parts(10_396_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_411_000 picoseconds. + Weight::from_parts(11_956_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_398_000 picoseconds. + Weight::from_parts(12_910_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 166_000 picoseconds. + Weight::from_parts(193_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_686_000 picoseconds. + Weight::from_parts(2_859_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_070_000 picoseconds. + Weight::from_parts(3_250_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_901_000 picoseconds. + Weight::from_parts(6_320_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 15_952_000 picoseconds. + Weight::from_parts(14_358_665, 3834) + // Standard Error: 3_358 + .saturating_add(Weight::from_parts(1_323_674, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} \ No newline at end of file diff --git a/prdoc/pr_5554.prdoc b/prdoc/pr_5554.prdoc new file mode 100644 index 000000000000..3ebf00b38ed7 --- /dev/null +++ b/prdoc/pr_5554.prdoc @@ -0,0 +1,31 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Identity Decouple usernames from identities + +doc: + - audience: [Runtime Dev, Runtime User] + description: | + This PR refactors pallet-identity to decouple usernames from identities. Usernames are now + separated from identities in storage, allowing for correct deposit accounting and for + authorities to put up their own deposit to create a username and remove usernames. Various + storage maps had to be refactored and migrated to allow this to happen. The call to remove a + dangling username is now replaced by the permissioned `kill_username` call. + +crates: + - name: pallet-alliance + bump: major + - name: pallet-identity + bump: major + - name: rococo-runtime + bump: major + - name: westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major + - name: polkadot-runtime-common + bump: major + - name: kitchensink-runtime + bump: major \ No newline at end of file diff --git a/substrate/bin/node/runtime/src/impls.rs b/substrate/bin/node/runtime/src/impls.rs index 43e7a766e0e8..2e096342451d 100644 --- a/substrate/bin/node/runtime/src/impls.rs +++ b/substrate/bin/node/runtime/src/impls.rs @@ -65,7 +65,7 @@ impl IdentityVerifier for AllianceIdentityVerifier { fn has_good_judgement(who: &AccountId) -> bool { use pallet_identity::{IdentityOf, Judgement}; IdentityOf::::get(who) - .map(|(registration, _)| registration.judgements) + .map(|registration| registration.judgements) .map_or(false, |judgements| { judgements .iter() diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 2dbc6ab39e73..12e8dc3e5077 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1570,6 +1570,7 @@ parameter_types! { // information, already accounted for by the byte deposit pub const BasicDeposit: Balance = deposit(1, 17); pub const ByteDeposit: Balance = deposit(0, 1); + pub const UsernameDeposit: Balance = deposit(0, 32); pub const SubAccountDeposit: Balance = 2 * DOLLARS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -1581,6 +1582,7 @@ impl pallet_identity::Config for Runtime { type Currency = Balances; type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; type IdentityInformation = IdentityInfo; @@ -1592,6 +1594,7 @@ impl pallet_identity::Config for Runtime { type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>; + type UsernameGracePeriod = ConstU32<{ 30 * DAYS }>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = pallet_identity::weights::SubstrateWeight; diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 5442e8779020..625cabf3457f 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -85,11 +85,13 @@ impl pallet_collective::Config for Test { parameter_types! { pub const BasicDeposit: u64 = 100; pub const ByteDeposit: u64 = 10; + pub const UsernameDeposit: u64 = 10; pub const SubAccountDeposit: u64 = 100; pub const MaxSubAccounts: u32 = 2; pub const MaxAdditionalFields: u32 = 2; pub const MaxRegistrars: u32 = 20; pub const PendingUsernameExpiration: u64 = 100; + pub const UsernameGracePeriod: u64 = 10; } ord_parameter_types! { pub const One: u64 = 1; @@ -106,6 +108,7 @@ impl pallet_identity::Config for Test { type Currency = Balances; type BasicDeposit = BasicDeposit; type ByteDeposit = ByteDeposit; + type UsernameDeposit = UsernameDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; type IdentityInformation = IdentityInfo; @@ -117,6 +120,7 @@ impl pallet_identity::Config for Test { type SigningPublicKey = AccountU64; type UsernameAuthorityOrigin = EnsureOneOrRoot; type PendingUsernameExpiration = PendingUsernameExpiration; + type UsernameGracePeriod = UsernameGracePeriod; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = (); @@ -149,7 +153,7 @@ impl IdentityVerifier for AllianceIdentityVerifier { fn has_good_judgement(who: &AccountId) -> bool { if let Some(judgements) = - IdentityOf::::get(who).map(|(registration, _)| registration.judgements) + IdentityOf::::get(who).map(|registration| registration.judgements) { judgements .iter() diff --git a/substrate/frame/identity/README.md b/substrate/frame/identity/README.md index 94b2ae0231d7..32b75d159a9b 100644 --- a/substrate/frame/identity/README.md +++ b/substrate/frame/identity/README.md @@ -27,15 +27,24 @@ no state-bloat attack is viable. #### Usernames -The pallet provides functionality for username authorities to issue usernames. When an account -receives a username, they get a default instance of `IdentityInfo`. Usernames also serve as a -reverse lookup from username to account. +The pallet provides functionality for username authorities to issue usernames, which are independent +of the identity information functionality; an account can set: +- an identity without setting a username +- a username without setting an identity +- an identity and a username -Username authorities are given an allocation by governance to prevent state bloat. Usernames -impose no cost or deposit on the user. +The username functionality implemented in this pallet is meant to be a user friendly lookup of +accounts. There are mappings in both directions, "account -> username" and "username -> account". -Users can have multiple usernames that map to the same `AccountId`, however one `AccountId` can -only map to a single username, known as the *primary*. +To grant a username, a username authority can either: +- be given an allocation by governance of a specific amount of usernames to issue for free, + without any deposit associated with storage costs; +- put up a deposit for each username it issues (usually a subsidized, reduced deposit, relative + to other deposits in the system). + +Users can have multiple usernames that map to the same `AccountId`, however one `AccountId` can only +map to a single username, known as the _primary_. This primary username will be the result of a +lookup in the `UsernameOf` map for any given account. ### Interface @@ -50,7 +59,7 @@ only map to a single username, known as the *primary*. - `accept_username` - Accept a username issued by a username authority. - `remove_expired_approval` - Remove a username that was issued but never accepted. - `set_primary_username` - Set a given username as an account's primary. -- `remove_dangling_username` - Remove a username that maps to an account without an identity. +- `remove_username` - Remove a username after its grace period has ended. ##### For General Users with Sub-Identities - `set_subs` - Set the sub-accounts of an identity. @@ -66,12 +75,14 @@ only map to a single username, known as the *primary*. ##### For Username Authorities - `set_username_for` - Set a username for a given account. The account must approve it. +- `unbind_username` - Start the grace period for a username. ##### For Superusers - `add_registrar` - Add a new registrar to the system. - `kill_identity` - Forcibly remove the associated identity; the deposit is lost. - `add_username_authority` - Add an account with the ability to issue usernames. - `remove_username_authority` - Remove an account with the ability to issue usernames. +- `kill_username` - Forcibly remove a username. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index ab04000c2281..bab581e92540 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use crate::Pallet as Identity; +use crate::{migration::v2::LazyMigrationV1ToV2, Pallet as Identity}; use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::{ @@ -593,19 +593,19 @@ mod benchmarks { assert_ok!(Identity::::add_username_authority( origin.clone(), authority_lookup.clone(), - suffix, + suffix.clone(), allocation )); #[extrinsic_call] - _(origin as T::RuntimeOrigin, authority_lookup); + _(origin as T::RuntimeOrigin, suffix.into(), authority_lookup); assert_last_event::(Event::::AuthorityRemoved { authority }.into()); Ok(()) } #[benchmark] - fn set_username_for() -> Result<(), BenchmarkError> { + fn set_username_for(p: Linear<0, 1>) -> Result<(), BenchmarkError> { // Set up a username authority. let auth_origin = T::UsernameAuthorityOrigin::try_successful_origin().expect("can generate origin"); @@ -613,6 +613,7 @@ mod benchmarks { let authority_lookup = T::Lookup::unlookup(authority.clone()); let suffix = bench_suffix(); let allocation = 10; + let _ = T::Currency::make_free_balance_be(&authority, BalanceOf::::max_value()); Identity::::add_username_authority( auth_origin, @@ -634,9 +635,20 @@ mod benchmarks { // Verify signature here to avoid surprise errors at runtime assert!(signature.verify(&bounded_username[..], &public.into())); + let use_allocation = match p { + 0 => false, + 1 => true, + _ => unreachable!(), + }; #[extrinsic_call] - _(RawOrigin::Signed(authority.clone()), who_lookup, username, Some(signature.into())); + set_username_for( + RawOrigin::Signed(authority.clone()), + who_lookup, + bounded_username.clone().into(), + Some(signature.into()), + use_allocation, + ); assert_has_event::( Event::::UsernameSet { @@ -648,6 +660,15 @@ mod benchmarks { assert_has_event::( Event::::PrimaryUsernameSet { who: who_account, username: bounded_username }.into(), ); + if use_allocation { + let suffix: Suffix = suffix.try_into().unwrap(); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 9); + } else { + assert_eq!( + T::Currency::free_balance(&authority), + BalanceOf::::max_value() - T::UsernameDeposit::get() + ); + } Ok(()) } @@ -656,7 +677,7 @@ mod benchmarks { let caller: T::AccountId = whitelisted_caller(); let username = bounded_username::(bench_username(), bench_suffix()); - Identity::::queue_acceptance(&caller, username.clone()); + Identity::::queue_acceptance(&caller, username.clone(), Provider::Allocation); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), username.clone()); @@ -666,10 +687,35 @@ mod benchmarks { } #[benchmark] - fn remove_expired_approval() -> Result<(), BenchmarkError> { + fn remove_expired_approval(p: Linear<0, 1>) -> Result<(), BenchmarkError> { + // Set up a username authority. + let auth_origin = + T::UsernameAuthorityOrigin::try_successful_origin().expect("can generate origin"); + let authority: T::AccountId = account("authority", 0, SEED); + let authority_lookup = T::Lookup::unlookup(authority.clone()); + let suffix = bench_suffix(); + let allocation = 10; + let _ = T::Currency::make_free_balance_be(&authority, BalanceOf::::max_value()); + + Identity::::add_username_authority( + auth_origin, + authority_lookup, + suffix.clone(), + allocation, + )?; + let caller: T::AccountId = whitelisted_caller(); - let username = bounded_username::(bench_username(), bench_suffix()); - Identity::::queue_acceptance(&caller, username.clone()); + let username = bounded_username::(bench_username(), suffix.clone()); + let username_deposit = T::UsernameDeposit::get(); + let provider = match p { + 0 => { + let _ = T::Currency::reserve(&authority, username_deposit); + Provider::AuthorityDeposit(username_deposit) + }, + 1 => Provider::Allocation, + _ => unreachable!(), + }; + Identity::::queue_acceptance(&caller, username.clone(), provider); let expected_expiration = frame_system::Pallet::::block_number() + T::PendingUsernameExpiration::get(); @@ -680,6 +726,16 @@ mod benchmarks { _(RawOrigin::Signed(caller.clone()), username); assert_last_event::(Event::::PreapprovalExpired { whose: caller }.into()); + match p { + 0 => { + assert_eq!(T::Currency::free_balance(&authority), BalanceOf::::max_value()); + }, + 1 => { + let suffix: Suffix = suffix.try_into().unwrap(); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); + }, + _ => unreachable!(), + } Ok(()) } @@ -690,8 +746,8 @@ mod benchmarks { let second_username = bounded_username::(b"slowbenchmark".to_vec(), bench_suffix()); // First one will be set as primary. Second will not be. - Identity::::insert_username(&caller, first_username); - Identity::::insert_username(&caller, second_username.clone()); + Identity::::insert_username(&caller, first_username, Provider::Allocation); + Identity::::insert_username(&caller, second_username.clone(), Provider::Allocation); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), second_username.clone()); @@ -703,24 +759,185 @@ mod benchmarks { } #[benchmark] - fn remove_dangling_username() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - let first_username = bounded_username::(bench_username(), bench_suffix()); - let second_username = bounded_username::(b"slowbenchmark".to_vec(), bench_suffix()); + fn unbind_username() -> Result<(), BenchmarkError> { + // Set up a username authority. + let auth_origin = + T::UsernameAuthorityOrigin::try_successful_origin().expect("can generate origin"); + let authority: T::AccountId = account("authority", 0, SEED); + let authority_lookup = T::Lookup::unlookup(authority.clone()); + let suffix = bench_suffix(); + let allocation = 10; + let _ = T::Currency::make_free_balance_be(&authority, BalanceOf::::max_value()); - // First one will be set as primary. Second will not be. - Identity::::insert_username(&caller, first_username); - Identity::::insert_username(&caller, second_username.clone()); + Identity::::add_username_authority( + auth_origin, + authority_lookup, + suffix.clone(), + allocation, + )?; - // User calls `clear_identity`, leaving their second username as "dangling" - Identity::::clear_identity(RawOrigin::Signed(caller.clone()).into())?; + let caller: T::AccountId = whitelisted_caller(); + let username = bounded_username::(bench_username(), suffix.clone()); + + let username_deposit = T::UsernameDeposit::get(); + Identity::::insert_username( + &caller, + username.clone(), + Provider::AuthorityDeposit(username_deposit), + ); #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), second_username.clone()); + _(RawOrigin::Signed(authority), username.clone()); - assert_last_event::( - Event::::DanglingUsernameRemoved { who: caller, username: second_username }.into(), + assert_last_event::(Event::::UsernameUnbound { username }.into()); + Ok(()) + } + + #[benchmark] + fn remove_username() -> Result<(), BenchmarkError> { + // Set up a username authority. + let authority: T::AccountId = account("authority", 0, SEED); + let suffix = bench_suffix(); + let _ = T::Currency::make_free_balance_be(&authority, BalanceOf::::max_value()); + let caller: T::AccountId = whitelisted_caller(); + let username = bounded_username::(bench_username(), suffix.clone()); + + let username_deposit = T::UsernameDeposit::get(); + Identity::::insert_username( + &caller, + username.clone(), + Provider::AuthorityDeposit(username_deposit), ); + let now = frame_system::Pallet::::block_number(); + let expiry = now + T::UsernameGracePeriod::get(); + UnbindingUsernames::::insert(&username, expiry); + + frame_system::Pallet::::set_block_number(expiry); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), username.clone()); + + assert_last_event::(Event::::UsernameRemoved { username }.into()); + Ok(()) + } + + #[benchmark] + fn kill_username(p: Linear<0, 1>) -> Result<(), BenchmarkError> { + // Set up a username authority. + let auth_origin = + T::UsernameAuthorityOrigin::try_successful_origin().expect("can generate origin"); + let authority: T::AccountId = account("authority", 0, SEED); + let authority_lookup = T::Lookup::unlookup(authority.clone()); + let suffix = bench_suffix(); + let allocation = 10; + let _ = T::Currency::make_free_balance_be(&authority, BalanceOf::::max_value()); + + Identity::::add_username_authority( + auth_origin, + authority_lookup, + suffix.clone(), + allocation, + )?; + + let caller: T::AccountId = whitelisted_caller(); + let username = bounded_username::(bench_username(), suffix.clone()); + let username_deposit = T::UsernameDeposit::get(); + let provider = match p { + 0 => { + let _ = T::Currency::reserve(&authority, username_deposit); + Provider::AuthorityDeposit(username_deposit) + }, + 1 => Provider::Allocation, + _ => unreachable!(), + }; + Identity::::insert_username(&caller, username.clone(), provider); + UnbindingUsernames::::insert(&username, frame_system::Pallet::::block_number()); + + #[extrinsic_call] + _(RawOrigin::Root, username.clone()); + + assert_last_event::(Event::::UsernameKilled { username }.into()); + match p { + 0 => { + assert_eq!( + T::Currency::free_balance(&authority), + BalanceOf::::max_value() - username_deposit + ); + }, + 1 => { + let suffix: Suffix = suffix.try_into().unwrap(); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); + }, + _ => unreachable!(), + } + Ok(()) + } + + #[benchmark] + fn migration_v2_authority_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_migration(); + assert_eq!(AuthorityOf::::iter().count(), 0); + #[block] + { + LazyMigrationV1ToV2::::authority_step(None); + } + assert_eq!(AuthorityOf::::get(&setup.suffix).unwrap().account_id, setup.authority); + Ok(()) + } + + #[benchmark] + fn migration_v2_username_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_migration(); + assert_eq!(UsernameInfoOf::::iter().count(), 0); + #[block] + { + LazyMigrationV1ToV2::::username_step(None); + } + assert_eq!(UsernameInfoOf::::iter().next().unwrap().1.owner, setup.account); + Ok(()) + } + + #[benchmark] + fn migration_v2_identity_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_migration(); + #[block] + { + LazyMigrationV1ToV2::::identity_step(None); + } + assert!(IdentityOf::::get(&setup.account).is_some()); + Ok(()) + } + + #[benchmark] + fn migration_v2_pending_username_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_migration(); + #[block] + { + LazyMigrationV1ToV2::::pending_username_step(None); + } + assert!(PendingUsernames::::get(&setup.username).is_some()); + Ok(()) + } + + #[benchmark] + fn migration_v2_cleanup_authority_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_cleanup(); + #[block] + { + LazyMigrationV1ToV2::::cleanup_authority_step(None); + } + LazyMigrationV1ToV2::::check_authority_cleanup_validity(setup.suffix, setup.authority); + Ok(()) + } + + #[benchmark] + fn migration_v2_cleanup_username_step() -> Result<(), BenchmarkError> { + let setup = LazyMigrationV1ToV2::::setup_benchmark_env_for_cleanup(); + #[block] + { + LazyMigrationV1ToV2::::cleanup_username_step(None); + } + LazyMigrationV1ToV2::::check_username_cleanup_validity(setup.username, setup.account); Ok(()) } diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs index 08e29ddffd12..11b43f958c4e 100644 --- a/substrate/frame/identity/src/lib.rs +++ b/substrate/frame/identity/src/lib.rs @@ -42,15 +42,26 @@ //! //! ### Usernames //! -//! The pallet provides functionality for username authorities to issue usernames. When an account -//! receives a username, they get a default instance of `IdentityInfo`. Usernames also serve as a -//! reverse lookup from username to account. +//! The pallet provides functionality for username authorities to issue usernames, which are +//! independent of the identity information functionality; an account can set: +//! - an identity without setting a username +//! - a username without setting an identity +//! - an identity and a username //! -//! Username authorities are given an allocation by governance to prevent state bloat. Usernames -//! impose no cost or deposit on the user. +//! The username functionality implemented in this pallet is meant to be a user friendly lookup of +//! accounts. There are mappings in both directions, "account -> username" and "username -> +//! account". +//! +//! Usernames are granted by authorities and grouped by suffix, with each suffix being administered +//! by one authority. To grant a username, a username authority can either: +//! - be given an allocation by governance of a specific amount of usernames to issue for free, +//! without any deposit associated with storage costs; +//! - put up a deposit for each username it issues (usually a subsidized, reduced deposit, relative +//! to other deposits in the system) //! //! Users can have multiple usernames that map to the same `AccountId`, however one `AccountId` can -//! only map to a single username, known as the _primary_. +//! only map to a single username, known as the _primary_. This primary username will be the result +//! of a lookup in the [UsernameOf] map for any given account. //! //! ## Interface //! @@ -65,7 +76,7 @@ //! * `accept_username` - Accept a username issued by a username authority. //! * `remove_expired_approval` - Remove a username that was issued but never accepted. //! * `set_primary_username` - Set a given username as an account's primary. -//! * `remove_dangling_username` - Remove a username that maps to an account without an identity. +//! * `remove_username` - Remove a username after its grace period has ended. //! //! #### For General Users with Sub-Identities //! * `set_subs` - Set the sub-accounts of an identity. @@ -81,12 +92,14 @@ //! //! #### For Username Authorities //! * `set_username_for` - Set a username for a given account. The account must approve it. +//! * `unbind_username` - Start the grace period for a username. //! //! #### For Superusers //! * `add_registrar` - Add a new registrar to the system. //! * `kill_identity` - Forcibly remove the associated identity; the deposit is lost. //! * `add_username_authority` - Add an account with the ability to issue usernames. //! * `remove_username_authority` - Remove an account with the ability to issue usernames. +//! * `kill_username` - Forcibly remove a username. //! //! [`Call`]: ./enum.Call.html //! [`Config`]: ./trait.Config.html @@ -103,13 +116,15 @@ pub mod weights; extern crate alloc; -use crate::types::{AuthorityPropertiesOf, Suffix, Username}; +use crate::types::{AuthorityProperties, Provider, Suffix, Username, UsernameInformation}; use alloc::{boxed::Box, vec::Vec}; use codec::Encode; use frame_support::{ ensure, pallet_prelude::{DispatchError, DispatchResult}, - traits::{BalanceStatus, Currency, Get, OnUnbalanced, ReservableCurrency, StorageVersion}, + traits::{ + BalanceStatus, Currency, Defensive, Get, OnUnbalanced, ReservableCurrency, StorageVersion, + }, BoundedVec, }; use frame_system::pallet_prelude::*; @@ -128,6 +143,7 @@ type NegativeImbalanceOf = <::Currency as Currency< ::AccountId, >>::NegativeImbalance; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +type ProviderOf = Provider>; #[frame_support::pallet] pub mod pallet { @@ -150,6 +166,11 @@ pub mod pallet { #[pallet::constant] type ByteDeposit: Get>; + /// The amount held on deposit per registered username. This value should change only in + /// runtime upgrades with proper migration of existing deposits. + #[pallet::constant] + type UsernameDeposit: Get>; + /// The amount held on deposit for a registered subaccount. This should account for the fact /// that one storage item's value will increase by the size of an account ID, and there will /// be another trie item whose value is the size of an account ID plus 32 bytes. @@ -192,6 +213,11 @@ pub mod pallet { #[pallet::constant] type PendingUsernameExpiration: Get>; + /// The number of blocks that must pass to enable the permanent deletion of a username by + /// its respective authority. + #[pallet::constant] + type UsernameGracePeriod: Get>; + /// The maximum length of a suffix. #[pallet::constant] type MaxSuffixLength: Get; @@ -204,7 +230,7 @@ pub mod pallet { type WeightInfo: WeightInfo; } - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -219,10 +245,15 @@ pub mod pallet { _, Twox64Concat, T::AccountId, - (Registration, T::MaxRegistrars, T::IdentityInformation>, Option>), + Registration, T::MaxRegistrars, T::IdentityInformation>, OptionQuery, >; + /// Identifies the primary username of an account. + #[pallet::storage] + pub type UsernameOf = + StorageMap<_, Twox64Concat, T::AccountId, Username, OptionQuery>; + /// The super-identity of an alternative "sub" identity together with its name, within that /// context. If the account is not some other account's sub-identity, then just `None`. #[pallet::storage] @@ -265,22 +296,28 @@ pub mod pallet { /// A map of the accounts who are authorized to grant usernames. #[pallet::storage] - pub type UsernameAuthorities = - StorageMap<_, Twox64Concat, T::AccountId, AuthorityPropertiesOf, OptionQuery>; + pub type AuthorityOf = + StorageMap<_, Twox64Concat, Suffix, AuthorityProperties, OptionQuery>; - /// Reverse lookup from `username` to the `AccountId` that has registered it. The value should - /// be a key in the `IdentityOf` map, but it may not if the user has cleared their identity. + /// Reverse lookup from `username` to the `AccountId` that has registered it and the provider of + /// the username. The `owner` value should be a key in the `UsernameOf` map, but it may not if + /// the user has cleared their username or it has been removed. /// - /// Multiple usernames may map to the same `AccountId`, but `IdentityOf` will only map to one + /// Multiple usernames may map to the same `AccountId`, but `UsernameOf` will only map to one /// primary username. #[pallet::storage] - pub type AccountOfUsername = - StorageMap<_, Blake2_128Concat, Username, T::AccountId, OptionQuery>; + pub type UsernameInfoOf = StorageMap< + _, + Blake2_128Concat, + Username, + UsernameInformation>, + OptionQuery, + >; /// Usernames that an authority has granted, but that the account controller has not confirmed /// that they want it. Used primarily in cases where the `AccountId` cannot provide a signature /// because they are a pure proxy, multisig, etc. In order to confirm it, they should call - /// [`Call::accept_username`]. + /// [accept_username](`Call::accept_username`). /// /// First tuple item is the account and second is the acceptance deadline. #[pallet::storage] @@ -288,10 +325,18 @@ pub mod pallet { _, Blake2_128Concat, Username, - (T::AccountId, BlockNumberFor), + (T::AccountId, BlockNumberFor, ProviderOf), OptionQuery, >; + /// Usernames for which the authority that granted them has started the removal process by + /// unbinding them. Each unbinding username maps to its grace period expiry, which is the first + /// block in which the username could be deleted through a + /// [remove_username](`Call::remove_username`) call. + #[pallet::storage] + pub type UnbindingUsernames = + StorageMap<_, Blake2_128Concat, Username, BlockNumberFor, OptionQuery>; + #[pallet::error] pub enum Error { /// Too many subs-accounts. @@ -346,6 +391,15 @@ pub mod pallet { NoUsername, /// The username cannot be forcefully removed because it can still be accepted. NotExpired, + /// The username cannot be removed because it's still in the grace period. + TooEarly, + /// The username cannot be removed because it is not unbinding. + NotUnbinding, + /// The username cannot be unbound because it is already unbinding. + AlreadyUnbinding, + /// The action cannot be performed because of insufficient privileges (e.g. authority + /// trying to unbind a username provided by the system). + InsufficientPrivileges, } #[pallet::event] @@ -387,6 +441,12 @@ pub mod pallet { /// A dangling username (as in, a username corresponding to an account that has removed its /// identity) has been removed. DanglingUsernameRemoved { who: T::AccountId, username: Username }, + /// A username has been unbound. + UsernameUnbound { username: Username }, + /// A username has been removed. + UsernameRemoved { username: Username }, + /// A username has been killed. + UsernameKilled { username: Username }, } #[pallet::call] @@ -444,24 +504,18 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; - let (mut id, username) = match IdentityOf::::get(&sender) { - Some((mut id, maybe_username)) => ( - { - // Only keep non-positive judgements. - id.judgements.retain(|j| j.1.is_sticky()); - id.info = *info; - id - }, - maybe_username, - ), - None => ( - Registration { - info: *info, - judgements: BoundedVec::default(), - deposit: Zero::zero(), - }, - None, - ), + let mut id = match IdentityOf::::get(&sender) { + Some(mut id) => { + // Only keep non-positive judgements. + id.judgements.retain(|j| j.1.is_sticky()); + id.info = *info; + id + }, + None => Registration { + info: *info, + judgements: BoundedVec::default(), + deposit: Zero::zero(), + }, }; let new_deposit = Self::calculate_identity_deposit(&id.info); @@ -470,7 +524,7 @@ pub mod pallet { id.deposit = new_deposit; let judgements = id.judgements.len(); - IdentityOf::::insert(&sender, (id, username)); + IdentityOf::::insert(&sender, id); Self::deposit_event(Event::IdentitySet { who: sender }); Ok(Some(T::WeightInfo::set_identity(judgements as u32)).into()) @@ -562,15 +616,11 @@ pub mod pallet { let sender = ensure_signed(origin)?; let (subs_deposit, sub_ids) = SubsOf::::take(&sender); - let (id, maybe_username) = - IdentityOf::::take(&sender).ok_or(Error::::NoIdentity)?; + let id = IdentityOf::::take(&sender).ok_or(Error::::NoIdentity)?; let deposit = id.total_deposit().saturating_add(subs_deposit); for sub in sub_ids.iter() { SuperOf::::remove(sub); } - if let Some(username) = maybe_username { - AccountOfUsername::::remove(username); - } let err_amount = T::Currency::unreserve(&sender, deposit); debug_assert!(err_amount.is_zero()); @@ -615,7 +665,7 @@ pub mod pallet { .and_then(Option::as_ref) .ok_or(Error::::EmptyIndex)?; ensure!(max_fee >= registrar.fee, Error::::FeeChanged); - let (mut id, username) = IdentityOf::::get(&sender).ok_or(Error::::NoIdentity)?; + let mut id = IdentityOf::::get(&sender).ok_or(Error::::NoIdentity)?; let item = (reg_index, Judgement::FeePaid(registrar.fee)); match id.judgements.binary_search_by_key(®_index, |x| x.0) { @@ -632,7 +682,7 @@ pub mod pallet { T::Currency::reserve(&sender, registrar.fee)?; let judgements = id.judgements.len(); - IdentityOf::::insert(&sender, (id, username)); + IdentityOf::::insert(&sender, id); Self::deposit_event(Event::JudgementRequested { who: sender, @@ -659,7 +709,7 @@ pub mod pallet { reg_index: RegistrarIndex, ) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; - let (mut id, username) = IdentityOf::::get(&sender).ok_or(Error::::NoIdentity)?; + let mut id = IdentityOf::::get(&sender).ok_or(Error::::NoIdentity)?; let pos = id .judgements @@ -674,7 +724,7 @@ pub mod pallet { let err_amount = T::Currency::unreserve(&sender, fee); debug_assert!(err_amount.is_zero()); let judgements = id.judgements.len(); - IdentityOf::::insert(&sender, (id, username)); + IdentityOf::::insert(&sender, id); Self::deposit_event(Event::JudgementUnrequested { who: sender, @@ -813,8 +863,7 @@ pub mod pallet { .and_then(Option::as_ref) .filter(|r| r.account == sender) .ok_or(Error::::InvalidIndex)?; - let (mut id, username) = - IdentityOf::::get(&target).ok_or(Error::::InvalidTarget)?; + let mut id = IdentityOf::::get(&target).ok_or(Error::::InvalidTarget)?; if T::Hashing::hash_of(&id.info) != identity { return Err(Error::::JudgementForDifferentIdentity.into()) @@ -841,7 +890,7 @@ pub mod pallet { } let judgements = id.judgements.len(); - IdentityOf::::insert(&target, (id, username)); + IdentityOf::::insert(&target, id); Self::deposit_event(Event::JudgementGiven { target, registrar_index: reg_index }); Ok(Some(T::WeightInfo::provide_judgement(judgements as u32)).into()) @@ -874,15 +923,11 @@ pub mod pallet { let target = T::Lookup::lookup(target)?; // Grab their deposit (and check that they have one). let (subs_deposit, sub_ids) = SubsOf::::take(&target); - let (id, maybe_username) = - IdentityOf::::take(&target).ok_or(Error::::NoIdentity)?; + let id = IdentityOf::::take(&target).ok_or(Error::::NoIdentity)?; let deposit = id.total_deposit().saturating_add(subs_deposit); for sub in sub_ids.iter() { SuperOf::::remove(sub); } - if let Some(username) = maybe_username { - AccountOfUsername::::remove(username); - } // Slash their deposit from them. T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0); @@ -1010,8 +1055,9 @@ pub mod pallet { /// Add an `AccountId` with permission to grant usernames with a given `suffix` appended. /// - /// The authority can grant up to `allocation` usernames. To top up their allocation, they - /// should just issue (or request via governance) a new `add_username_authority` call. + /// The authority can grant up to `allocation` usernames. To top up the allocation or + /// change the account used to grant usernames, this call can be used with the updated + /// parameters to overwrite the existing configuration. #[pallet::call_index(15)] #[pallet::weight(T::WeightInfo::add_username_authority())] pub fn add_username_authority( @@ -1024,13 +1070,12 @@ pub mod pallet { let authority = T::Lookup::lookup(authority)?; // We don't need to check the length because it gets checked when casting into a // `BoundedVec`. - Self::validate_username(&suffix, None).map_err(|_| Error::::InvalidSuffix)?; + Self::validate_suffix(&suffix)?; let suffix = Suffix::::try_from(suffix).map_err(|_| Error::::InvalidSuffix)?; - // The authority may already exist, but we don't need to check. They might be changing - // their suffix or adding allocation, so we just want to overwrite whatever was there. - UsernameAuthorities::::insert( - &authority, - AuthorityPropertiesOf:: { suffix, allocation }, + // The call is `UsernameAuthorityOrigin` guarded, overwrite the old entry if it exists. + AuthorityOf::::insert( + &suffix, + AuthorityProperties:: { account_id: authority.clone(), allocation }, ); Self::deposit_event(Event::AuthorityAdded { authority }); Ok(()) @@ -1041,18 +1086,26 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_username_authority())] pub fn remove_username_authority( origin: OriginFor, + suffix: Vec, authority: AccountIdLookupOf, ) -> DispatchResult { T::UsernameAuthorityOrigin::ensure_origin(origin)?; + let suffix = Suffix::::try_from(suffix).map_err(|_| Error::::InvalidSuffix)?; let authority = T::Lookup::lookup(authority)?; - UsernameAuthorities::::take(&authority).ok_or(Error::::NotUsernameAuthority)?; + let properties = + AuthorityOf::::take(&suffix).ok_or(Error::::NotUsernameAuthority)?; + ensure!(properties.account_id == authority, Error::::InvalidSuffix); Self::deposit_event(Event::AuthorityRemoved { authority }); Ok(()) } /// Set the username for `who`. Must be called by a username authority. /// - /// The authority must have an `allocation`. Users can either pre-sign their usernames or + /// If `use_allocation` is set, the authority must have a username allocation available to + /// spend. Otherwise, the authority will need to put up a deposit for registering the + /// username. + /// + /// Users can either pre-sign their usernames or /// accept them later. /// /// Usernames must: @@ -1060,45 +1113,42 @@ pub mod pallet { /// - When combined with the suffix of the issuing authority be _less than_ the /// `MaxUsernameLength`. #[pallet::call_index(17)] - #[pallet::weight(T::WeightInfo::set_username_for())] + #[pallet::weight(T::WeightInfo::set_username_for(if *use_allocation { 1 } else { 0 }))] pub fn set_username_for( origin: OriginFor, who: AccountIdLookupOf, username: Vec, signature: Option, + use_allocation: bool, ) -> DispatchResult { // Ensure origin is a Username Authority and has an allocation. Decrement their // allocation by one. let sender = ensure_signed(origin)?; - let suffix = UsernameAuthorities::::try_mutate( - &sender, - |maybe_authority| -> Result, DispatchError> { + let suffix = Self::validate_username(&username)?; + let provider = AuthorityOf::::try_mutate( + &suffix, + |maybe_authority| -> Result, DispatchError> { let properties = maybe_authority.as_mut().ok_or(Error::::NotUsernameAuthority)?; - ensure!(properties.allocation > 0, Error::::NoAllocation); - properties.allocation.saturating_dec(); - Ok(properties.suffix.clone()) + ensure!(properties.account_id == sender, Error::::NotUsernameAuthority); + if use_allocation { + ensure!(properties.allocation > 0, Error::::NoAllocation); + properties.allocation.saturating_dec(); + Ok(Provider::new_with_allocation()) + } else { + let deposit = T::UsernameDeposit::get(); + T::Currency::reserve(&sender, deposit)?; + Ok(Provider::new_with_deposit(deposit)) + } }, )?; - // Ensure that the username only contains allowed characters. We already know the suffix - // does. - let username_length = username.len().saturating_add(suffix.len()) as u32; - Self::validate_username(&username, Some(username_length))?; - - // Concatenate the username with suffix and cast into a BoundedVec. Should be infallible - // since we already ensured it is below the max length. - let mut full_username = - Vec::with_capacity(username.len().saturating_add(suffix.len()).saturating_add(1)); - full_username.extend(username); - full_username.extend(b"."); - full_username.extend(suffix); let bounded_username = - Username::::try_from(full_username).map_err(|_| Error::::InvalidUsername)?; + Username::::try_from(username).map_err(|_| Error::::InvalidUsername)?; // Usernames must be unique. Ensure it's not taken. ensure!( - !AccountOfUsername::::contains_key(&bounded_username), + !UsernameInfoOf::::contains_key(&bounded_username), Error::::UsernameTaken ); ensure!( @@ -1112,10 +1162,10 @@ pub mod pallet { // Account has pre-signed an authorization. Verify the signature provided and grant // the username directly. Self::validate_signature(&bounded_username[..], &s, &who)?; - Self::insert_username(&who, bounded_username); + Self::insert_username(&who, bounded_username, provider); } else { // The user must accept the username, therefore, queue it. - Self::queue_acceptance(&who, bounded_username); + Self::queue_acceptance(&who, bounded_username, provider); } Ok(()) } @@ -1129,10 +1179,10 @@ pub mod pallet { username: Username, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let (approved_for, _) = + let (approved_for, _, provider) = PendingUsernames::::take(&username).ok_or(Error::::NoUsername)?; ensure!(approved_for == who.clone(), Error::::InvalidUsername); - Self::insert_username(&who, username.clone()); + Self::insert_username(&who, username.clone(), provider); Self::deposit_event(Event::UsernameSet { who: who.clone(), username }); Ok(Pays::No.into()) } @@ -1141,17 +1191,37 @@ pub mod pallet { /// accepted by the user and must now be beyond its expiration. The call must include the /// full username, as in `username.suffix`. #[pallet::call_index(19)] - #[pallet::weight(T::WeightInfo::remove_expired_approval())] + #[pallet::weight(T::WeightInfo::remove_expired_approval(0))] pub fn remove_expired_approval( origin: OriginFor, username: Username, ) -> DispatchResultWithPostInfo { let _ = ensure_signed(origin)?; - if let Some((who, expiration)) = PendingUsernames::::take(&username) { + if let Some((who, expiration, provider)) = PendingUsernames::::take(&username) { let now = frame_system::Pallet::::block_number(); ensure!(now > expiration, Error::::NotExpired); + let actual_weight = match provider { + Provider::AuthorityDeposit(deposit) => { + let suffix = Self::suffix_of_username(&username) + .ok_or(Error::::InvalidUsername)?; + let authority_account = AuthorityOf::::get(&suffix) + .map(|auth_info| auth_info.account_id) + .ok_or(Error::::NotUsernameAuthority)?; + let err_amount = T::Currency::unreserve(&authority_account, deposit); + debug_assert!(err_amount.is_zero()); + T::WeightInfo::remove_expired_approval(0) + }, + Provider::Allocation => { + // We don't refund the allocation, it is lost, but we refund some weight. + T::WeightInfo::remove_expired_approval(1) + }, + Provider::System => { + // Usernames added by the system shouldn't ever be expired. + return Err(Error::::InvalidTarget.into()); + }, + }; Self::deposit_event(Event::PreapprovalExpired { whose: who.clone() }); - Ok(Pays::No.into()) + Ok((Some(actual_weight), Pays::No).into()) } else { Err(Error::::NoUsername.into()) } @@ -1164,107 +1234,139 @@ pub mod pallet { // ensure `username` maps to `origin` (i.e. has already been set by an authority). let who = ensure_signed(origin)?; let account_of_username = - AccountOfUsername::::get(&username).ok_or(Error::::NoUsername)?; + UsernameInfoOf::::get(&username).ok_or(Error::::NoUsername)?.owner; ensure!(who == account_of_username, Error::::InvalidUsername); - let (registration, _maybe_username) = - IdentityOf::::get(&who).ok_or(Error::::NoIdentity)?; - IdentityOf::::insert(&who, (registration, Some(username.clone()))); + UsernameOf::::insert(&who, username.clone()); Self::deposit_event(Event::PrimaryUsernameSet { who: who.clone(), username }); Ok(()) } - /// Remove a username that corresponds to an account with no identity. Exists when a user - /// gets a username but then calls `clear_identity`. + /// Start the process of removing a username by placing it in the unbinding usernames map. + /// Once the grace period has passed, the username can be deleted by calling + /// [remove_username](crate::Call::remove_username). #[pallet::call_index(21)] - #[pallet::weight(T::WeightInfo::remove_dangling_username())] - pub fn remove_dangling_username( + #[pallet::weight(T::WeightInfo::unbind_username())] + pub fn unbind_username(origin: OriginFor, username: Username) -> DispatchResult { + let who = ensure_signed(origin)?; + let username_info = + UsernameInfoOf::::get(&username).ok_or(Error::::NoUsername)?; + let suffix = Self::suffix_of_username(&username).ok_or(Error::::InvalidUsername)?; + let authority_account = AuthorityOf::::get(&suffix) + .map(|auth_info| auth_info.account_id) + .ok_or(Error::::NotUsernameAuthority)?; + ensure!(who == authority_account, Error::::NotUsernameAuthority); + match username_info.provider { + Provider::AuthorityDeposit(_) | Provider::Allocation => { + let now = frame_system::Pallet::::block_number(); + let grace_period_expiry = now.saturating_add(T::UsernameGracePeriod::get()); + UnbindingUsernames::::try_mutate(&username, |maybe_init| { + if maybe_init.is_some() { + return Err(Error::::AlreadyUnbinding); + } + *maybe_init = Some(grace_period_expiry); + Ok(()) + })?; + }, + Provider::System => return Err(Error::::InsufficientPrivileges.into()), + } + Self::deposit_event(Event::UsernameUnbound { username }); + Ok(()) + } + + /// Permanently delete a username which has been unbinding for longer than the grace period. + /// Caller is refunded the fee if the username expired and the removal was successful. + #[pallet::call_index(22)] + #[pallet::weight(T::WeightInfo::remove_username())] + pub fn remove_username( origin: OriginFor, username: Username, ) -> DispatchResultWithPostInfo { - // ensure `username` maps to `origin` (i.e. has already been set by an authority). let _ = ensure_signed(origin)?; - let who = AccountOfUsername::::take(&username).ok_or(Error::::NoUsername)?; - ensure!(!IdentityOf::::contains_key(&who), Error::::InvalidUsername); - Self::deposit_event(Event::DanglingUsernameRemoved { who: who.clone(), username }); + let grace_period_expiry = + UnbindingUsernames::::take(&username).ok_or(Error::::NotUnbinding)?; + let now = frame_system::Pallet::::block_number(); + ensure!(now >= grace_period_expiry, Error::::TooEarly); + let username_info = UsernameInfoOf::::take(&username) + .defensive_proof("an unbinding username must exist") + .ok_or(Error::::NoUsername)?; + // If this is the primary username, remove the entry from the account -> username map. + UsernameOf::::mutate(&username_info.owner, |maybe_primary| { + if maybe_primary.as_ref().map_or(false, |primary| *primary == username) { + *maybe_primary = None; + } + }); + match username_info.provider { + Provider::AuthorityDeposit(username_deposit) => { + let suffix = Self::suffix_of_username(&username) + .defensive_proof("registered username must be valid") + .ok_or(Error::::InvalidUsername)?; + if let Some(authority_account) = + AuthorityOf::::get(&suffix).map(|auth_info| auth_info.account_id) + { + let err_amount = + T::Currency::unreserve(&authority_account, username_deposit); + debug_assert!(err_amount.is_zero()); + } + }, + Provider::Allocation => { + // We don't refund the allocation, it is lost. + }, + Provider::System => return Err(Error::::InsufficientPrivileges.into()), + } + Self::deposit_event(Event::UsernameRemoved { username }); Ok(Pays::No.into()) } + + /// Call with [ForceOrigin](crate::Config::ForceOrigin) privileges which deletes a username + /// and slashes any deposit associated with it. + #[pallet::call_index(23)] + #[pallet::weight(T::WeightInfo::kill_username(0))] + pub fn kill_username( + origin: OriginFor, + username: Username, + ) -> DispatchResultWithPostInfo { + T::ForceOrigin::ensure_origin(origin)?; + let username_info = + UsernameInfoOf::::take(&username).ok_or(Error::::NoUsername)?; + // If this is the primary username, remove the entry from the account -> username map. + UsernameOf::::mutate(&username_info.owner, |maybe_primary| { + if match maybe_primary { + Some(primary) if *primary == username => true, + _ => false, + } { + *maybe_primary = None; + } + }); + let _ = UnbindingUsernames::::take(&username); + let actual_weight = match username_info.provider { + Provider::AuthorityDeposit(username_deposit) => { + let suffix = + Self::suffix_of_username(&username).ok_or(Error::::InvalidUsername)?; + if let Some(authority_account) = + AuthorityOf::::get(&suffix).map(|auth_info| auth_info.account_id) + { + T::Slashed::on_unbalanced( + T::Currency::slash_reserved(&authority_account, username_deposit).0, + ); + } + T::WeightInfo::kill_username(0) + }, + Provider::Allocation => { + // We don't refund the allocation, it is lost, but we do refund some weight. + T::WeightInfo::kill_username(1) + }, + Provider::System => { + // Force origin can remove system usernames. + T::WeightInfo::kill_username(1) + }, + }; + Self::deposit_event(Event::UsernameKilled { username }); + Ok((Some(actual_weight), Pays::No).into()) + } } } impl Pallet { - /// Information that is pertinent to identify the entity behind an account. First item is the - /// registration, second is the account's primary username. - /// - /// TWOX-NOTE: OK ― `AccountId` is a secure hash. - pub fn identity( - who: T::AccountId, - ) -> Option<( - Registration, T::MaxRegistrars, T::IdentityInformation>, - Option>, - )> { - IdentityOf::::get(who) - } - - /// The super-identity of an alternative "sub" identity together with its name, within that - /// context. If the account is not some other account's sub-identity, then just `None`. - pub fn super_of(who: T::AccountId) -> Option<(T::AccountId, Data)> { - SuperOf::::get(who) - } - - /// Alternative "sub" identities of this account. - /// - /// The first item is the deposit, the second is a vector of the accounts. - /// - /// TWOX-NOTE: OK ― `AccountId` is a secure hash. - pub fn subs_of( - who: T::AccountId, - ) -> (BalanceOf, BoundedVec) { - SubsOf::::get(who) - } - - /// The set of registrars. Not expected to get very big as can only be added through a - /// special origin (likely a council motion). - /// - /// The index into this can be cast to `RegistrarIndex` to get a valid value. - pub fn registrars() -> BoundedVec< - Option< - RegistrarInfo< - BalanceOf, - T::AccountId, - ::FieldsIdentifier, - >, - >, - T::MaxRegistrars, - > { - Registrars::::get() - } - - /// A map of the accounts who are authorized to grant usernames. - pub fn authority(who: T::AccountId) -> Option> { - UsernameAuthorities::::get(who) - } - - /// Reverse lookup from `username` to the `AccountId` that has registered it. The value should - /// be a key in the `IdentityOf` map, but it may not if the user has cleared their identity. - /// - /// Multiple usernames may map to the same `AccountId`, but `IdentityOf` will only map to one - /// primary username. - pub fn username(username: Username) -> Option { - AccountOfUsername::::get(username) - } - - /// Usernames that an authority has granted, but that the account controller has not confirmed - /// that they want it. Used primarily in cases where the `AccountId` cannot provide a signature - /// because they are a pure proxy, multisig, etc. In order to confirm it, they should call - /// [`Call::accept_username`]. - /// - /// First tuple item is the account and second is the acceptance deadline. - pub fn preapproved_usernames( - username: Username, - ) -> Option<(T::AccountId, BlockNumberFor)> { - PendingUsernames::::get(username) - } - /// Get the subs of an account. pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> { SubsOf::::get(who) @@ -1300,7 +1402,7 @@ impl Pallet { fields: ::FieldsIdentifier, ) -> bool { IdentityOf::::get(who) - .map_or(false, |(registration, _username)| (registration.info.has_identity(fields))) + .map_or(false, |registration| (registration.info.has_identity(fields))) } /// Calculate the deposit required for an identity. @@ -1312,23 +1414,56 @@ impl Pallet { /// Validate that a username conforms to allowed characters/format. /// - /// The function will validate the characters in `username` and that `length` (if `Some`) - /// conforms to the limit. It is not expected to pass a fully formatted username here (i.e. one - /// with any protocol-added characters included, such as a `.`). The suffix is also separately - /// validated by this function to ensure the full username conforms. - fn validate_username(username: &Vec, length: Option) -> DispatchResult { - // Verify input length before allocating a Vec with the user's input. `<` instead of `<=` - // because it needs one element for the point (`username` + `.` + `suffix`). - if let Some(l) = length { - ensure!(l < T::MaxUsernameLength::get(), Error::::InvalidUsername); - } + /// The function will validate the characters in `username`. It is expected to pass a fully + /// formatted username here (i.e. "username.suffix"). The suffix is also separately validated + /// and returned by this function. + fn validate_username(username: &Vec) -> Result, DispatchError> { + // Verify input length before allocating a Vec with the user's input. + ensure!( + username.len() <= T::MaxUsernameLength::get() as usize, + Error::::InvalidUsername + ); + // Usernames cannot be empty. ensure!(!username.is_empty(), Error::::InvalidUsername); + let separator_idx = + username.iter().rposition(|c| *c == b'.').ok_or(Error::::InvalidUsername)?; + ensure!(separator_idx > 0, Error::::InvalidUsername); + let suffix_start = separator_idx.checked_add(1).ok_or(Error::::InvalidUsername)?; + ensure!(suffix_start < username.len(), Error::::InvalidUsername); // Username must be lowercase and alphanumeric. ensure!( - username.iter().all(|byte| byte.is_ascii_digit() || byte.is_ascii_lowercase()), + username + .iter() + .take(separator_idx) + .all(|byte| byte.is_ascii_digit() || byte.is_ascii_lowercase()), Error::::InvalidUsername ); + let suffix: Suffix = (&username[suffix_start..]) + .to_vec() + .try_into() + .map_err(|_| Error::::InvalidUsername)?; + Ok(suffix) + } + + /// Return the suffix of a username, if it is valid. + fn suffix_of_username(username: &Username) -> Option> { + let separator_idx = username.iter().rposition(|c| *c == b'.')?; + let suffix_start = separator_idx.checked_add(1)?; + if suffix_start >= username.len() { + return None; + } + (&username[suffix_start..]).to_vec().try_into().ok() + } + + /// Validate that a suffix conforms to allowed characters/format. + fn validate_suffix(suffix: &Vec) -> Result<(), DispatchError> { + ensure!(suffix.len() <= T::MaxSuffixLength::get() as usize, Error::::InvalidSuffix); + ensure!(!suffix.is_empty(), Error::::InvalidSuffix); + ensure!( + suffix.iter().all(|byte| byte.is_ascii_digit() || byte.is_ascii_lowercase()), + Error::::InvalidSuffix + ); Ok(()) } @@ -1357,34 +1492,22 @@ impl Pallet { } /// A username has met all conditions. Insert the relevant storage items. - pub fn insert_username(who: &T::AccountId, username: Username) { + pub fn insert_username(who: &T::AccountId, username: Username, provider: ProviderOf) { // Check if they already have a primary. If so, leave it. If not, set it. // Likewise, check if they have an identity. If not, give them a minimal one. - let (reg, primary_username, new_is_primary) = match IdentityOf::::get(&who) { + let (primary_username, new_is_primary) = match UsernameOf::::get(&who) { // User has an existing Identity and a primary username. Leave it. - Some((reg, Some(primary))) => (reg, primary, false), + Some(primary) => (primary, false), // User has an Identity but no primary. Set the new one as primary. - Some((reg, None)) => (reg, username.clone(), true), - // User does not have an existing Identity. Give them a fresh default one and set - // their username as primary. - None => ( - Registration { - info: Default::default(), - judgements: Default::default(), - deposit: Zero::zero(), - }, - username.clone(), - true, - ), + None => (username.clone(), true), }; - // Enter in identity map. Note: In the case that the user did not have a pre-existing - // Identity, we have given them the storage item for free. If they ever call - // `set_identity` with identity info, then they will need to place the normal identity - // deposit. - IdentityOf::::insert(&who, (reg, Some(primary_username))); + if new_is_primary { + UsernameOf::::insert(&who, primary_username); + } + let username_info = UsernameInformation { owner: who.clone(), provider }; // Enter in username map. - AccountOfUsername::::insert(username.clone(), &who); + UsernameInfoOf::::insert(username.clone(), username_info); Self::deposit_event(Event::UsernameSet { who: who.clone(), username: username.clone() }); if new_is_primary { Self::deposit_event(Event::PrimaryUsernameSet { who: who.clone(), username }); @@ -1393,10 +1516,10 @@ impl Pallet { /// A username was granted by an authority, but must be accepted by `who`. Put the username /// into a queue for acceptance. - pub fn queue_acceptance(who: &T::AccountId, username: Username) { + pub fn queue_acceptance(who: &T::AccountId, username: Username, provider: ProviderOf) { let now = frame_system::Pallet::::block_number(); let expiration = now.saturating_add(T::PendingUsernameExpiration::get()); - PendingUsernames::::insert(&username, (who.clone(), expiration)); + PendingUsernames::::insert(&username, (who.clone(), expiration, provider)); Self::deposit_event(Event::UsernameQueued { who: who.clone(), username, expiration }); } @@ -1415,7 +1538,7 @@ impl Pallet { pub fn reap_identity(who: &T::AccountId) -> Result<(u32, u32, u32), DispatchError> { // `take` any storage items keyed by `target` // identity - let (id, _maybe_username) = IdentityOf::::take(&who).ok_or(Error::::NoIdentity)?; + let id = IdentityOf::::take(&who).ok_or(Error::::NoIdentity)?; let registrars = id.judgements.len() as u32; let encoded_byte_size = id.info.encoded_size() as u32; @@ -1449,7 +1572,7 @@ impl Pallet { let new_id_deposit = IdentityOf::::try_mutate( &target, |identity_of| -> Result, DispatchError> { - let (reg, _) = identity_of.as_mut().ok_or(Error::::NoIdentity)?; + let reg = identity_of.as_mut().ok_or(Error::::NoIdentity)?; // Calculate what deposit should be let encoded_byte_size = reg.info.encoded_size() as u32; let byte_deposit = @@ -1491,14 +1614,11 @@ impl Pallet { ) -> DispatchResult { IdentityOf::::insert( &who, - ( - Registration { - judgements: Default::default(), - deposit: Zero::zero(), - info: info.clone(), - }, - None::>, - ), + Registration { + judgements: Default::default(), + deposit: Zero::zero(), + info: info.clone(), + }, ); Ok(()) } diff --git a/substrate/frame/identity/src/migration.rs b/substrate/frame/identity/src/migration.rs index 8725bfd39df1..3a78692cfcd7 100644 --- a/substrate/frame/identity/src/migration.rs +++ b/substrate/frame/identity/src/migration.rs @@ -15,16 +15,23 @@ //! Storage migrations for the Identity pallet. +extern crate alloc; + use super::*; use frame_support::{ - migrations::VersionedMigration, pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade, + migrations::VersionedMigration, pallet_prelude::*, storage_alias, + traits::UncheckedOnRuntimeUpgrade, IterableStorageMap, }; +#[cfg(feature = "try-runtime")] +use alloc::collections::BTreeMap; #[cfg(feature = "try-runtime")] use codec::{Decode, Encode}; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; +pub const PALLET_MIGRATIONS_ID: &[u8; 15] = b"pallet-identity"; + pub mod versioned { use super::*; @@ -37,31 +44,78 @@ pub mod versioned { >; } -pub mod v1 { +/// The old identity types in v0. +mod types_v0 { use super::*; - /// The log target. - const TARGET: &'static str = "runtime::identity::migration::v1"; + #[storage_alias] + pub type IdentityOf = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + >, + OptionQuery, + >; +} - /// The old identity type, useful in pre-upgrade. - mod v0 { - use super::*; - use frame_support::storage_alias; +/// The old identity types in v1. +mod types_v1 { + use super::*; - #[storage_alias] - pub type IdentityOf = StorageMap< - Pallet, - Twox64Concat, - ::AccountId, + #[storage_alias] + pub type IdentityOf = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + ( Registration< BalanceOf, ::MaxRegistrars, ::IdentityInformation, >, - OptionQuery, - >; - } + Option>, + ), + OptionQuery, + >; + + #[storage_alias] + pub type UsernameAuthorities = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + AuthorityProperties>, + OptionQuery, + >; + + #[storage_alias] + pub type AccountOfUsername = StorageMap< + Pallet, + Blake2_128Concat, + Username, + ::AccountId, + OptionQuery, + >; + + #[cfg(feature = "try-runtime")] + #[storage_alias] + pub type PendingUsernames = StorageMap< + Pallet, + Blake2_128Concat, + Username, + (::AccountId, BlockNumberFor), + OptionQuery, + >; +} +pub mod v1 { + use super::*; + + /// The log target. + const TARGET: &'static str = "runtime::identity::migration::v1"; /// Migration to add usernames to Identity info. /// /// `T` is the runtime and `KL` is the key limit to migrate. This is just a safety guard to @@ -71,7 +125,7 @@ pub mod v1 { impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV0ToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - let identities = v0::IdentityOf::::iter().count(); + let identities = types_v0::IdentityOf::::iter().count(); log::info!( target: TARGET, "pre-upgrade state contains '{}' identities.", @@ -91,8 +145,8 @@ pub mod v1 { let mut translated: u64 = 0; let mut interrupted = false; - for (account, registration) in v0::IdentityOf::::iter() { - IdentityOf::::insert(account, (registration, None::>)); + for (account, registration) in types_v0::IdentityOf::::iter() { + types_v1::IdentityOf::::insert(account, (registration, None::>)); translated.saturating_inc(); if translated >= KL { log::warn!( @@ -116,7 +170,7 @@ pub mod v1 { fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { let identities_to_migrate: u64 = Decode::decode(&mut &state[..]) .expect("failed to decode the state from pre-upgrade."); - let identities = IdentityOf::::iter().count() as u64; + let identities = types_v1::IdentityOf::::iter().count() as u64; log::info!("post-upgrade expects '{}' identities to have been migrated.", identities); ensure!(identities_to_migrate == identities, "must migrate all identities."); log::info!(target: TARGET, "migrated all identities."); @@ -124,3 +178,673 @@ pub mod v1 { } } } + +pub mod v2 { + use super::*; + use frame_support::{ + migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, + weights::WeightMeter, + }; + + type HashedKey = BoundedVec>; + // The resulting state of the step and the actual weight consumed. + type StepResultOf = + MigrationState<::AccountId, Username, Suffix>; + + #[cfg(feature = "runtime-benchmarks")] + pub(crate) type BenchmarkingSetupOf = + BenchmarkingSetup, ::AccountId, Username>; + + /// Progressive states of a migration. The migration starts with the first variant and ends with + /// the last. + #[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)] + pub enum MigrationState { + Authority(A), + FinishedAuthorities, + Identity(HashedKey), + FinishedIdentities, + Username(U), + FinishedUsernames, + PendingUsername(HashedKey), + FinishedPendingUsernames, + CleanupAuthorities(S), + FinishedCleanupAuthorities, + CleanupUsernames(U), + Finished, + } + + #[cfg(feature = "try-runtime")] + #[derive(Encode, Decode)] + struct TryRuntimeState { + authorities: BTreeMap, (T::AccountId, u32)>, + identities: BTreeMap< + T::AccountId, + Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + >, + >, + primary_usernames: BTreeMap>, + usernames: BTreeMap, T::AccountId>, + pending_usernames: BTreeMap, (T::AccountId, BlockNumberFor)>, + } + + pub struct LazyMigrationV1ToV2(PhantomData); + impl SteppedMigration for LazyMigrationV1ToV2 { + type Cursor = MigrationState, Suffix>; + type Identifier = MigrationId<15>; + + fn id() -> Self::Identifier { + MigrationId { pallet_id: *PALLET_MIGRATIONS_ID, version_from: 1, version_to: 2 } + } + + fn step( + mut cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + if Pallet::::on_chain_storage_version() != Self::id().version_from as u16 { + return Ok(None); + } + + // Check that we have enough weight for at least the next step. If we don't, then the + // migration cannot be complete. + let required = match &cursor { + Some(state) => Self::required_weight(&state), + // Worst case weight for `authority_step`. + None => T::WeightInfo::migration_v2_authority_step(), + }; + if meter.remaining().any_lt(required) { + return Err(SteppedMigrationError::InsufficientWeight { required }); + } + + loop { + // Check that we would have enough weight to perform this step in the worst case + // scenario. + let required_weight = match &cursor { + Some(state) => Self::required_weight(&state), + // Worst case weight for `authority_step`. + None => T::WeightInfo::migration_v2_authority_step(), + }; + if !meter.can_consume(required_weight) { + break; + } + + let next = match &cursor { + // At first, migrate any authorities. + None => Self::authority_step(None), + // Migrate any remaining authorities. + Some(MigrationState::Authority(maybe_last_authority)) => + Self::authority_step(Some(maybe_last_authority)), + // After the last authority was migrated, start migrating usernames from + // the former `AccountOfUsername` into `UsernameInfoOf`. + Some(MigrationState::FinishedAuthorities) => Self::username_step(None), + // Keep migrating usernames. + Some(MigrationState::Username(maybe_last_username)) => + Self::username_step(Some(maybe_last_username)), + // After the last username was migrated, start migrating all identities in + // `IdentityOf`, which currently hold the primary username of the owner account + // as well as any associated identity. Accounts which set a username but not an + // identity also have a zero deposit identity stored, which will be removed. + Some(MigrationState::FinishedUsernames) => Self::identity_step(None), + // Keep migrating identities. + Some(MigrationState::Identity(last_key)) => + Self::identity_step(Some(last_key.clone())), + // After the last identity was migrated, start migrating usernames pending + // approval from `PendingUsernames`. + Some(MigrationState::FinishedIdentities) => Self::pending_username_step(None), + // Keep migrating pending usernames. + Some(MigrationState::PendingUsername(last_key)) => + Self::pending_username_step(Some(last_key.clone())), + // After the last pending username was migrated, start clearing the storage + // previously associated with authorities in `UsernameAuthority`. + Some(MigrationState::FinishedPendingUsernames) => + Self::cleanup_authority_step(None), + // Keep clearing the obsolete authority storage. + Some(MigrationState::CleanupAuthorities(maybe_last_username)) => + Self::cleanup_authority_step(Some(maybe_last_username)), + // After the last obsolete authority was cleared from storage, start clearing + // the storage previously associated with usernames in `AccountOfUsername`. + Some(MigrationState::FinishedCleanupAuthorities) => + Self::cleanup_username_step(None), + // Keep clearing the obsolete username storage. + Some(MigrationState::CleanupUsernames(maybe_last_username)) => + Self::cleanup_username_step(Some(maybe_last_username)), + // After the last obsolete username was cleared from storage, the migration is + // done. + Some(MigrationState::Finished) => { + StorageVersion::new(Self::id().version_to as u16).put::>(); + return Ok(None) + }, + }; + + cursor = Some(next); + meter.consume(required_weight); + } + + Ok(cursor) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let authorities: BTreeMap, (T::AccountId, u32)> = + types_v1::UsernameAuthorities::::iter() + .map(|(account, authority_properties)| { + ( + authority_properties.account_id, + (account, authority_properties.allocation), + ) + }) + .collect(); + let mut primary_usernames: BTreeMap<_, _> = Default::default(); + let identities = types_v1::IdentityOf::::iter() + .map(|(account, (identity, maybe_username))| { + if let Some(username) = maybe_username { + primary_usernames.insert(account.clone(), username); + } + (account, identity) + }) + .collect::>(); + let usernames = types_v1::AccountOfUsername::::iter().collect::>(); + let pending_usernames: BTreeMap, (T::AccountId, BlockNumberFor)> = + types_v1::PendingUsernames::::iter().collect(); + let state: TryRuntimeState = TryRuntimeState { + authorities, + identities, + primary_usernames, + usernames, + pending_usernames, + }; + + Ok(state.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let mut prev_state: TryRuntimeState = TryRuntimeState::::decode(&mut &state[..]) + .expect("Failed to decode the previous storage state"); + + for (suffix, authority_properties) in AuthorityOf::::iter() { + let (prev_account, prev_allocation) = prev_state + .authorities + .remove(&suffix) + .expect("should have authority in previous state"); + assert_eq!(prev_account, authority_properties.account_id); + assert_eq!(prev_allocation, authority_properties.allocation); + } + assert!(prev_state.authorities.is_empty()); + + for (account, identity) in IdentityOf::::iter() { + assert!(identity.deposit > 0u32.into()); + let prev_identity = prev_state + .identities + .remove(&account) + .expect("should have identity in previous state"); + assert_eq!(identity, prev_identity); + } + + for (account, free_identity) in prev_state.identities.iter() { + assert_eq!(free_identity.deposit, 0u32.into()); + assert!(UsernameOf::::contains_key(&account)); + } + prev_state.identities.clear(); + + for (account, primary_username) in UsernameOf::::iter() { + let prev_primary_username = prev_state + .primary_usernames + .remove(&account) + .expect("should have primary username in previous state"); + assert_eq!(prev_primary_username, primary_username); + } + + for (username, username_info) in UsernameInfoOf::::iter() { + let prev_account = prev_state + .usernames + .remove(&username) + .expect("should have username info in previous state"); + assert_eq!(prev_account, username_info.owner); + assert_eq!(username_info.provider, Provider::Allocation); + } + assert!(prev_state.usernames.is_empty()); + + for (username, (account, expiration, provider)) in PendingUsernames::::iter() { + let (prev_account, prev_expiration) = prev_state + .pending_usernames + .remove(&username) + .expect("should have pending username in previous state"); + assert_eq!(prev_account, account); + assert_eq!(prev_expiration, expiration); + assert_eq!(provider, Provider::Allocation); + } + assert!(prev_state.pending_usernames.is_empty()); + + Ok(()) + } + } + + impl LazyMigrationV1ToV2 { + pub(crate) fn required_weight( + step: &MigrationState, Suffix>, + ) -> Weight { + match step { + MigrationState::Authority(_) => T::WeightInfo::migration_v2_authority_step(), + MigrationState::FinishedAuthorities | MigrationState::Username(_) => + T::WeightInfo::migration_v2_username_step(), + MigrationState::FinishedUsernames | MigrationState::Identity(_) => + T::WeightInfo::migration_v2_identity_step(), + MigrationState::FinishedIdentities | MigrationState::PendingUsername(_) => + T::WeightInfo::migration_v2_pending_username_step(), + MigrationState::FinishedPendingUsernames | + MigrationState::CleanupAuthorities(_) => T::WeightInfo::migration_v2_cleanup_authority_step(), + MigrationState::FinishedCleanupAuthorities | + MigrationState::CleanupUsernames(_) => T::WeightInfo::migration_v2_cleanup_username_step(), + MigrationState::Finished => Weight::zero(), + } + } + + // Migrate one entry from `UsernameAuthorities` to `AuthorityOf`. + pub(crate) fn authority_step(maybe_last_key: Option<&T::AccountId>) -> StepResultOf { + let mut iter = if let Some(last_key) = maybe_last_key { + types_v1::UsernameAuthorities::::iter_from( + types_v1::UsernameAuthorities::::hashed_key_for(last_key), + ) + } else { + types_v1::UsernameAuthorities::::iter() + }; + if let Some((authority_account, properties)) = iter.next() { + let suffix = properties.account_id; + let allocation = properties.allocation; + let new_properties = + AuthorityProperties { account_id: authority_account.clone(), allocation }; + AuthorityOf::::insert(&suffix, new_properties); + MigrationState::Authority(authority_account) + } else { + MigrationState::FinishedAuthorities + } + } + + // Migrate one entry from `AccountOfUsername` to `UsernameInfoOf`. + pub(crate) fn username_step(maybe_last_key: Option<&Username>) -> StepResultOf { + let mut iter = if let Some(last_key) = maybe_last_key { + types_v1::AccountOfUsername::::iter_from( + types_v1::AccountOfUsername::::hashed_key_for(last_key), + ) + } else { + types_v1::AccountOfUsername::::iter() + }; + + if let Some((username, owner_account)) = iter.next() { + let username_info = UsernameInformation { + owner: owner_account, + provider: Provider::new_with_allocation(), + }; + UsernameInfoOf::::insert(&username, username_info); + + MigrationState::Username(username) + } else { + MigrationState::FinishedUsernames + } + } + + // Migrate one entry from `IdentityOf` to `UsernameOf`, if it has a username associated with + // it. Remove the entry if there was no real identity associated with the account. + pub(crate) fn identity_step(maybe_last_key: Option) -> StepResultOf { + if let Some(mut last_key) = + IdentityOf::::translate_next::< + ( + Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + >, + Option>, + ), + _, + >(maybe_last_key.map(|b| b.to_vec()), |account, (identity, maybe_username)| { + if let Some(primary_username) = maybe_username { + UsernameOf::::insert(&account, primary_username); + } + if identity.deposit > BalanceOf::::zero() { + Some(identity) + } else { + None + } + }) { + last_key.truncate(HashedKey::bound()); + MigrationState::Identity( + HashedKey::try_from(last_key) + .expect("truncated to bound so the conversion must succeed; qed"), + ) + } else { + MigrationState::FinishedIdentities + } + } + + // Migrate one entry from `PendingUsernames` to contain the new `Provider` field. + pub(crate) fn pending_username_step(maybe_last_key: Option) -> StepResultOf { + if let Some(mut last_key) = + PendingUsernames::::translate_next::<(T::AccountId, BlockNumberFor), _>( + maybe_last_key.map(|b| b.to_vec()), + |_, (owner_account, since)| { + Some((owner_account, since, Provider::new_with_allocation())) + }, + ) { + last_key.truncate(HashedKey::bound()); + MigrationState::PendingUsername( + HashedKey::try_from(last_key) + .expect("truncated to bound so the conversion must succeed; qed"), + ) + } else { + MigrationState::FinishedPendingUsernames + } + } + + // Remove one entry from `UsernameAuthorities`. + pub(crate) fn cleanup_authority_step( + maybe_last_key: Option<&Suffix>, + ) -> StepResultOf { + let mut iter = if let Some(last_key) = maybe_last_key { + AuthorityOf::::iter_from(AuthorityOf::::hashed_key_for(last_key)) + } else { + AuthorityOf::::iter() + }; + + if let Some((suffix, properties)) = iter.next() { + let _ = types_v1::UsernameAuthorities::::take(&properties.account_id); + MigrationState::CleanupAuthorities(suffix) + } else { + MigrationState::FinishedCleanupAuthorities + } + } + + // Remove one entry from `AccountOfUsername`. + pub(crate) fn cleanup_username_step( + maybe_last_key: Option<&Username>, + ) -> StepResultOf { + let mut iter = if let Some(last_key) = maybe_last_key { + UsernameInfoOf::::iter_from(UsernameInfoOf::::hashed_key_for(last_key)) + } else { + UsernameInfoOf::::iter() + }; + + if let Some((username, _)) = iter.next() { + let _ = types_v1::AccountOfUsername::::take(&username); + MigrationState::CleanupUsernames(username) + } else { + MigrationState::Finished + } + } + } + + #[cfg(feature = "runtime-benchmarks")] + pub(crate) struct BenchmarkingSetup { + pub(crate) suffix: S, + pub(crate) authority: A, + pub(crate) account: A, + pub(crate) username: U, + } + + #[cfg(feature = "runtime-benchmarks")] + impl LazyMigrationV1ToV2 { + pub(crate) fn setup_benchmark_env_for_migration() -> BenchmarkingSetupOf { + use frame_support::Hashable; + let suffix: Suffix = b"bench".to_vec().try_into().unwrap(); + let authority: T::AccountId = frame_benchmarking::account("authority", 0, 0); + let account_id: T::AccountId = frame_benchmarking::account("account", 1, 0); + + let prop: AuthorityProperties> = + AuthorityProperties { account_id: suffix.clone(), allocation: 10 }; + types_v1::UsernameAuthorities::::insert(&authority, &prop); + + let username: Username = b"account.bench".to_vec().try_into().unwrap(); + let info = T::IdentityInformation::create_identity_info(); + let registration: Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + > = Registration { judgements: Default::default(), deposit: 10u32.into(), info }; + frame_support::migration::put_storage_value( + b"Identity", + b"IdentityOf", + &account_id.twox_64_concat(), + (®istration, Some(username.clone())), + ); + types_v1::AccountOfUsername::::insert(&username, &account_id); + let since: BlockNumberFor = 0u32.into(); + frame_support::migration::put_storage_value( + b"Identity", + b"PendingUsernames", + &username.blake2_128_concat(), + (&account_id, since), + ); + BenchmarkingSetup { suffix, authority, account: account_id, username } + } + + pub(crate) fn setup_benchmark_env_for_cleanup() -> BenchmarkingSetupOf { + let suffix: Suffix = b"bench".to_vec().try_into().unwrap(); + let authority: T::AccountId = frame_benchmarking::account("authority", 0, 0); + let account_id: T::AccountId = frame_benchmarking::account("account", 1, 0); + + let prop: AuthorityProperties> = + AuthorityProperties { account_id: suffix.clone(), allocation: 10 }; + types_v1::UsernameAuthorities::::insert(&authority, &prop); + let prop: AuthorityProperties = + AuthorityProperties { account_id: authority.clone(), allocation: 10 }; + AuthorityOf::::insert(&suffix, &prop); + + let username: Username = b"account.bench".to_vec().try_into().unwrap(); + let info = T::IdentityInformation::create_identity_info(); + let registration: Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + > = Registration { judgements: Default::default(), deposit: 10u32.into(), info }; + IdentityOf::::insert(&account_id, ®istration); + UsernameOf::::insert(&account_id, &username); + let username_info = UsernameInformation { + owner: account_id.clone(), + provider: Provider::new_with_allocation(), + }; + UsernameInfoOf::::insert(&username, username_info); + types_v1::AccountOfUsername::::insert(&username, &account_id); + let since: BlockNumberFor = 0u32.into(); + PendingUsernames::::insert( + &username, + (&account_id, since, Provider::new_with_allocation()), + ); + BenchmarkingSetup { suffix, authority, account: account_id, username } + } + + pub(crate) fn check_authority_cleanup_validity(suffix: Suffix, authority: T::AccountId) { + assert_eq!(types_v1::UsernameAuthorities::::iter().count(), 0); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().account_id, authority); + } + + pub(crate) fn check_username_cleanup_validity( + username: Username, + account_id: T::AccountId, + ) { + assert_eq!(types_v1::AccountOfUsername::::iter().count(), 0); + assert_eq!(UsernameInfoOf::::get(&username).unwrap().owner, account_id); + } + } + + #[cfg(test)] + mod tests { + use frame_support::Hashable; + + use super::*; + use crate::tests::{new_test_ext, Test}; + + fn registration( + with_deposit: bool, + ) -> Registration< + BalanceOf, + ::MaxRegistrars, + ::IdentityInformation, + > { + Registration { + judgements: Default::default(), + deposit: if with_deposit { 10u32.into() } else { 0u32.into() }, + info: Default::default(), + } + } + + fn account_from_u8(byte: u8) -> ::AccountId { + [byte; 32].into() + } + + #[test] + fn migrate_to_v2() { + new_test_ext().execute_with(|| { + StorageVersion::new(1).put::>(); + // Set up the first authority. + let authority_1 = account_from_u8(151); + let suffix_1: Suffix = b"evn".to_vec().try_into().unwrap(); + let prop = AuthorityProperties { account_id: suffix_1.clone(), allocation: 10 }; + types_v1::UsernameAuthorities::::insert(&authority_1, &prop); + // Set up the first authority. + let authority_2 = account_from_u8(152); + let suffix_2: Suffix = b"odd".to_vec().try_into().unwrap(); + let prop = AuthorityProperties { account_id: suffix_2.clone(), allocation: 10 }; + types_v1::UsernameAuthorities::::insert(&authority_2, &prop); + + // (owner_account, primary_username, maybe_secondary_username, has_identity) + // If `has_identity` is set, this `owner_account` will have a real identity + // associated and a non-zero deposit for it. + let mut usernames = vec![]; + for i in 0u8..100u8 { + let account_id = account_from_u8(i); + let bare_username = format!("acc{}.", i).as_bytes().to_vec(); + let mut username_1 = bare_username.clone(); + username_1.extend(suffix_1.iter()); + let username_1: Username = username_1.try_into().unwrap(); + types_v1::AccountOfUsername::::insert(&username_1, &account_id); + + if i % 2 == 0 { + let has_identity = i % 4 == 0; + let reg = registration(has_identity); + frame_support::migration::put_storage_value( + b"Identity", + b"IdentityOf", + &account_id.twox_64_concat(), + (reg, Some(username_1.clone())), + ); + usernames.push((account_id, username_1, None, has_identity)); + } else { + let has_identity = i % 3 == 0; + let mut username_2 = bare_username.clone(); + username_2.extend(suffix_2.iter()); + let username_2: Username = username_2.try_into().unwrap(); + types_v1::AccountOfUsername::::insert(&username_2, &account_id); + let reg = registration(has_identity); + frame_support::migration::put_storage_value( + b"Identity", + b"IdentityOf", + &account_id.twox_64_concat(), + (reg, Some(username_2.clone())), + ); + usernames.push((account_id, username_2, Some(username_1), has_identity)); + } + } + + // (username, owner_account, since) + let mut pending = vec![]; + for i in 100u8..110u8 { + let account_id = account_from_u8(i); + let mut bare_username = format!("acc{}.", i).as_bytes().to_vec(); + bare_username.extend(suffix_1.iter()); + let username: Username = bare_username.try_into().unwrap(); + let since: BlockNumberFor = i.into(); + frame_support::migration::put_storage_value( + b"Identity", + b"PendingUsernames", + &username.blake2_128_concat(), + (&account_id, since), + ); + pending.push((username, account_id, since)); + } + + let mut identity_only = vec![]; + for i in 120u8..130u8 { + let account_id = account_from_u8(i); + let reg = registration(true); + frame_support::migration::put_storage_value( + b"Identity", + b"IdentityOf", + &account_id.twox_64_concat(), + (reg, None::>), + ); + identity_only.push(account_id); + } + + // Run the actual migration. + let mut weight_meter = WeightMeter::new(); + let mut cursor = None; + while let Some(new_cursor) = + LazyMigrationV1ToV2::::step(cursor, &mut weight_meter).unwrap() + { + cursor = Some(new_cursor); + } + assert_eq!(Pallet::::on_chain_storage_version(), 2); + + // Check that the authorities were migrated. + let expected_prop = + AuthorityProperties { account_id: authority_1.clone(), allocation: 10 }; + assert_eq!(AuthorityOf::::get(&suffix_1), Some(expected_prop)); + + let expected_prop = + AuthorityProperties { account_id: authority_2.clone(), allocation: 10 }; + assert_eq!(AuthorityOf::::get(&suffix_2), Some(expected_prop)); + + // Check that the username information was migrated. + let count_of_usernames_without_identities = + usernames.iter().filter(|(_, _, _, has_id)| *has_id).count(); + assert_eq!(UsernameOf::::iter().count(), usernames.len()); + // All accounts have `evn` usernames, only half of them have `odd` usernames. + assert_eq!( + UsernameInfoOf::::iter().count(), + usernames.len() + usernames.len() / 2 + ); + for (owner, primary, maybe_secondary, has_identity) in usernames.iter() { + let username_info = UsernameInfoOf::::get(primary).unwrap(); + assert_eq!(&username_info.owner, owner); + let actual_primary = UsernameOf::::get(owner).unwrap(); + assert_eq!(primary, &actual_primary); + assert_eq!(IdentityOf::::contains_key(owner), *has_identity); + if let Some(secondary) = maybe_secondary { + let expected_info = UsernameInformation { + owner: owner.clone(), + provider: Provider::new_with_allocation(), + }; + assert_eq!(UsernameInfoOf::::get(secondary), Some(expected_info)); + } + } + + // Check that existing identities were preserved. + for id in identity_only.iter() { + let expected_reg = registration(true); + assert_eq!(IdentityOf::::get(id), Some(expected_reg)); + assert!(!UsernameOf::::contains_key(id)); + } + let identity_count = IdentityOf::::iter().count(); + assert_eq!( + identity_count, + count_of_usernames_without_identities + identity_only.len() + ); + + // Check that pending usernames were migrated. + let pending_count = PendingUsernames::::iter().count(); + assert_eq!(pending_count, pending.len()); + for (username, owner, since) in pending.iter() { + let expected_pending = (owner.clone(), *since, Provider::Allocation); + assert_eq!(PendingUsernames::::get(username), Some(expected_pending)); + } + + // Check that obsolete storage was cleared. + assert_eq!(types_v1::AccountOfUsername::::iter().count(), 0); + assert_eq!(types_v1::UsernameAuthorities::::iter().count(), 0); + }); + } + } +} diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index 3adb823ad5da..a095085a8188 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -77,6 +77,7 @@ impl pallet_identity::Config for Test { type Slashed = (); type BasicDeposit = ConstU64<100>; type ByteDeposit = ConstU64<10>; + type UsernameDeposit = ConstU64<10>; type SubAccountDeposit = ConstU64<100>; type MaxSubAccounts = ConstU32<2>; type IdentityInformation = IdentityInfo; @@ -87,6 +88,7 @@ impl pallet_identity::Config for Test { type SigningPublicKey = AccountPublic; type UsernameAuthorityOrigin = EnsureRoot; type PendingUsernameExpiration = ConstU64<100>; + type UsernameGracePeriod = ConstU64<2>; type MaxSuffixLength = ConstU32<7>; type MaxUsernameLength = ConstU32<32>; type WeightInfo = (); @@ -157,23 +159,21 @@ fn unfunded_accounts() -> [AccountIdOf; 2] { [account(100), account(101)] } -// First return value is a username that would be submitted as a parameter to the dispatchable. As -// in, it has no suffix attached. Second is a full BoundedVec username with suffix, which is what a -// user would need to sign. -fn test_username_of(int: Vec, suffix: Vec) -> (Vec, Username) { +// Returns a full BoundedVec username with suffix, which is what a user would need to sign. +fn test_username_of(int: Vec, suffix: Vec) -> Username { let base = b"testusername"; let mut username = Vec::with_capacity(base.len() + int.len()); username.extend(base); username.extend(int); let mut bounded_username = Vec::with_capacity(username.len() + suffix.len() + 1); - bounded_username.extend(username.clone()); + bounded_username.extend(username); bounded_username.extend(b"."); bounded_username.extend(suffix); let bounded_username = Username::::try_from(bounded_username) .expect("test usernames should fit within bounds"); - (username, bounded_username) + bounded_username } fn infoof_ten() -> IdentityInfo { @@ -401,7 +401,7 @@ fn registration_should_work() { RuntimeOrigin::signed(ten.clone()), Box::new(ten_info.clone()) )); - assert_eq!(IdentityOf::::get(ten.clone()).unwrap().0.info, ten_info); + assert_eq!(IdentityOf::::get(ten.clone()).unwrap().info, ten_info); assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit); assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(ten.clone()))); assert_eq!(Balances::free_balance(ten.clone()), 1000); @@ -485,7 +485,7 @@ fn uninvited_judgement_should_work() { identity_hash )); assert_eq!( - IdentityOf::::get(ten).unwrap().0.judgements, + IdentityOf::::get(ten).unwrap().judgements, vec![(0, Judgement::Reasonable)] ); }); @@ -875,20 +875,14 @@ fn poke_deposit_works() { // Set a custom registration with 0 deposit IdentityOf::::insert::< _, - ( - Registration>, - Option>, - ), + Registration>, >( &ten, - ( - Registration { - judgements: Default::default(), - deposit: Zero::zero(), - info: ten_info.clone(), - }, - None::>, - ), + Registration { + judgements: Default::default(), + deposit: Zero::zero(), + info: ten_info.clone(), + }, ); assert!(IdentityOf::::get(ten.clone()).is_some()); // Set a sub with zero deposit @@ -910,14 +904,11 @@ fn poke_deposit_works() { // new registration deposit is 10 assert_eq!( IdentityOf::::get(&ten), - Some(( - Registration { - judgements: Default::default(), - deposit: id_deposit, - info: infoof_ten() - }, - None - )) + Some(Registration { + judgements: Default::default(), + deposit: id_deposit, + info: infoof_ten() + },) ); // new subs deposit is 10 vvvvvvvvvvvv assert_eq!(SubsOf::::get(ten), (subs_deposit, vec![twenty].try_into().unwrap())); @@ -932,20 +923,14 @@ fn poke_deposit_does_not_insert_new_subs_storage() { // Set a custom registration with 0 deposit IdentityOf::::insert::< _, - ( - Registration>, - Option>, - ), + Registration>, >( &ten, - ( - Registration { - judgements: Default::default(), - deposit: Zero::zero(), - info: ten_info.clone(), - }, - None::>, - ), + Registration { + judgements: Default::default(), + deposit: Zero::zero(), + info: ten_info.clone(), + }, ); assert!(IdentityOf::::get(ten.clone()).is_some()); @@ -961,14 +946,11 @@ fn poke_deposit_does_not_insert_new_subs_storage() { // new registration deposit is 10 assert_eq!( IdentityOf::::get(&ten), - Some(( - Registration { - judgements: Default::default(), - deposit: id_deposit, - info: infoof_ten() - }, - None - )) + Some(Registration { + judgements: Default::default(), + deposit: id_deposit, + info: infoof_ten() + }) ); // No new subs storage item. assert!(!SubsOf::::contains_key(&ten)); @@ -989,10 +971,11 @@ fn adding_and_removing_authorities_should_work() { suffix.clone(), allocation )); + let suffix: Suffix = suffix.try_into().unwrap(); assert_eq!( - UsernameAuthorities::::get(&authority), - Some(AuthorityPropertiesOf:: { - suffix: suffix.clone().try_into().unwrap(), + AuthorityOf::::get(&suffix), + Some(AuthorityProperties::> { + account_id: authority.clone(), allocation }) ); @@ -1001,20 +984,24 @@ fn adding_and_removing_authorities_should_work() { assert_ok!(Identity::add_username_authority( RuntimeOrigin::root(), authority.clone(), - suffix.clone(), + suffix.clone().into(), 11u32 )); assert_eq!( - UsernameAuthorities::::get(&authority), - Some(AuthorityPropertiesOf:: { - suffix: suffix.try_into().unwrap(), + AuthorityOf::::get(&suffix), + Some(AuthorityProperties::> { + account_id: authority.clone(), allocation: 11 }) ); // remove - assert_ok!(Identity::remove_username_authority(RuntimeOrigin::root(), authority.clone(),)); - assert!(UsernameAuthorities::::get(&authority).is_none()); + assert_ok!(Identity::remove_username_authority( + RuntimeOrigin::root(), + suffix.clone().into(), + authority.clone(), + )); + assert!(AuthorityOf::::get(&suffix).is_none()); }); } @@ -1022,7 +1009,9 @@ fn adding_and_removing_authorities_should_work() { fn set_username_with_signature_without_existing_identity_should_work() { new_test_ext().execute_with(|| { // set up authority + let initial_authority_balance = 1000; let [authority, _] = unfunded_accounts(); + Balances::make_free_balance_be(&authority, initial_authority_balance); let suffix: Vec = b"test".to_vec(); let allocation: u32 = 10; assert_ok!(Identity::add_username_authority( @@ -1033,38 +1022,84 @@ fn set_username_with_signature_without_existing_identity_should_work() { )); // set up username - let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix); + let username = test_username_of(b"42".to_vec(), suffix.clone()); // set up user and sign message let public = sr25519_generate(0.into(), None); let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); - let signature = MultiSignature::Sr25519( - sr25519_sign(0.into(), &public, &username_to_sign[..]).unwrap(), - ); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); assert_ok!(Identity::set_username_for( - RuntimeOrigin::signed(authority), + RuntimeOrigin::signed(authority.clone()), who_account.clone(), - username.clone(), - Some(signature) + username.clone().into(), + Some(signature), + true, )); - // Even though user has no balance and no identity, they get a default one for free. + // Even though user has no balance and no identity, the authority provides the username for + // free. + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + // Lookup from username to account works. + let expected_user_info = + UsernameInformation { owner: who_account, provider: Provider::Allocation }; + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) + ); + // No balance was reserved. + assert_eq!(Balances::free_balance(&authority), initial_authority_balance); + // But the allocation decreased. assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(username_to_sign.clone()) - )) + AuthorityOf::::get(&Identity::suffix_of_username(&username).unwrap()) + .unwrap() + .allocation, + 9 ); + + // do the same for a username with a deposit + let username_deposit: BalanceOf = ::UsernameDeposit::get(); + // set up username + let second_username = test_username_of(b"84".to_vec(), suffix.clone()); + + // set up user and sign message + let public = sr25519_generate(1.into(), None); + let second_who: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); + let signature = + MultiSignature::Sr25519(sr25519_sign(1.into(), &public, &second_username[..]).unwrap()); + // don't use the allocation this time + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + second_who.clone(), + second_username.clone().into(), + Some(signature), + false, + )); + + // Even though user has no balance and no identity, the authority placed the deposit for + // them. + assert_eq!(UsernameOf::::get(&second_who), Some(second_username.clone())); // Lookup from username to account works. + let expected_user_info = UsernameInformation { + owner: second_who, + provider: Provider::AuthorityDeposit(username_deposit), + }; + assert_eq!( + UsernameInfoOf::::get::<&Username>(&second_username), + Some(expected_user_info) + ); + // The username deposit was reserved. + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit + ); + // But the allocation was preserved. assert_eq!( - AccountOfUsername::::get::<&Username>(&username_to_sign), - Some(who_account) + AuthorityOf::::get(&Identity::suffix_of_username(&second_username).unwrap()) + .unwrap() + .allocation, + 9 ); }); } @@ -1084,14 +1119,13 @@ fn set_username_with_signature_with_existing_identity_should_work() { )); // set up username - let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix); + let username = test_username_of(b"42".to_vec(), suffix); // set up user and sign message let public = sr25519_generate(0.into(), None); let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); - let signature = MultiSignature::Sr25519( - sr25519_sign(0.into(), &public, &username_to_sign[..]).unwrap(), - ); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); // Set an identity for who. They need some balance though. Balances::make_free_balance_be(&who_account, 1000); @@ -1103,24 +1137,84 @@ fn set_username_with_signature_with_existing_identity_should_work() { assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority), who_account.clone(), - username.clone(), - Some(signature) + username.clone().into(), + Some(signature), + true, )); + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + let expected_user_info = + UsernameInformation { owner: who_account, provider: Provider::Allocation }; assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: id_deposit(&ten_info), - info: ten_info - }, - Some(username_to_sign.clone()) - )) + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) ); + }); +} + +#[test] +fn set_username_through_deposit_with_existing_identity_should_work() { + new_test_ext().execute_with(|| { + // set up authority + let initial_authority_balance = 1000; + let [authority, _] = unfunded_accounts(); + Balances::make_free_balance_be(&authority, initial_authority_balance); + let suffix: Vec = b"test".to_vec(); + let allocation: u32 = 10; + assert_ok!(Identity::add_username_authority( + RuntimeOrigin::root(), + authority.clone(), + suffix.clone(), + allocation + )); + + // set up username + let username = test_username_of(b"42".to_vec(), suffix); + + // set up user and sign message + let public = sr25519_generate(0.into(), None); + let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); + + // Set an identity for who. They need some balance though. + Balances::make_free_balance_be(&who_account, 1000); + let ten_info = infoof_ten(); + let expected_identity_deposit = Identity::calculate_identity_deposit(&ten_info); + assert_ok!(Identity::set_identity( + RuntimeOrigin::signed(who_account.clone()), + Box::new(ten_info.clone()) + )); assert_eq!( - AccountOfUsername::::get::<&Username>(&username_to_sign), - Some(who_account) + expected_identity_deposit, + IdentityOf::::get(&who_account).unwrap().deposit + ); + assert_eq!(Balances::reserved_balance(&who_account), expected_identity_deposit); + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who_account.clone(), + username.clone().into(), + Some(signature), + false, + )); + + let username_deposit: BalanceOf = ::UsernameDeposit::get(); + // The authority placed the deposit for the username. + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit + ); + // No extra balance was reserved from the user for the username. + assert_eq!(Balances::free_balance(&who_account), 1000 - expected_identity_deposit); + assert_eq!(Balances::reserved_balance(&who_account), expected_identity_deposit); + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + let expected_user_info = UsernameInformation { + owner: who_account, + provider: Provider::AuthorityDeposit(username_deposit), + }; + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) ); }); } @@ -1144,8 +1238,8 @@ fn set_username_with_bytes_signature_should_work() { let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); // set up username - let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix); - let unwrapped_username = username_to_sign.to_vec(); + let username = test_username_of(b"42".to_vec(), suffix); + let unwrapped_username = username.to_vec(); // Sign an unwrapped version, as in `username.suffix`. let signature_on_unwrapped = MultiSignature::Sr25519( @@ -1184,27 +1278,20 @@ fn set_username_with_bytes_signature_should_work() { assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority), who_account.clone(), - username, - Some(signature_on_wrapped) + username.clone().into(), + Some(signature_on_wrapped), + true, )); // The username in storage should not include ``. As in, it's the original // `username_to_sign`. - assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(username_to_sign.clone()) - )) - ); + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); // Likewise for the lookup. + let expected_user_info = + UsernameInformation { owner: who_account, provider: Provider::Allocation }; assert_eq!( - AccountOfUsername::::get::<&Username>(&username_to_sign), - Some(who_account) + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) ); }); } @@ -1213,7 +1300,9 @@ fn set_username_with_bytes_signature_should_work() { fn set_username_with_acceptance_should_work() { new_test_ext().execute_with(|| { // set up authority + let initial_authority_balance = 1000; let [authority, who] = unfunded_accounts(); + Balances::make_free_balance_be(&authority, initial_authority_balance); let suffix: Vec = b"test".to_vec(); let allocation: u32 = 10; assert_ok!(Identity::add_username_authority( @@ -1224,45 +1313,82 @@ fn set_username_with_acceptance_should_work() { )); // set up username - let (username, full_username) = test_username_of(b"101".to_vec(), suffix); + let username = test_username_of(b"101".to_vec(), suffix.clone()); let now = frame_system::Pallet::::block_number(); let expiration = now + <::PendingUsernameExpiration as Get>::get(); assert_ok!(Identity::set_username_for( - RuntimeOrigin::signed(authority), + RuntimeOrigin::signed(authority.clone()), who.clone(), - username.clone(), - None + username.clone().into(), + None, + true, )); // Should be pending assert_eq!( - PendingUsernames::::get::<&Username>(&full_username), - Some((who.clone(), expiration)) + PendingUsernames::::get::<&Username>(&username), + Some((who.clone(), expiration, Provider::Allocation)) ); + // Now the user can accept + assert_ok!(Identity::accept_username(RuntimeOrigin::signed(who.clone()), username.clone())); + + // No more pending + assert!(PendingUsernames::::get::<&Username>(&username).is_none()); + // Check Identity storage + assert_eq!(UsernameOf::::get(&who), Some(username.clone())); + // Check reverse lookup + let expected_user_info = UsernameInformation { owner: who, provider: Provider::Allocation }; + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) + ); + assert_eq!(Balances::free_balance(&authority), initial_authority_balance); + + let second_caller = account(99); + let second_username = test_username_of(b"102".to_vec(), suffix); + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + second_caller.clone(), + second_username.clone().into(), + None, + false, + )); + + // Should be pending + let username_deposit = ::UsernameDeposit::get(); + assert_eq!( + PendingUsernames::::get::<&Username>(&second_username), + Some((second_caller.clone(), expiration, Provider::AuthorityDeposit(username_deposit))) + ); + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit + ); // Now the user can accept assert_ok!(Identity::accept_username( - RuntimeOrigin::signed(who.clone()), - full_username.clone() + RuntimeOrigin::signed(second_caller.clone()), + second_username.clone() )); // No more pending - assert!(PendingUsernames::::get::<&Username>(&full_username).is_none()); + assert!(PendingUsernames::::get::<&Username>(&second_username).is_none()); // Check Identity storage + assert_eq!(UsernameOf::::get(&second_caller), Some(second_username.clone())); + // Check reverse lookup + let expected_user_info = UsernameInformation { + owner: second_caller, + provider: Provider::AuthorityDeposit(username_deposit), + }; assert_eq!( - IdentityOf::::get(&who), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(full_username.clone()) - )) + UsernameInfoOf::::get::<&Username>(&second_username), + Some(expected_user_info) + ); + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit ); - // Check reverse lookup - assert_eq!(AccountOfUsername::::get::<&Username>(&full_username), Some(who)); }); } @@ -1295,7 +1421,7 @@ fn invalid_usernames_should_be_rejected() { assert_ok!(Identity::add_username_authority( RuntimeOrigin::root(), authority.clone(), - valid_suffix, + valid_suffix.clone(), allocation )); @@ -1311,25 +1437,33 @@ fn invalid_usernames_should_be_rejected() { //0 1 2 v With `.test` this makes it too long. b"testusernametestusernametest".to_vec(), ]; - for username in invalid_usernames { + for username in invalid_usernames.into_iter().map(|mut username| { + username.push(b'.'); + username.extend(valid_suffix.clone()); + username + }) { assert_noop!( Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), who.clone(), username.clone(), - None + None, + true, ), Error::::InvalidUsername ); } // valid one works - let valid_username = b"testusernametestusernametes".to_vec(); + let mut valid_username = b"testusernametestusernametes".to_vec(); + valid_username.push(b'.'); + valid_username.extend(valid_suffix); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority), who, valid_username, - None + None, + true, )); }); } @@ -1352,21 +1486,24 @@ fn authorities_should_run_out_of_allocation() { assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), pi, - b"username314159".to_vec(), - None + b"username314159.test".to_vec(), + None, + true, )); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), e, - b"username271828".to_vec(), - None + b"username271828.test".to_vec(), + None, + true )); assert_noop!( Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), c, - b"username299792458".to_vec(), - None + b"username299792458.test".to_vec(), + None, + true, ), Error::::NoAllocation ); @@ -1392,91 +1529,65 @@ fn setting_primary_should_work() { let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); // set up username - let (first_username, first_to_sign) = test_username_of(b"42".to_vec(), suffix.clone()); + let first_username = test_username_of(b"42".to_vec(), suffix.clone()); let first_signature = - MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &first_to_sign[..]).unwrap()); + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &first_username[..]).unwrap()); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), who_account.clone(), - first_username.clone(), - Some(first_signature) + first_username.clone().into(), + Some(first_signature), + true )); // First username set as primary. - assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(first_to_sign.clone()) - )) - ); + assert_eq!(UsernameOf::::get(&who_account), Some(first_username.clone())); // set up username - let (second_username, second_to_sign) = test_username_of(b"101".to_vec(), suffix); + let second_username = test_username_of(b"101".to_vec(), suffix); let second_signature = - MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &second_to_sign[..]).unwrap()); + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &second_username[..]).unwrap()); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority), who_account.clone(), - second_username.clone(), - Some(second_signature) + second_username.clone().into(), + Some(second_signature), + true, )); // The primary is still the first username. - assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(first_to_sign.clone()) - )) - ); + assert_eq!(UsernameOf::::get(&who_account), Some(first_username.clone())); // Lookup from both works. + let expected_user_info = + UsernameInformation { owner: who_account.clone(), provider: Provider::Allocation }; assert_eq!( - AccountOfUsername::::get::<&Username>(&first_to_sign), - Some(who_account.clone()) + UsernameInfoOf::::get::<&Username>(&first_username), + Some(expected_user_info.clone()) ); assert_eq!( - AccountOfUsername::::get::<&Username>(&second_to_sign), - Some(who_account.clone()) + UsernameInfoOf::::get::<&Username>(&second_username), + Some(expected_user_info.clone()) ); assert_ok!(Identity::set_primary_username( RuntimeOrigin::signed(who_account.clone()), - second_to_sign.clone() + second_username.clone() )); // The primary is now the second username. - assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: 0, - info: Default::default() - }, - Some(second_to_sign.clone()) - )) - ); + assert_eq!(UsernameOf::::get(&who_account), Some(second_username.clone())); // Lookup from both still works. assert_eq!( - AccountOfUsername::::get::<&Username>(&first_to_sign), - Some(who_account.clone()) + UsernameInfoOf::::get::<&Username>(&first_username), + Some(expected_user_info.clone()) ); assert_eq!( - AccountOfUsername::::get::<&Username>(&second_to_sign), - Some(who_account) + UsernameInfoOf::::get::<&Username>(&second_username), + Some(expected_user_info) ); }); } @@ -1498,60 +1609,67 @@ fn must_own_primary() { // Set up first user ("pi") and a username. let pi_public = sr25519_generate(0.into(), None); let pi_account: AccountIdOf = MultiSigner::Sr25519(pi_public).into_account().into(); - let (pi_username, pi_to_sign) = - test_username_of(b"username314159".to_vec(), suffix.clone()); + let pi_username = test_username_of(b"username314159".to_vec(), suffix.clone()); let pi_signature = - MultiSignature::Sr25519(sr25519_sign(0.into(), &pi_public, &pi_to_sign[..]).unwrap()); + MultiSignature::Sr25519(sr25519_sign(0.into(), &pi_public, &pi_username[..]).unwrap()); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), pi_account.clone(), - pi_username.clone(), - Some(pi_signature) + pi_username.clone().into(), + Some(pi_signature), + true, )); // Set up second user ("e") and a username. let e_public = sr25519_generate(1.into(), None); let e_account: AccountIdOf = MultiSigner::Sr25519(e_public).into_account().into(); - let (e_username, e_to_sign) = test_username_of(b"username271828".to_vec(), suffix.clone()); + let e_username = test_username_of(b"username271828".to_vec(), suffix.clone()); let e_signature = - MultiSignature::Sr25519(sr25519_sign(1.into(), &e_public, &e_to_sign[..]).unwrap()); + MultiSignature::Sr25519(sr25519_sign(1.into(), &e_public, &e_username[..]).unwrap()); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), e_account.clone(), - e_username.clone(), - Some(e_signature) + e_username.clone().into(), + Some(e_signature), + true )); // Ensure that both users have their usernames. + let expected_pi_info = + UsernameInformation { owner: pi_account.clone(), provider: Provider::Allocation }; assert_eq!( - AccountOfUsername::::get::<&Username>(&pi_to_sign), - Some(pi_account.clone()) + UsernameInfoOf::::get::<&Username>(&pi_username), + Some(expected_pi_info) ); + let expected_e_info = + UsernameInformation { owner: e_account.clone(), provider: Provider::Allocation }; assert_eq!( - AccountOfUsername::::get::<&Username>(&e_to_sign), - Some(e_account.clone()) + UsernameInfoOf::::get::<&Username>(&e_username), + Some(expected_e_info) ); // Cannot set primary to a username that does not exist. - let (_, c_username) = test_username_of(b"speedoflight".to_vec(), suffix.clone()); + let c_username = test_username_of(b"speedoflight".to_vec(), suffix.clone()); assert_err!( - Identity::set_primary_username(RuntimeOrigin::signed(pi_account.clone()), c_username,), + Identity::set_primary_username(RuntimeOrigin::signed(pi_account.clone()), c_username), Error::::NoUsername ); // Cannot take someone else's username as your primary. assert_err!( - Identity::set_primary_username(RuntimeOrigin::signed(pi_account.clone()), e_to_sign,), + Identity::set_primary_username(RuntimeOrigin::signed(pi_account.clone()), e_username), Error::::InvalidUsername ); }); } #[test] -fn unaccepted_usernames_should_expire() { +fn unaccepted_usernames_through_grant_should_expire() { new_test_ext().execute_with(|| { // set up authority + let initial_authority_balance = 1000; let [authority, who] = unfunded_accounts(); + Balances::make_free_balance_be(&authority, initial_authority_balance); let suffix: Vec = b"test".to_vec(); let allocation: u32 = 10; assert_ok!(Identity::add_username_authority( @@ -1562,31 +1680,34 @@ fn unaccepted_usernames_should_expire() { )); // set up username - let (username, full_username) = test_username_of(b"101".to_vec(), suffix); + let username = test_username_of(b"101".to_vec(), suffix.clone()); let now = frame_system::Pallet::::block_number(); let expiration = now + <::PendingUsernameExpiration as Get>::get(); + let suffix: Suffix = suffix.try_into().unwrap(); + + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); assert_ok!(Identity::set_username_for( - RuntimeOrigin::signed(authority), + RuntimeOrigin::signed(authority.clone()), who.clone(), - username.clone(), - None + username.clone().into(), + None, + true, )); + assert_eq!(Balances::free_balance(&authority), initial_authority_balance); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 9); // Should be pending assert_eq!( - PendingUsernames::::get::<&Username>(&full_username), - Some((who.clone(), expiration)) + PendingUsernames::::get::<&Username>(&username), + Some((who.clone(), expiration, Provider::Allocation)) ); run_to_block(now + expiration - 1); // Cannot be removed assert_noop!( - Identity::remove_expired_approval( - RuntimeOrigin::signed(account(1)), - full_username.clone() - ), + Identity::remove_expired_approval(RuntimeOrigin::signed(account(1)), username.clone()), Error::::NotExpired ); @@ -1595,19 +1716,98 @@ fn unaccepted_usernames_should_expire() { // Anyone can remove assert_ok!(Identity::remove_expired_approval( RuntimeOrigin::signed(account(1)), - full_username.clone() + username.clone() )); + assert_eq!(Balances::free_balance(&authority), initial_authority_balance); + // Allocation wasn't refunded + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 9); // No more pending - assert!(PendingUsernames::::get::<&Username>(&full_username).is_none()); + assert!(PendingUsernames::::get::<&Username>(&username).is_none()); }); } #[test] -fn removing_dangling_usernames_should_work() { +fn unaccepted_usernames_through_deposit_should_expire() { new_test_ext().execute_with(|| { // set up authority - let [authority, caller] = unfunded_accounts(); + let initial_authority_balance = 1000; + let [authority, who] = unfunded_accounts(); + Balances::make_free_balance_be(&authority, initial_authority_balance); + let suffix: Vec = b"test".to_vec(); + let allocation: u32 = 10; + assert_ok!(Identity::add_username_authority( + RuntimeOrigin::root(), + authority.clone(), + suffix.clone(), + allocation + )); + + // set up username + let username = test_username_of(b"101".to_vec(), suffix.clone()); + let now = frame_system::Pallet::::block_number(); + let expiration = now + <::PendingUsernameExpiration as Get>::get(); + + let suffix: Suffix = suffix.try_into().unwrap(); + let username_deposit: BalanceOf = ::UsernameDeposit::get(); + + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who.clone(), + username.clone().into(), + None, + false, + )); + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit + ); + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); + + // Should be pending + assert_eq!( + PendingUsernames::::get::<&Username>(&username), + Some((who.clone(), expiration, Provider::AuthorityDeposit(username_deposit))) + ); + + run_to_block(now + expiration - 1); + + // Cannot be removed + assert_noop!( + Identity::remove_expired_approval(RuntimeOrigin::signed(account(1)), username.clone()), + Error::::NotExpired + ); + + run_to_block(now + expiration); + + // Anyone can remove + assert_eq!( + Balances::free_balance(&authority), + initial_authority_balance - username_deposit + ); + assert_eq!(Balances::reserved_balance(&authority), username_deposit); + assert_ok!(Identity::remove_expired_approval( + RuntimeOrigin::signed(account(1)), + username.clone() + )); + // Deposit was refunded + assert_eq!(Balances::free_balance(&authority), initial_authority_balance); + // Allocation wasn't refunded + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 10); + + // No more pending + assert!(PendingUsernames::::get::<&Username>(&username).is_none()); + }); +} + +#[test] +fn kill_username_should_work() { + new_test_ext().execute_with(|| { + let initial_authority_balance = 10000; + // set up first authority + let authority = account(100); + Balances::make_free_balance_be(&authority, initial_authority_balance); let suffix: Vec = b"test".to_vec(); let allocation: u32 = 10; assert_ok!(Identity::add_username_authority( @@ -1617,99 +1817,421 @@ fn removing_dangling_usernames_should_work() { allocation )); + let second_authority = account(200); + Balances::make_free_balance_be(&second_authority, initial_authority_balance); + let second_suffix: Vec = b"abc".to_vec(); + assert_ok!(Identity::add_username_authority( + RuntimeOrigin::root(), + second_authority.clone(), + second_suffix.clone(), + allocation + )); + + let username_deposit = ::UsernameDeposit::get(); + // set up username - let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix.clone()); + let username = test_username_of(b"42".to_vec(), suffix.clone()); // set up user and sign message let public = sr25519_generate(0.into(), None); let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); - let signature = MultiSignature::Sr25519( - sr25519_sign(0.into(), &public, &username_to_sign[..]).unwrap(), - ); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); // Set an identity for who. They need some balance though. Balances::make_free_balance_be(&who_account, 1000); - let ten_info = infoof_ten(); - assert_ok!(Identity::set_identity( - RuntimeOrigin::signed(who_account.clone()), - Box::new(ten_info.clone()) - )); assert_ok!(Identity::set_username_for( RuntimeOrigin::signed(authority.clone()), who_account.clone(), - username.clone(), - Some(signature) + username.clone().into(), + Some(signature), + false )); + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - username_deposit + ); // Now they set up a second username. - let (username_two, username_two_to_sign) = test_username_of(b"43".to_vec(), suffix); + let username_two = test_username_of(b"43".to_vec(), suffix.clone()); // set up user and sign message - let signature_two = MultiSignature::Sr25519( - sr25519_sign(0.into(), &public, &username_two_to_sign[..]).unwrap(), + let signature_two = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username_two[..]).unwrap()); + + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who_account.clone(), + username_two.clone().into(), + Some(signature_two), + false + )); + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - 2 * username_deposit ); + // Now they set up a third username with another authority. + let username_three = test_username_of(b"42".to_vec(), second_suffix.clone()); + + // set up user and sign message + let signature_three = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username_three[..]).unwrap()); + assert_ok!(Identity::set_username_for( - RuntimeOrigin::signed(authority), + RuntimeOrigin::signed(second_authority.clone()), who_account.clone(), - username_two.clone(), - Some(signature_two) + username_three.clone().into(), + Some(signature_three), + true )); + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - 2 * username_deposit + ); + assert_eq!(Balances::free_balance(second_authority.clone()), initial_authority_balance); // The primary should still be the first one. + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + + // But both usernames should look up the account. + let expected_user_info = UsernameInformation { + owner: who_account.clone(), + provider: Provider::AuthorityDeposit(username_deposit), + }; assert_eq!( - IdentityOf::::get(&who_account), - Some(( - Registration { - judgements: Default::default(), - deposit: id_deposit(&ten_info), - info: ten_info - }, - Some(username_to_sign.clone()) - )) + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info.clone()) + ); + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username_two), + Some(expected_user_info.clone()) ); + // Regular accounts can't kill a username, not even the authority that granted it. + assert_noop!( + Identity::kill_username(RuntimeOrigin::signed(authority.clone()), username.clone()), + BadOrigin + ); + + // Can't kill a username that doesn't exist. + assert_noop!( + Identity::kill_username( + RuntimeOrigin::root(), + test_username_of(b"999".to_vec(), suffix.clone()) + ), + Error::::NoUsername + ); + + // Unbind the second username. + assert_ok!(Identity::unbind_username( + RuntimeOrigin::signed(authority.clone()), + username_two.clone() + )); + + // Kill the second username. + assert_ok!(Identity::kill_username(RuntimeOrigin::root(), username_two.clone().into())); + + // The reverse lookup of the primary is gone. + assert!(UsernameInfoOf::::get::<&Username>(&username_two).is_none()); + // The unbinding map entry is gone. + assert!(UnbindingUsernames::::get::<&Username>(&username).is_none()); + // The authority's deposit was slashed. + assert_eq!(Balances::reserved_balance(authority.clone()), username_deposit); + + // But the reverse lookup of the primary is still there + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info) + ); + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + assert!(UsernameInfoOf::::contains_key(&username_three)); + + // Kill the first, primary username. + assert_ok!(Identity::kill_username(RuntimeOrigin::root(), username.clone().into())); + + // The reverse lookup of the primary is gone. + assert!(UsernameInfoOf::::get::<&Username>(&username).is_none()); + assert!(!UsernameOf::::contains_key(&who_account)); + // The authority's deposit was slashed. + assert_eq!(Balances::reserved_balance(authority.clone()), 0); + + // But the reverse lookup of the third and final username is still there + let expected_user_info = + UsernameInformation { owner: who_account.clone(), provider: Provider::Allocation }; + assert_eq!( + UsernameInfoOf::::get::<&Username>(&username_three), + Some(expected_user_info) + ); + + // Kill the third and last username. + assert_ok!(Identity::kill_username(RuntimeOrigin::root(), username_three.clone().into())); + // Everything is gone. + assert!(!UsernameInfoOf::::contains_key(&username_three)); + }); +} + +#[test] +fn unbind_and_remove_username_should_work() { + new_test_ext().execute_with(|| { + let initial_authority_balance = 10000; + // Set up authority. + let authority = account(100); + Balances::make_free_balance_be(&authority, initial_authority_balance); + let suffix: Vec = b"test".to_vec(); + let allocation: u32 = 10; + assert_ok!(Identity::add_username_authority( + RuntimeOrigin::root(), + authority.clone(), + suffix.clone(), + allocation + )); + + let username_deposit = ::UsernameDeposit::get(); + + // Set up username. + let username = test_username_of(b"42".to_vec(), suffix.clone()); + + // Set up user and sign message. + let public = sr25519_generate(0.into(), None); + let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); + + // Set an identity for who. They need some balance though. + Balances::make_free_balance_be(&who_account, 1000); + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who_account.clone(), + username.clone().into(), + Some(signature), + false + )); + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - username_deposit + ); + + // Now they set up a second username. + let username_two = test_username_of(b"43".to_vec(), suffix.clone()); + + // Set up user and sign message. + let signature_two = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username_two[..]).unwrap()); + + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who_account.clone(), + username_two.clone().into(), + Some(signature_two), + true + )); + // Second one is free. + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - username_deposit + ); + + // The primary should still be the first one. + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + // But both usernames should look up the account. + let expected_user_info = UsernameInformation { + owner: who_account.clone(), + provider: Provider::AuthorityDeposit(username_deposit), + }; assert_eq!( - AccountOfUsername::::get::<&Username>(&username_to_sign), - Some(who_account.clone()) + UsernameInfoOf::::get::<&Username>(&username), + Some(expected_user_info.clone()) ); + let expected_user_info = + UsernameInformation { owner: who_account.clone(), provider: Provider::Allocation }; assert_eq!( - AccountOfUsername::::get::<&Username>(&username_two_to_sign), - Some(who_account.clone()) + UsernameInfoOf::::get::<&Username>(&username_two), + Some(expected_user_info.clone()) ); - // Someone tries to remove it, but they can't + // Regular accounts can't kill a username, not even the authority that granted it. assert_noop!( - Identity::remove_dangling_username( - RuntimeOrigin::signed(caller.clone()), - username_to_sign.clone() + Identity::kill_username(RuntimeOrigin::signed(authority.clone()), username.clone()), + BadOrigin + ); + + // Can't unbind a username that doesn't exist. + let dummy_suffix = b"abc".to_vec(); + let dummy_username = test_username_of(b"999".to_vec(), dummy_suffix.clone()); + let dummy_authority = account(78); + assert_noop!( + Identity::unbind_username( + RuntimeOrigin::signed(dummy_authority.clone()), + dummy_username.clone() ), - Error::::InvalidUsername + Error::::NoUsername ); - // Now the user calls `clear_identity` - assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(who_account.clone()),)); + let dummy_suffix: Suffix = dummy_suffix.try_into().unwrap(); + // Only the authority that granted the username can unbind it. + UsernameInfoOf::::insert( + dummy_username.clone(), + UsernameInformation { owner: who_account.clone(), provider: Provider::Allocation }, + ); + assert_noop!( + Identity::unbind_username( + RuntimeOrigin::signed(dummy_authority.clone()), + dummy_username.clone() + ), + Error::::NotUsernameAuthority + ); + // Simulate a dummy authority. + AuthorityOf::::insert( + dummy_suffix.clone(), + AuthorityProperties { account_id: dummy_authority.clone(), allocation: 10 }, + ); + // But try to remove the dummy username as a different authority, not the one that + // originally granted the username. + assert_noop!( + Identity::unbind_username( + RuntimeOrigin::signed(authority.clone()), + dummy_username.clone() + ), + Error::::NotUsernameAuthority + ); + // Clean up storage. + let _ = UsernameInfoOf::::take(dummy_username.clone()); + let _ = AuthorityOf::::take(dummy_suffix); - // Identity is gone - assert!(IdentityOf::::get(who_account.clone()).is_none()); + // We can successfully unbind the username as the authority that granted it. + assert_ok!(Identity::unbind_username( + RuntimeOrigin::signed(authority.clone()), + username_two.clone() + )); + let grace_period: BlockNumberFor = ::UsernameGracePeriod::get(); + let now = 1; + assert_eq!(System::block_number(), now); + let expected_grace_period_expiry: BlockNumberFor = now + grace_period; + assert_eq!( + UnbindingUsernames::::get(&username_two), + Some(expected_grace_period_expiry) + ); - // The reverse lookup of the primary is gone. - assert!(AccountOfUsername::::get::<&Username>(&username_to_sign).is_none()); + // Still in the grace period. + assert_noop!( + Identity::remove_username(RuntimeOrigin::signed(account(0)), username_two.clone()), + Error::::TooEarly + ); + + // Advance the block number to simulate the grace period passing. + System::set_block_number(expected_grace_period_expiry); + + let suffix: Suffix = suffix.try_into().unwrap(); + // We can now remove the username from any account. + assert_ok!(Identity::remove_username( + RuntimeOrigin::signed(account(0)), + username_two.clone() + )); + // The username is gone. + assert!(!UnbindingUsernames::::contains_key(&username_two)); + assert!(!UsernameInfoOf::::contains_key(&username_two)); + // Primary username was preserved. + assert_eq!(UsernameOf::::get(&who_account), Some(username.clone())); + // The username was granted through a governance allocation, so no deposit was released. + assert_eq!( + Balances::free_balance(authority.clone()), + initial_authority_balance - username_deposit + ); + // Allocation wasn't refunded. + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 9); + + // Unbind the first username as well. + assert_ok!(Identity::unbind_username( + RuntimeOrigin::signed(authority.clone()), + username.clone() + )); + let now: BlockNumberFor = expected_grace_period_expiry; + assert_eq!(System::block_number(), now); + let expected_grace_period_expiry: BlockNumberFor = now + grace_period; + assert_eq!(UnbindingUsernames::::get(&username), Some(expected_grace_period_expiry)); + // Advance the block number to simulate the grace period passing. + System::set_block_number(expected_grace_period_expiry); + // We can now remove the username from any account. + assert_ok!(Identity::remove_username(RuntimeOrigin::signed(account(0)), username.clone())); + // The username is gone. + assert!(!UnbindingUsernames::::contains_key(&username)); + assert!(!UsernameInfoOf::::contains_key(&username)); + // Primary username was also removed. + assert!(!UsernameOf::::contains_key(&who_account)); + // The username deposit was released. + assert_eq!(Balances::free_balance(authority.clone()), initial_authority_balance); + // Allocation didn't change. + assert_eq!(AuthorityOf::::get(&suffix).unwrap().allocation, 9); + }); +} + +#[test] +#[should_panic] +fn unbind_dangling_username_defensive_should_panic() { + new_test_ext().execute_with(|| { + let initial_authority_balance = 10000; + // Set up authority. + let authority = account(100); + Balances::make_free_balance_be(&authority, initial_authority_balance); + let suffix: Vec = b"test".to_vec(); + let allocation: u32 = 10; + assert_ok!(Identity::add_username_authority( + RuntimeOrigin::root(), + authority.clone(), + suffix.clone(), + allocation + )); - // But the reverse lookup of the non-primary is still there + let username_deposit: BalanceOf = ::UsernameDeposit::get(); + + // Set up username. + let username = test_username_of(b"42".to_vec(), suffix.clone()); + + // Set up user and sign message. + let public = sr25519_generate(0.into(), None); + let who_account: AccountIdOf = MultiSigner::Sr25519(public).into_account().into(); + let signature = + MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &username[..]).unwrap()); + + // Set an identity for who. They need some balance though. + Balances::make_free_balance_be(&who_account, 1000); + assert_ok!(Identity::set_username_for( + RuntimeOrigin::signed(authority.clone()), + who_account.clone(), + username.clone().into(), + Some(signature), + false + )); assert_eq!( - AccountOfUsername::::get::<&Username>(&username_two_to_sign), - Some(who_account) + Balances::free_balance(authority.clone()), + initial_authority_balance - username_deposit ); - // Now it can be removed - assert_ok!(Identity::remove_dangling_username( - RuntimeOrigin::signed(caller), - username_two_to_sign.clone() + // We can successfully unbind the username as the authority that granted it. + assert_ok!(Identity::unbind_username( + RuntimeOrigin::signed(authority.clone()), + username.clone() )); + assert_eq!(System::block_number(), 1); + assert_eq!(UnbindingUsernames::::get(&username), Some(1)); + + // Still in the grace period. + assert_noop!( + Identity::remove_username(RuntimeOrigin::signed(account(0)), username.clone()), + Error::::TooEarly + ); - // And the reverse lookup is gone - assert!(AccountOfUsername::::get::<&Username>(&username_two_to_sign).is_none()); + // Advance the block number to simulate the grace period passing. + System::set_block_number(3); + + // Simulate a dangling entry in the unbinding map without an actual username registered. + UsernameInfoOf::::remove(&username); + UsernameOf::::remove(&who_account); + assert_noop!( + Identity::remove_username(RuntimeOrigin::signed(account(0)), username.clone()), + Error::::NoUsername + ); }); } diff --git a/substrate/frame/identity/src/types.rs b/substrate/frame/identity/src/types.rs index 45401d53e9e9..ece3c34f82ef 100644 --- a/substrate/frame/identity/src/types.rs +++ b/substrate/frame/identity/src/types.rs @@ -320,9 +320,6 @@ pub struct RegistrarInfo< pub fields: IdField, } -/// Authority properties for a given pallet configuration. -pub type AuthorityPropertiesOf = AuthorityProperties>; - /// The number of usernames that an authority may allocate. type Allocation = u32; /// A byte vec used to represent a username. @@ -330,11 +327,9 @@ pub(crate) type Suffix = BoundedVec::MaxSuffixLength>; /// Properties of a username authority. #[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Debug)] -pub struct AuthorityProperties { - /// The suffix added to usernames granted by this authority. Will be appended to usernames; for - /// example, a suffix of `wallet` will result in `.wallet` being appended to a user's selected - /// name. - pub suffix: Suffix, +pub struct AuthorityProperties { + /// The account of the authority. + pub account_id: Account, /// The number of usernames remaining that this authority can grant. pub allocation: Allocation, } @@ -342,6 +337,34 @@ pub struct AuthorityProperties { /// A byte vec used to represent a username. pub(crate) type Username = BoundedVec::MaxUsernameLength>; +#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Debug)] +pub enum Provider { + Allocation, + AuthorityDeposit(Balance), + System, +} + +impl Provider { + pub fn new_with_allocation() -> Self { + Self::Allocation + } + + pub fn new_with_deposit(deposit: Balance) -> Self { + Self::AuthorityDeposit(deposit) + } + + #[allow(unused)] + pub fn new_permanent() -> Self { + Self::System + } +} + +#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Debug)] +pub struct UsernameInformation { + pub owner: Account, + pub provider: Provider, +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/frame/identity/src/weights.rs b/substrate/frame/identity/src/weights.rs index 008d5465bb4f..a74cca9dc8ec 100644 --- a/substrate/frame/identity/src/weights.rs +++ b/substrate/frame/identity/src/weights.rs @@ -69,11 +69,19 @@ pub trait WeightInfo { fn quit_sub(s: u32, ) -> Weight; fn add_username_authority() -> Weight; fn remove_username_authority() -> Weight; - fn set_username_for() -> Weight; + fn set_username_for(p: u32) -> Weight; fn accept_username() -> Weight; - fn remove_expired_approval() -> Weight; + fn remove_expired_approval(p: u32) -> Weight; fn set_primary_username() -> Weight; - fn remove_dangling_username() -> Weight; + fn unbind_username() -> Weight; + fn remove_username() -> Weight; + fn kill_username(p: u32) -> Weight; + fn migration_v2_authority_step() -> Weight; + fn migration_v2_username_step() -> Weight; + fn migration_v2_identity_step() -> Weight; + fn migration_v2_pending_username_step() -> Weight; + fn migration_v2_cleanup_authority_step() -> Weight; + fn migration_v2_cleanup_username_step() -> Weight; } /// Weights for `pallet_identity` using the Substrate node and recommended hardware. @@ -380,7 +388,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -406,7 +414,7 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Identity::PendingUsernames` (r:1 w:1) /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + fn remove_expired_approval(_p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `3550` @@ -428,18 +436,32 @@ impl WeightInfo for SubstrateWeight { .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(81), added: 2556, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `11037` - // Minimum execution time: 12_017_000 picoseconds. - Weight::from_parts(12_389_000, 11037) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } @@ -746,7 +768,7 @@ impl WeightInfo for () { /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn set_username_for() -> Weight { + fn set_username_for(_p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` @@ -772,7 +794,7 @@ impl WeightInfo for () { } /// Storage: `Identity::PendingUsernames` (r:1 w:1) /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) - fn remove_expired_approval() -> Weight { + fn remove_expired_approval(_p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `3550` @@ -794,17 +816,31 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) - /// Storage: `Identity::IdentityOf` (r:1 w:0) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) - fn remove_dangling_username() -> Weight { - // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `11037` - // Minimum execution time: 12_017_000 picoseconds. - Weight::from_parts(12_389_000, 11037) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + fn unbind_username() -> Weight { + Weight::zero() + } + fn remove_username() -> Weight { + Weight::zero() + } + fn kill_username(_p: u32) -> Weight { + Weight::zero() + } + fn migration_v2_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_identity_step() -> Weight { + Weight::zero() + } + fn migration_v2_pending_username_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_authority_step() -> Weight { + Weight::zero() + } + fn migration_v2_cleanup_username_step() -> Weight { + Weight::zero() } } From 17eee37c315d2e19848b622c041c71f07e6b8d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 29 Oct 2024 17:24:20 +0100 Subject: [PATCH 57/58] pallet-revive: Use custom target to build test fixtures (#6266) This removes the need to use a custom toolchain to build the contract test fixtures. Instead, we supply a custom target and use the currently in use upstream toolchain. --------- Co-authored-by: Jan Bujak Co-authored-by: Cyrill Leutwiler Co-authored-by: command-bot <> --- Cargo.lock | 56 ++++++++++++------- substrate/frame/revive/Cargo.toml | 2 - substrate/frame/revive/fixtures/Cargo.toml | 2 +- substrate/frame/revive/fixtures/build.rs | 55 ++++++++++-------- .../frame/revive/fixtures/build/Cargo.toml | 2 +- .../fixtures/contracts/oom_rw_included.rs | 7 ++- .../fixtures/contracts/oom_rw_trailing.rs | 7 ++- .../riscv32emac-unknown-none-polkavm.json | 26 +++++++++ substrate/frame/revive/src/limits.rs | 2 +- substrate/frame/revive/uapi/Cargo.toml | 2 +- 10 files changed, 108 insertions(+), 53 deletions(-) create mode 100644 substrate/frame/revive/fixtures/riscv32emac-unknown-none-polkavm.json diff --git a/Cargo.lock b/Cargo.lock index d091e46e9bf9..5bc51b7840ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6997,6 +6997,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator 0.3.0", + "stable_deref_trait", +] + [[package]] name = "glob" version = "0.3.1" @@ -12520,7 +12530,6 @@ dependencies = [ "parity-scale-codec", "paste", "polkavm 0.13.0", - "polkavm-common 0.13.0", "pretty_assertions", "rlp 0.6.1", "scale-info", @@ -12584,7 +12593,7 @@ dependencies = [ "frame-system", "log", "parity-wasm", - "polkavm-linker 0.13.0", + "polkavm-linker 0.14.0", "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", @@ -12644,7 +12653,7 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive 0.13.0", + "polkavm-derive 0.14.0", "scale-info", ] @@ -16405,11 +16414,16 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "084b4339aae7dfdaaa5aa7d634110afd95970e0737b6fb2a0cb10db8b56b753c" dependencies = [ - "blake3", "log", "polkavm-assembler 0.13.0", ] +[[package]] +name = "polkavm-common" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711952a783e9c5ad407cdacb1ed147f36d37c5d43417c1091d86456d2999417b" + [[package]] name = "polkavm-derive" version = "0.8.0" @@ -16430,11 +16444,11 @@ dependencies = [ [[package]] name = "polkavm-derive" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4456b9657b2abd04ac41a61c99e206b7410f93daf0e9b42b49089508d836c40" +checksum = "b4832a0aebf6cefc988bb7b2d74ea8c86c983164672e2fc96300f356a1babfc1" dependencies = [ - "polkavm-derive-impl-macro 0.13.0", + "polkavm-derive-impl-macro 0.14.0", ] [[package]] @@ -16463,11 +16477,11 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4f2c19e7ccc53d8e21429e83b6589bd4139d15481e455a90ba4335a4decb5a" +checksum = "e339fc7c11310fe5adf711d9342278ac44a75c9784947937cce12bd4f30842f2" dependencies = [ - "polkavm-common 0.13.0", + "polkavm-common 0.14.0", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16495,11 +16509,11 @@ dependencies = [ [[package]] name = "polkavm-derive-impl-macro" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f3ad876ca1855038c21d48cbe35164552208a54b21f8295a7d76bc33ef1e38" +checksum = "b569754b15060d03000c09e3bf11509d527f60b75d79b4c30c3625b5071d9702" dependencies = [ - "polkavm-derive-impl 0.13.0", + "polkavm-derive-impl 0.14.0", "syn 2.0.82", ] @@ -16520,15 +16534,15 @@ dependencies = [ [[package]] name = "polkavm-linker" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa6e5a396abf195289d6d63d70182e59a7c27b9b06d0b7361317df05c07c8a8" +checksum = "0959ac3b0f4fd5caf5c245c637705f19493efe83dba31a83bbba928b93b0116a" dependencies = [ - "gimli 0.28.0", + "gimli 0.31.1", "hashbrown 0.14.5", "log", "object 0.36.1", - "polkavm-common 0.13.0", + "polkavm-common 0.14.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -16986,8 +17000,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -17020,7 +17034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -17033,7 +17047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 8dbad5ffd8b6..c6e733477f38 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -20,7 +20,6 @@ targets = ["x86_64-unknown-linux-gnu"] environmental = { workspace = true } paste = { workspace = true } polkavm = { version = "0.13.0", default-features = false } -polkavm-common = { version = "0.13.0", default-features = false } bitflags = { workspace = true } codec = { features = ["derive", "max-encoded-len"], workspace = true } scale-info = { features = ["derive"], workspace = true } @@ -100,7 +99,6 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-utility/std", - "polkavm-common/std", "polkavm/std", "rlp/std", "scale-info/std", diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 1d89db002b72..1e6c950addfd 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -21,7 +21,7 @@ log = { workspace = true } parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } -polkavm-linker = { version = "0.13.0" } +polkavm-linker = { version = "0.14.0" } anyhow = { workspace = true, default-features = true } [features] diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index ee7db4203cc9..38d63621677d 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -32,6 +32,10 @@ mod build { process::Command, }; + const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN"; + const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP"; + const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE"; + /// A contract entry. struct Entry { /// The path to the contract source file. @@ -112,54 +116,50 @@ mod build { fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into) } - fn invoke_build(current_dir: &Path) -> Result<()> { - let encoded_rustflags = [ - "-Dwarnings", - "-Crelocation-model=pie", - "-Clink-arg=--emit-relocs", - "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", - ] - .join("\x1f"); + fn invoke_build(target: &Path, current_dir: &Path) -> Result<()> { + let encoded_rustflags = ["-Dwarnings"].join("\x1f"); - let build_res = Command::new(env::var("CARGO")?) + let mut build_command = Command::new(env::var("CARGO")?); + build_command .current_dir(current_dir) .env_clear() .env("PATH", env::var("PATH").unwrap_or_default()) .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) - .env("RUSTUP_TOOLCHAIN", "rve-nightly") .env("RUSTC_BOOTSTRAP", "1") .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) .args([ "build", "--release", - "--target=riscv32ema-unknown-none-elf", "-Zbuild-std=core", "-Zbuild-std-features=panic_immediate_abort", ]) - .output() - .expect("failed to execute process"); + .arg("--target") + .arg(target); + + if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) { + build_command.env("RUSTUP_TOOLCHAIN", &toolchain); + } + + let build_res = build_command.output().expect("failed to execute process"); if build_res.status.success() { return Ok(()) } let stderr = String::from_utf8_lossy(&build_res.stderr); - - if stderr.contains("'rve-nightly' is not installed") { - eprintln!("RISC-V toolchain is not installed.\nDownload and install toolchain from https://github.com/paritytech/rustc-rv32e-toolchain."); - eprintln!("{}", stderr); - } else { - eprintln!("{}", stderr); - } + eprintln!("{}", stderr); bail!("Failed to build contracts"); } /// Post-process the compiled code. fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { + let strip = std::env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1"); + let optimize = std::env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1"); + let mut config = polkavm_linker::Config::default(); - config.set_strip(false); - config.set_optimize(true); + config.set_strip(strip); + config.set_optimize(optimize); let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) @@ -171,7 +171,9 @@ mod build { fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { for entry in entries { post_process( - &build_dir.join("target/riscv32ema-unknown-none-elf/release").join(entry.name()), + &build_dir + .join("target/riscv32emac-unknown-none-polkavm/release") + .join(entry.name()), &out_dir.join(entry.out_filename()), )?; } @@ -183,6 +185,11 @@ mod build { let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); let contracts_dir = fixtures_dir.join("contracts"); let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + let target = fixtures_dir.join("riscv32emac-unknown-none-polkavm.json"); + + println!("cargo::rerun-if-env-changed={OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR}"); + println!("cargo::rerun-if-env-changed={OVERRIDE_STRIP_ENV_VAR}"); + println!("cargo::rerun-if-env-changed={OVERRIDE_OPTIMIZE_ENV_VAR}"); // the fixtures have a dependency on the uapi crate println!("cargo::rerun-if-changed={}", fixtures_dir.display()); @@ -200,7 +207,7 @@ mod build { let tmp_dir_path = tmp_dir.path(); create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; - invoke_build(tmp_dir_path)?; + invoke_build(&target, tmp_dir_path)?; write_output(tmp_dir_path, &out_dir, entries)?; diff --git a/substrate/frame/revive/fixtures/build/Cargo.toml b/substrate/frame/revive/fixtures/build/Cargo.toml index c4aaf131148e..5d0e256e2e73 100644 --- a/substrate/frame/revive/fixtures/build/Cargo.toml +++ b/substrate/frame/revive/fixtures/build/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] uapi = { package = 'pallet-revive-uapi', path = "", default-features = false } common = { package = 'pallet-revive-fixtures-common', path = "" } -polkavm-derive = { version = "0.13.0" } +polkavm-derive = { version = "0.14.0" } [profile.release] opt-level = 3 diff --git a/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs b/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs index 2cdcf7bafed1..123ee38a5200 100644 --- a/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs +++ b/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs @@ -28,11 +28,16 @@ use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; static mut BUFFER: [u8; 513 * 1024] = [42; 513 * 1024]; +unsafe fn buffer() -> &'static [u8; 513 * 1024] { + let ptr = core::ptr::addr_of!(BUFFER); + &*ptr +} + #[no_mangle] #[polkavm_derive::polkavm_export] pub unsafe extern "C" fn call_never() { // make sure the buffer is not optimized away - api::return_value(ReturnFlags::empty(), &BUFFER); + api::return_value(ReturnFlags::empty(), buffer()); } #[no_mangle] diff --git a/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs index ddd4139db3ed..e127effca20c 100644 --- a/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs +++ b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs @@ -28,11 +28,16 @@ use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; static mut BUFFER: [u8; 2 * 1025 * 1024] = [0; 2 * 1025 * 1024]; +unsafe fn buffer() -> &'static [u8; 2 * 1025 * 1024] { + let ptr = core::ptr::addr_of!(BUFFER); + &*ptr +} + #[no_mangle] #[polkavm_derive::polkavm_export] pub unsafe extern "C" fn call_never() { // make sure the buffer is not optimized away - api::return_value(ReturnFlags::empty(), &BUFFER); + api::return_value(ReturnFlags::empty(), buffer()); } #[no_mangle] diff --git a/substrate/frame/revive/fixtures/riscv32emac-unknown-none-polkavm.json b/substrate/frame/revive/fixtures/riscv32emac-unknown-none-polkavm.json new file mode 100644 index 000000000000..bbd54cdefbac --- /dev/null +++ b/substrate/frame/revive/fixtures/riscv32emac-unknown-none-polkavm.json @@ -0,0 +1,26 @@ +{ + "arch": "riscv32", + "cpu": "generic-rv32", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p:32:32-i64:64-n32-S32", + "eh-frame-header": false, + "emit-debug-gdb-scripts": false, + "features": "+e,+m,+a,+c,+lui-addi-fusion,+fast-unaligned-access,+xtheadcondmov", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "llvm-abiname": "ilp32e", + "llvm-target": "riscv32", + "max-atomic-width": 32, + "panic-strategy": "abort", + "relocation-model": "pie", + "target-pointer-width": "32", + "singlethread": true, + "pre-link-args": { + "ld": [ + "--emit-relocs", + "--unique", + "--relocatable" + ] + }, + "env": "polkavm" +} diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index 0695590f5379..64e66382b9ab 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -132,7 +132,7 @@ pub mod code { // This scans the whole program but we only do it once on code deployment. // It is safe to do unchecked math in u32 because the size of the program // was already checked above. - use polkavm_common::program::ISA32_V1_NoSbrk as ISA; + use polkavm::program::ISA32_V1_NoSbrk as ISA; let mut num_instructions: u32 = 0; let mut max_basic_block_size: u32 = 0; let mut basic_block_size: u32 = 0; diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 9eaa1b68ca8e..0c7461a35d69 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -21,7 +21,7 @@ codec = { features = [ ], optional = true, workspace = true } [target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = { version = "0.13.0" } +polkavm-derive = { version = "0.14.0" } [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] From c852c6c7ede191ada28a217c75b962d60f0f825d Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:23:35 +0000 Subject: [PATCH 58/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c0dfbb2e7e4..41490d07750d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -